204 lines
6.6 KiB
JavaScript
204 lines
6.6 KiB
JavaScript
import { describe, expect, it } from "vitest";
|
|
import { reopenResolvedUnknown } from "@/lib/graph/reopen-resolved-unknown.js";
|
|
|
|
function makeTestGraph() {
|
|
return {
|
|
centralStatement: "Should we enter the European market?",
|
|
currentSummary: "We have some initial understanding.",
|
|
nodes: [
|
|
{
|
|
id: "u-1",
|
|
label: "Whether there is genuine demand for our category in Europe",
|
|
description: "Need to validate European demand.",
|
|
kind: "unknown",
|
|
status: "resolved",
|
|
confidence: "high",
|
|
evidenceIds: ["e-1"],
|
|
dependsOn: [],
|
|
affects: [],
|
|
},
|
|
{
|
|
id: "u-2",
|
|
label: "Whether our product is suitable for European compliance requirements",
|
|
description: "Compliance question.",
|
|
kind: "unknown",
|
|
status: "resolved",
|
|
confidence: "medium",
|
|
evidenceIds: ["e-2"],
|
|
dependsOn: [],
|
|
affects: [],
|
|
},
|
|
{
|
|
id: "u-3",
|
|
label: "Whether the cost is justified by market size",
|
|
description: "Cost-benefit question.",
|
|
kind: "unknown",
|
|
status: "unknown",
|
|
confidence: "medium",
|
|
evidenceIds: [],
|
|
dependsOn: ["u-2"],
|
|
affects: [],
|
|
},
|
|
{
|
|
id: "obs-1",
|
|
label: "Current revenue is $2M ARR.",
|
|
description: "Financial observation.",
|
|
kind: "observation",
|
|
status: "known",
|
|
confidence: "high",
|
|
evidenceIds: [],
|
|
dependsOn: [],
|
|
affects: [],
|
|
},
|
|
{
|
|
id: "state-1",
|
|
label: "Evaluating European market entry",
|
|
kind: "state",
|
|
status: "provisional",
|
|
confidence: "medium",
|
|
evidenceIds: [],
|
|
dependsOn: [],
|
|
affects: [],
|
|
},
|
|
],
|
|
edges: [
|
|
{ fromNodeId: "u-2", toNodeId: "u-3" },
|
|
],
|
|
resolvedNodeIds: ["u-1", "u-2"],
|
|
};
|
|
}
|
|
|
|
describe("reopenResolvedUnknown — pure deterministic transformation", () => {
|
|
describe("resolved unknown target", () => {
|
|
it("sets canonical unresolved status on target node", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
const targetNode = result.nodes.find((n) => n.id === "u-1");
|
|
expect(targetNode.status).toBe("unknown");
|
|
});
|
|
|
|
it("preserves same node identity (id unchanged)", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
const targetNode = result.nodes.find((n) => n.id === "u-1");
|
|
expect(targetNode.id).toBe("u-1");
|
|
});
|
|
|
|
it("removes node ID from resolvedNodeIds", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
expect(result.resolvedNodeIds).not.toContain("u-1");
|
|
});
|
|
|
|
it("preserves other resolvedNodeIds (unrelated state)", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
expect(result.resolvedNodeIds).toContain("u-2");
|
|
});
|
|
|
|
it("preserves all unrelated node properties", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
const u2 = result.nodes.find((n) => n.id === "u-2");
|
|
expect(u2.status).toBe("resolved");
|
|
expect(u2.confidence).toBe("medium");
|
|
expect(u2.label).toBe("Whether our product is suitable for European compliance requirements");
|
|
|
|
const obs1 = result.nodes.find((n) => n.id === "obs-1");
|
|
expect(obs1.status).toBe("known");
|
|
expect(obs1.kind).toBe("observation");
|
|
});
|
|
|
|
it("preserves all edges", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
expect(result.edges).toHaveLength(1);
|
|
expect(result.edges[0].fromNodeId).toBe("u-2");
|
|
expect(result.edges[0].toNodeId).toBe("u-3");
|
|
});
|
|
|
|
it("preserves centralStatement and currentSummary", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
|
|
expect(result.centralStatement).toBe("Should we enter the European market?");
|
|
expect(result.currentSummary).toBe("We have some initial understanding.");
|
|
});
|
|
|
|
it("does not mutate input graph", () => {
|
|
const graph = makeTestGraph();
|
|
reopenResolvedUnknown(graph, "u-1");
|
|
|
|
const targetNode = graph.nodes.find((n) => n.id === "u-1");
|
|
expect(targetNode.status).toBe("resolved");
|
|
|
|
const u2 = graph.nodes.find((n) => n.id === "u-2");
|
|
expect(u2.status).toBe("resolved");
|
|
|
|
expect(graph.resolvedNodeIds).toContain("u-1");
|
|
});
|
|
});
|
|
|
|
describe("canonical resolved-node-ID target", () => {
|
|
it("reopens an unknown-status node marked resolved by graph state without mutating input", () => {
|
|
const graph = makeTestGraph();
|
|
graph.nodes[0] = { ...graph.nodes[0], status: "unknown" };
|
|
|
|
const result = reopenResolvedUnknown(graph, "u-1");
|
|
const targetNode = result.nodes.find((n) => n.id === "u-1");
|
|
|
|
expect(targetNode.kind).toBe("unknown");
|
|
expect(targetNode.status).toBe("unknown");
|
|
expect(result.resolvedNodeIds).not.toContain("u-1");
|
|
expect(result.resolvedNodeIds).toContain("u-2");
|
|
expect(graph.nodes.find((n) => n.id === "u-1").status).toBe("unknown");
|
|
expect(graph.resolvedNodeIds).toContain("u-1");
|
|
});
|
|
});
|
|
|
|
describe("invalid/no-op cases", () => {
|
|
it("returns original graph when node does not exist", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "nonexistent");
|
|
|
|
expect(result).toBe(graph);
|
|
expect(result.nodes.find((n) => n.id === "u-1").status).toBe("resolved");
|
|
expect(result.resolvedNodeIds).toContain("u-1");
|
|
});
|
|
|
|
it("returns original graph when node is already unresolved", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "u-3");
|
|
|
|
expect(result).toBe(graph);
|
|
expect(result.resolvedNodeIds).toContain("u-1");
|
|
expect(result.resolvedNodeIds).toContain("u-2");
|
|
});
|
|
|
|
it("returns original graph when node is not an unknown kind", () => {
|
|
const graph = makeTestGraph();
|
|
const result = reopenResolvedUnknown(graph, "obs-1");
|
|
|
|
expect(result).toBe(graph);
|
|
});
|
|
|
|
it("returns original graph when input graph is null", () => {
|
|
expect(reopenResolvedUnknown(null, "u-1")).toBeNull();
|
|
});
|
|
|
|
it("returns original graph when input graph has no nodes", () => {
|
|
const graph = makeTestGraph();
|
|
const noNodes = { ...graph, nodes: [] };
|
|
const result = reopenResolvedUnknown(noNodes, "u-1");
|
|
|
|
expect(result).toBe(noNodes);
|
|
});
|
|
});
|
|
});
|