import assert from "node:assert/strict"; import test from "node:test"; import type { DiagramIRV2 } from "../../model/diagram-ir.js"; import { projectLinkedViews, queryDiagram, runPolicies, simulateFailure } from "./analysis.js"; const IR: DiagramIRV2 = { version: 2, provenance: { source: "catalog" }, pages: [{ id: "system", title: "System", nodes: [ { id: "internet", label: "Internet", kind: "external", properties: { importance: 10, trust_boundary: "public" }, provenance: { source: "inventory" } }, { id: "api", label: "API", kind: "service", properties: { importance: 8, environment: "production", trust_boundary: "private" } }, { id: "worker", label: "Worker", kind: "service", properties: { owner: "ops", runtime: "node" } }, { id: "db", label: "DB", kind: "database", properties: { owner: "data", trust_boundary: "data" } }, { id: "vendor", label: "Vendor", kind: "external" }, { id: "orphan", label: "Orphan", kind: "service" }, ], edges: [ { id: "bad", source: "internet", target: "db" }, { id: "request", source: "internet", target: "api", properties: { protocol: "HTTPS" } }, { id: "dispatch", source: "api", target: "worker", kind: "async", properties: { isolates_failure: true } }, { id: "write", source: "api", target: "db", kind: "write", properties: { protocol: "TLS" } }, { id: "external-call", source: "api", target: "vendor" }, { id: "cycle", source: "worker", target: "api" }, ] }] }; test("linked view projection is deterministic, preserves model identity, and explains fallbacks", () => { const views = projectLinkedViews(IR, ["executive", "system", "deployment", "dataflow", "security"]); assert.deepEqual(views.map((view) => view.id), ["executive", "system", "deployment", "dataflow", "security"]); assert.deepEqual(views[0].nodes.slice(0, 2).map((node) => node.id), ["internet", "api"]); assert.deepEqual(views[0].nodes[0].provenance, { source: "inventory" }); assert.equal(views[1].fallback, false); assert.ok(views[2].nodes.some((node) => node.id === "worker")); assert.ok(views[3].nodes.some((node) => node.id === "db")); assert.equal(projectLinkedViews({ ...IR, pages: [{ ...IR.pages[0], nodes: IR.pages[0].nodes.map((node) => ({ id: node.id, label: node.label })) }] }, ["security"])[0].fallbackReason?.length! > 0, true); assert.deepEqual(projectLinkedViews(IR), projectLinkedViews(IR)); }); test("semantic query filters kind/properties and chooses deterministic shortest path", () => { assert.deepEqual(queryDiagram(IR, { kind: "service", properties: { owner: "ops" } }).nodes.map((node) => node.id), ["worker"]); const path = queryDiagram(IR, { from: "internet", to: "worker" }); assert.deepEqual(path.path, ["internet", "api", "worker"]); assert.deepEqual(path.edges.map((edge) => edge.id), ["request", "dispatch"]); }); test("architecture policies distinguish errors and warnings for all lifecycle rules", () => { const report = runPolicies(IR); assert.ok(report.findings.some((finding) => finding.rule === "no-direct-internet-to-database" && finding.severity === "error")); for (const rule of ["no-cycles", "no-orphans", "every-service-has-owner", "production-has-observability", "external-dependencies-have-timeouts", "trust-boundaries-use-protocol"]) { assert.ok(report.findings.some((finding) => finding.rule === rule && finding.severity === "warning"), rule); } assert.equal(report.errors, 1); assert.ok(report.warnings >= 6); }); test("what-if outgoing reachability stops at failure-isolating edges", () => { const result = simulateFailure(IR, "internet"); assert.deepEqual(result.impacted, ["api", "db", "vendor"]); assert.deepEqual(result.paths.api, ["internet", "api"]); assert.equal(result.paths.worker, undefined); assert.throws(() => simulateFailure(IR, "missing"), /unknown node/i); }); test("semantic operations merge an element projected onto several pages", () => { const shared: DiagramIRV2 = { version: 2, pages: [ { id: "context", title: "Context", nodes: [ { id: "shop", label: "Shop", kind: "c4-system", properties: { c4Type: "system" }, provenance: { line: 2 } }, { id: "buyer", label: "Buyer", kind: "actor" }, ], edges: [{ id: "uses", source: "buyer", target: "shop" }] }, { id: "containers", title: "Containers", nodes: [ { id: "shop", label: "Shop", kind: "container", properties: { c4Type: "system" }, provenance: { line: 2 } }, { id: "buyer", label: "Buyer", kind: "actor" }, { id: "api", label: "API", kind: "service", parentId: "shop" }, ], edges: [{ id: "uses", source: "buyer", target: "shop" }, { id: "calls", source: "buyer", target: "api" }] }, ], }; const system = projectLinkedViews(shared, ["system"])[0]; assert.deepEqual(system.nodes.map((node) => node.id), ["shop", "buyer", "api"]); assert.equal(system.nodes.find((node) => node.id === "shop")?.kind, "c4-system", "specific kind wins over the presentational container promotion"); assert.equal(system.nodes.find((node) => node.id === "api")?.parentId, undefined, "parent that is not a container in the merged model is dropped"); assert.deepEqual(system.edges.map((edge) => edge.id), ["uses", "calls"]); assert.deepEqual(queryDiagram(shared, { from: "buyer", to: "api" }).path, ["buyer", "api"]); assert.equal(runPolicies(shared, ["no-orphans"]).findings.length, 0); assert.deepEqual(simulateFailure(shared, "buyer").impacted, ["api", "shop"]); assert.throws(() => queryDiagram({ ...shared, pages: [shared.pages[0], { ...shared.pages[1], edges: [{ id: "uses", source: "api", target: "shop" }] }] }, {}), /ambiguous.*edge.*uses/i); }); test("views keep container parents that are part of the selection and detach the rest", () => { const nested: DiagramIRV2 = { version: 2, pages: [{ id: "p", title: "P", nodes: [ { id: "outer", label: "Outer", kind: "container", geometry: { x: 100, y: 100, width: 400, height: 300 }, properties: { importance: 5 } }, { id: "inner", label: "Inner", kind: "container", parentId: "outer", geometry: { x: 20, y: 40, width: 200, height: 150 } }, { id: "leaf", label: "Leaf", kind: "service", parentId: "inner", geometry: { x: 10, y: 50, width: 120, height: 60 }, properties: { importance: 9 } }, ], edges: [] }] }; const [executive] = projectLinkedViews({ ...nested, pages: [{ ...nested.pages[0], nodes: nested.pages[0].nodes.slice(0, 2).concat(Array.from({ length: 11 }, (_, index) => ({ id: `n${index}`, label: `N${index}`, properties: { importance: 8 } })), nested.pages[0].nodes.slice(2)) }] }, ["executive"]); const leaf = executive.nodes.find((node) => node.id === "leaf")!; assert.equal(executive.nodes.some((node) => node.id === "inner"), false); assert.equal(leaf.parentId, undefined); assert.deepEqual(leaf.geometry, { x: 130, y: 190, width: 120, height: 60 }); const [system] = projectLinkedViews(nested, ["system"]); assert.equal(system.nodes.find((node) => node.id === "leaf")?.parentId, "inner"); assert.deepEqual(system.nodes.find((node) => node.id === "leaf")?.geometry, { x: 10, y: 50, width: 120, height: 60 }); }); test("semantic operations fail closed on ambiguous page-local IDs", () => { const ambiguous: DiagramIRV2 = { version: 2, pages: [ { id: "one", title: "One", nodes: [{ id: "shared", label: "First" }], edges: [] }, { id: "two", title: "Two", nodes: [{ id: "shared", label: "Second" }], edges: [] }, ], }; assert.throws(() => queryDiagram(ambiguous, { kind: "service" }), /ambiguous.*shared.*one.*two/i); assert.throws(() => projectLinkedViews(ambiguous), /ambiguous.*shared.*one.*two/i); assert.throws(() => runPolicies(ambiguous), /ambiguous.*shared.*one.*two/i); assert.throws(() => simulateFailure(ambiguous, "shared"), /ambiguous.*shared.*one.*two/i); });