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 }]);
});