test.govlab/context/core/validators/reason.validator.test.ts

test.govlab/context/core/validators/reason.validator.test.ts is a file in Codebase Testing. 282 lines of code and 0 definitions.

import { CONCEPT_COLLECTION_BY_AXIS } from "@govlab/context/configuration/constants/reason.constants.ts";
import type { ReasonData } from "@govlab/context/types/reason.types.ts";
import assert from "node:assert/strict";
import { bundledReason } from "./ontology.fixture.ts";
import { test } from "vitest";
import { validateReason } from "@govlab/context/core/validators/reason.validator.ts";

const SURFACE_KIND = "test-surface";

const goodData = (): ReasonData => ({
    axes: [{ id: "a1", layer: "l1", mandatory: "yes", primaryMathType: "m1", question: "q", selectable: true }],
    derivationLoop: {
        id: "dl",
        stages: [{ axis: "a1", id: "s1" }],
        transitions: [{ from: "s1", kind: "advance", to: "s1" }],
    },
    dimensions: [],
    edges: [{ from: "n1", to: "algorithms:x" }],
    failureShapes: [],
    invariants: [],
    layers: [{ id: "l1", label: "L1", question: "q" }],
    lenses: [],
    maps: { foundations: { layers: {}, sequence: [] }, invariants: {}, patternOperations: [] },
    mathDomains: [],
    mathTypes: [{ domains: [], id: "m1", predicateFamily: "pf", question: "q", yieldsShape: "shape" }],
    models: [],
    modes: [],
    nodes: [{ axis: "a1", id: "n1", mathType: "m1", name: "N1" }],
    patternTypes: [],
    representations: [],
    substrate: { cycle: [], nodes: [], recursion: { from: "", to: "" } },
    techniques: [],
    testSurfaces: [],
    universalAxes: [],
});

const surfaceData = (): ReasonData => {
    const data = goodData();
    data.dimensions = [{ id: "meaning", mathDomains: [], mathNature: "n", question: "q" }];
    data.lenses = [
        { id: "semantic", mathDomains: [], mathFields: [], nature: "n", question: "q", universalAxis: "u1" },
    ];
    data.universalAxes = [{ id: "u1", question: "q", subsumes: ["meaning"] }];
    data.modes = [{ id: "comparison", practice: "p" }];
    data.nodes = [
        { axis: "a1", id: "n1", mathType: "m1", name: "N1" },
        { axis: "a1", id: "ver-ground-truth", mathType: "m1", name: "Ground Truth" },
        { axis: "a1", id: "ver-evidence", mathType: "m1", name: "Evidence" },
    ];
    data.techniques = [{ fails: "passes on stale doubles", id: "unit-testing", mode: "comparison", principle: "p" }];
    data.invariants = [{ id: "correct-outputs", name: "Correct Outputs", statement: "s" }];
    data.testSurfaces = [
        {
            dimension: "meaning",
            evidence: { grounds: ["ver-evidence"], required: true, source: "test-result" },
            failureModes: ["a stale expected value"],
            fit: "the meaning of the result is compared with the meaning expected",
            id: "s-sem",
            invariant: "correct-outputs",
            lens: "semantic",
            predicate: { expression: "x", grounds: ["ver-ground-truth"], type: "equivalence" },
            techniques: ["unit-testing"],
            verdictDomain: ["pass", "fail", "unknown"],
        },
    ];
    return data;
};

const firstSurface = (data: ReasonData): ReasonData["testSurfaces"][number] => {
    const [surface] = data.testSurfaces;
    assert.ok(surface, "expected a test surface");
    return surface;
};

const dangles = (data: ReasonData, kind: string, field: string, value: string): boolean =>
    validateReason(data).danglingFields.some(
        (entry) => entry.kind === kind && entry.field === field && entry.value === value,
    );

const emptied = (data: ReasonData): string[] =>
    validateReason(data).emptyFields.map((entry) => `${entry.kind}:${entry.id}:${entry.field}`);

const danglingOf = (data: ReasonData, kind: string): { field: string; id: string; value: string }[] =>
    validateReason(data)
        .danglingFields.filter((entry) => entry.kind === kind)
        .map(({ field, id, value }) => ({ field, id, value }));

test("validateReason is clean on well-formed data and on the surface baseline", () => {
    assert.equal(validateReason(goodData()).total, 0);
    assert.equal(validateReason(surfaceData()).total, 0);
});

test("validateReason reports a duplicate id", () => {
    const data = goodData();
    data.nodes = [
        { axis: "a1", id: "n1", mathType: "m1", name: "N1" },
        { axis: "a1", id: "n1", mathType: "m1", name: "N1" },
    ];
    const issues = validateReason(data);
    assert.ok(issues.duplicateIds.includes("node:n1"), JSON.stringify(issues.duplicateIds));
    assert.ok(issues.total > 0);
});

test("validateReason reports a node, axis, substrate node or lens field that names no record", () => {
    const axis = goodData();
    axis.nodes = [{ axis: "aX", id: "n1", mathType: "m1", name: "N1" }];
    assert.ok(dangles(axis, "node", "axis", "aX"));
    const mathType = goodData();
    mathType.nodes = [{ axis: "a1", id: "n1", mathType: "mZ", name: "N1" }];
    assert.ok(dangles(mathType, "node", "mathType", "mZ"));
    const primary = goodData();
    primary.axes = [
        { id: "a1", layer: "l1", mandatory: "yes", primaryMathType: "mZ", question: "q", selectable: true },
    ];
    assert.ok(dangles(primary, "axis", "primaryMathType", "mZ"));
    const substrate = goodData();
    substrate.substrate = {
        cycle: [],
        nodes: [{ id: "s1", layer: "l1", mathType: "mZ", name: "s1" }],
        recursion: { from: "", to: "" },
    };
    assert.ok(dangles(substrate, "substrate-node", "mathType", "mZ"));
    const lens = goodData();
    lens.lenses = [{ id: "lens1", mathDomains: [], mathFields: [], nature: "n", question: "q", universalAxis: "uZ" }];
    assert.ok(dangles(lens, "lens", "universalAxis", "uZ"));
});

test("validateReason reports a loop transition to a non-stage, and an edge source that is neither node nor stage", () => {
    const transition = goodData();
    transition.derivationLoop.transitions = [{ from: "s1", kind: "advance", to: "sZ" }];
    assert.ok(
        validateReason(transition).danglingTransitions.some(
            (entry) => entry.to === "sZ" && entry.reason.includes("to is not a stage"),
        ),
    );
    const edge = goodData();
    edge.edges = [{ from: "ghost", to: "algorithms:x" }];
    assert.ok(validateReason(edge).danglingEdgeSources.includes("ghost"));
});

test("validateReason reports a surface field that names no record", () => {
    const cases: [string, (surface: ReasonData["testSurfaces"][number]) => void, string][] = [
        [
            "dimension",
            (surface) => {
                surface.dimension = "ghost";
            },
            "ghost",
        ],
        [
            "lens",
            (surface) => {
                surface.lens = "ghost";
            },
            "ghost",
        ],
        [
            "techniques",
            (surface) => {
                surface.techniques = ["ghost"];
            },
            "ghost",
        ],
        [
            "invariant",
            (surface) => {
                surface.invariant = "ghost";
            },
            "ghost",
        ],
        [
            "predicate.grounds",
            (surface) => {
                surface.predicate.grounds = ["ghost"];
            },
            "ghost",
        ],
        [
            "evidence.grounds",
            (surface) => {
                surface.evidence.grounds = ["ghost"];
            },
            "ghost",
        ],
    ];
    for (const [field, plant, value] of cases) {
        const data = surfaceData();
        plant(firstSurface(data));
        assert.ok(dangles(data, SURFACE_KIND, field, value), field);
    }
});

test("validateReason reports a technique whose mode names no mode", () => {
    const data = surfaceData();
    const [technique] = data.techniques;
    assert.ok(technique, "expected a technique");
    technique.mode = "ghost-mode";
    assert.ok(dangles(data, "technique", "mode", "ghost-mode"));
});

test("validateReason reports a surface with no techniques, an empty verdict domain, or a blank predicate", () => {
    const techniques = surfaceData();
    firstSurface(techniques).techniques = [];
    assert.deepEqual(emptied(techniques), ["test-surface:s-sem:techniques"]);
    const verdicts = surfaceData();
    firstSurface(verdicts).verdictDomain = [];
    assert.deepEqual(emptied(verdicts), ["test-surface:s-sem:verdictDomain"]);
    const predicate = surfaceData();
    firstSurface(predicate).predicate.expression = "   ";
    assert.deepEqual(emptied(predicate), ["test-surface:s-sem:predicate.expression"]);
});

test("validateReason reports two surfaces colliding on one dimension and lens cell", () => {
    const data = surfaceData();
    data.testSurfaces.push({ ...firstSurface(data), id: "s-sem-2" });
    const issues = validateReason(data);
    assert.ok(
        issues.collidingSurfaceCells.some(
            (entry) => entry.surfaces.includes("s-sem") && entry.surfaces.includes("s-sem-2"),
        ),
    );
});

test("an answer shape outside what the node's math type yields is reported", () => {
    const data = bundledReason();
    assert.deepEqual(validateReason(data).answerShapeMismatches, []);
    const planted = {
        ...data,
        nodes: data.nodes.map((node) => (node.id === "ter-block" ? { ...node, answerShape: "ranking" } : node)),
    };
    assert.deepEqual(validateReason(planted).answerShapeMismatches, [
        { allowed: "boolean", answerShape: "ranking", node: "ter-block" },
    ]);
});

test("a model step that is not a record of the model's declared step kind is reported", () => {
    const data = bundledReason();
    assert.deepEqual(validateReason(data).unresolvedModelSteps, []);
    const planted = {
        ...data,
        models: data.models.map((model) =>
            model.id === "epistemology" ? { ...model, sequence: [...model.sequence, "guessing"] } : model,
        ),
    };
    assert.deepEqual(validateReason(planted).unresolvedModelSteps, [
        { model: "epistemology", step: "guessing", stepKind: "mode" },
    ]);
});

test("a failure shape that names no invariant, or no quality concept, is reported", () => {
    const data = bundledReason();
    assert.deepEqual(validateReason(data).danglingFields, []);
    assert.deepEqual(validateReason(data).emptyFields, []);
    const ghost = { breaks: "epi-ghost", canon: [], fix: "f", id: "ghost-shape", instances: [], name: "G", shape: "s" };
    const planted = { ...data, failureShapes: [...data.failureShapes, ghost] };
    assert.deepEqual(danglingOf(planted, "failure-shape"), [
        { field: "breaks", id: "ghost-shape", value: "epi-ghost" },
    ]);
    assert.deepEqual(validateReason(planted).emptyFields, [
        { field: "canon", id: "ghost-shape", kind: "failure-shape" },
    ]);
});

test("a technique with no fails and a surface with no fit are reported empty", () => {
    const data = bundledReason();
    const [technique] = data.techniques;
    const [surface] = data.testSurfaces;
    assert.ok(technique && surface);
    const failless = { ...data, techniques: [{ ...technique, fails: "" }, ...data.techniques.slice(1)] };
    assert.deepEqual(validateReason(failless).emptyFields, [{ field: "fails", id: technique.id, kind: "technique" }]);
    const fitless = { ...data, testSurfaces: [{ ...surface, fit: " " }, ...data.testSurfaces.slice(1)] };
    assert.deepEqual(validateReason(fitless).emptyFields, [{ field: "fit", id: surface.id, kind: "test-surface" }]);
});

test("a reference to a missing layer, math domain or representation is reported", () => {
    const data = bundledReason();
    assert.deepEqual(validateReason(data).danglingConcepts, []);
    const [axis] = data.axes;
    const [mathType] = data.mathTypes;
    const represented = data.nodes.find(
        (node) => CONCEPT_COLLECTION_BY_AXIS.get(node.axis) === "representation" && typeof node.concept === "string",
    );
    if (axis === undefined || mathType === undefined || represented === undefined) {
        throw new Error("the bundled reasoning data holds no axis, math type or representation node to plant on");
    }
    const planted = validateReason({
        ...data,
        axes: data.axes.map((entry) => (entry.id === axis.id ? { ...entry, layer: "ghost-layer" } : entry)),
        mathTypes: data.mathTypes.map((entry) =>
            entry.id === mathType.id ? { ...entry, domains: [...entry.domains, "ghost-domain"] } : entry,
        ),
        nodes: data.nodes.map((node) => (node.id === represented.id ? { ...node, concept: "ghost-form" } : node)),
    });
    assert.deepEqual(
        planted.danglingFields.map(({ field, id, kind, value }) => ({ field, id, kind, value })),
        [
            { field: "layer", id: axis.id, kind: "axis", value: "ghost-layer" },
            { field: "domains", id: mathType.id, kind: "math-type", value: "ghost-domain" },
        ],
    );
    assert.deepEqual(planted.danglingConcepts, [{ concept: "ghost-form", node: represented.id }]);
});