Files
confidence-engine/tests/experimental/progressed-investigation-fixture.test.js
T

190 lines
7.6 KiB
JavaScript

/**
* Progressed investigation fixture apparatus verification.
*
* Proves that the canonical progressed-investigation fixture:
* 1. loads successfully as valid JSON
* 2. contains >= 1 admitted known/supported evidence item
* 3. contains >= 1 unresolved Open Question (kind=unknown, status=unknown)
* 4. the unresolved Open Question is not admitted evidence
* 5. at least one eligible Finding exists (null disposition)
* 6. unresolved Open Question is not represented as an eligible Finding
* 7. resolvedNodeIds does NOT include any unknown node id
*/
import { describe, it, expect } from "vitest";
import fixture from "../fixtures/progressed-investigation-manufacturing.json";
describe("Progressed investigation fixture apparatus", () => {
/* ── 1. Fixture loads successfully ─────────────────── */
it("loads as valid JSON with required top-level keys", () => {
expect(fixture).toHaveProperty("success", true);
expect(fixture).toHaveProperty("stage", "update_applied");
expect(fixture).toHaveProperty("situationGraph");
expect(fixture.situationGraph).toHaveProperty("nodes");
expect(fixture.situationGraph).toHaveProperty("edges");
expect(fixture.situationGraph).toHaveProperty("activeUnknownNodeId");
expect(fixture.situationGraph).toHaveProperty("resolvedNodeIds");
expect(fixture.situationGraph).toHaveProperty("centralStatement");
});
/* ── 2. Contains admitted evidence (>= 1 supported/known node) ─── */
it("contains at least one admitted known or supported evidence node", () => {
const admitted = fixture.situationGraph.nodes.filter(
(n) => n.status === "supported" || n.status === "known"
);
expect(admitted.length).toBeGreaterThan(0);
});
it("admitted nodes are not unknown kind", () => {
const admitted = fixture.situationGraph.nodes.filter(
(n) => ["supported", "known"].includes(n.status)
);
for (const node of admitted) {
expect(node.kind).not.toBe("unknown");
}
});
/* ── 3. Contains unresolved Open Question (>= 1) ─────── */
it("contains at least one unresolved Open Question node", () => {
const unresolved = fixture.situationGraph.nodes.filter(
(n) => n.kind === "unknown" && n.status === "unknown"
);
expect(unresolved.length).toBeGreaterThan(0);
});
/* ── 4. Unresolved Open Question is not admitted evidence ─── */
it("no node is simultaneously unresolved unknown AND admitted evidence", () => {
const allNodes = fixture.situationGraph.nodes;
for (const node of allNodes) {
if (node.kind === "unknown" && node.status === "unknown") {
// An unresolved unknown must not have admitted status
expect(["supported", "known"]).not.toContain(node.status);
}
}
});
it("unresolved and admitted node ID sets are disjoint", () => {
const unresolvedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
const admittedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => ["supported", "known"].includes(n.status))
.map((n) => n.id)
);
for (const id of unresolvedIds) {
expect(admittedIds.has(id)).toBe(false);
}
});
it("unresolved unknown nodes are distinct from admitted evidence", () => {
const unresolvedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
const admittedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => ["supported", "known"].includes(n.status))
.map((n) => n.id)
);
for (const id of unresolvedIds) {
expect(admittedIds.has(id)).toBe(false);
}
});
/* ── 5. Unresolved question excluded from resolvedNodeIds ─── */
it("resolvedNodeIds does not contain any unresolved unknown node id", () => {
const unresolvedIds = fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id);
for (const nodeId of unresolvedIds) {
expect(fixture.situationGraph.resolvedNodeIds).not.toContain(nodeId);
}
});
/* ── 6. Eligible Finding exists ─────────────────────── */
it("findings array exists and has eligible items", () => {
expect(fixture.findings).toBeDefined();
expect(Array.isArray(fixture.findings)).toBe(true);
const eligible = fixture.findings.filter(
(f) => f.status === "provisional" && f.userDisposition === null
);
expect(eligible.length).toBeGreaterThan(0);
});
/* ── 7. Unresolved Open Question is not represented as a Finding ─── */
it("unresolved unknown nodes are not mapped to findings propositions", () => {
const unresolvedNodeIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
// originTargetNodeId links findings to unknown nodes
// A finding whose originatingTargetNodeId is an unresolved unknown is fine
// but the proposition should not BE the same as the node label
const eligibleFindings = fixture.findings.filter(
(f) => f.status === "provisional" && f.userDisposition === null
);
const unresolvedNodes = fixture.situationGraph.nodes.filter(
(n) => n.kind === "unknown" && n.status === "unknown"
);
for (const finding of eligibleFindings) {
// The proposition is an admitted evidence item, not the unknown itself
expect(finding.proposition).not.toContain("Whether");
// Verify the originating node is NOT resolved — it remains open
const targetNode = unresolvedNodes.find(
(n) => n.id === finding.originatingTargetNodeId
);
if (targetNode) {
expect(targetNode.status).toBe("unknown");
expect(targetNode.kind).toBe("unknown");
}
}
});
/* ── 8. Canonical shape integrity ──────────────────── */
it("activeUnknownNodeId references a valid unresolved unknown node", () => {
const unresolvedIds = fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id);
expect(unresolvedIds).toContain(fixture.situationGraph.activeUnknownNodeId);
});
it("all nodes pass the canonical situationNode schema invariants", () => {
for (const node of fixture.situationGraph.nodes) {
expect(node).toHaveProperty("id");
expect(node).toHaveProperty("label");
expect(node).toHaveProperty("description");
expect(["observation","reported_claim","metric","state","transition","relationship","assumption","unknown","conclusion","option"]).toContain(node.kind);
expect(["known","unknown","provisional","supported","weakened","contradicted","resolved"]).toContain(node.status);
expect(["low","medium","high"]).toContain(node.confidence);
expect(Array.isArray(node.evidenceIds)).toBe(true);
expect(Array.isArray(node.dependsOn)).toBe(true);
expect(Array.isArray(node.affects)).toBe(true);
}
});
it("all edges reference valid nodes", () => {
const nodeIds = new Set(fixture.situationGraph.nodes.map((n) => n.id));
for (const edge of fixture.situationGraph.edges) {
expect(edge).toHaveProperty("id");
expect(nodeIds.has(edge.fromNodeId)).toBe(true);
expect(nodeIds.has(edge.toNodeId)).toBe(true);
expect(edge).toHaveProperty("relationship");
expect(edge).toHaveProperty("confidence");
}
});
});