mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fix(javascript): preserve unresolved version relationships
Keep positive topology differences separate from unpaired neighbor identity. Preserve exact content pairing and source observations, report unresolved relationships without inventing incomplete inventory, and omit comparison edges when no substantive difference was observed. Validated with 45 focused domain/CLI/MCP tests, nine expanded domain controls, check:fast, and docs:check. Clean baseline fails seven controls and passes two positive controls. Controlled static fixtures only; no target execution or large-bundle profiling. Refs #4681
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
import { expect, it } from "vitest";
|
||||
import {
|
||||
artifactEvidence,
|
||||
completeCoverage,
|
||||
fixtureNode,
|
||||
} from "./javascriptApplicationGraph.fixture.js";
|
||||
import {
|
||||
createJavaScriptApplicationGraph,
|
||||
createJavaScriptApplicationEdge,
|
||||
type JavaScriptApplicationGraph,
|
||||
} from "./javascriptApplicationGraph.js";
|
||||
import { compareJavaScriptApplicationVersions } from "./javascriptApplicationVersionComparison.js";
|
||||
|
||||
const graph = (
|
||||
sha: string,
|
||||
peers: "ambiguous" | "unique" | "unmatched",
|
||||
options: {
|
||||
relation?: "imports" | "invokes";
|
||||
target?: "peer-one" | "peer-two";
|
||||
direction?: "in" | "out";
|
||||
content?: string;
|
||||
partial?: true;
|
||||
} = {},
|
||||
) => {
|
||||
const evidence = artifactEvidence(sha, "main.js");
|
||||
const nodes = ["main", "peer-one", "peer-two"].map((key) =>
|
||||
fixtureNode(
|
||||
"javascript-module",
|
||||
{
|
||||
strategy: "artifact-local-key",
|
||||
stability: "artifact-version",
|
||||
artifact_sha256: sha,
|
||||
namespace: "module",
|
||||
key,
|
||||
},
|
||||
key === "main" ? "main" : "peer",
|
||||
{
|
||||
source_sha256:
|
||||
key === "main"
|
||||
? (options.content ?? "d".repeat(64))
|
||||
: peers === "ambiguous"
|
||||
? "c".repeat(64)
|
||||
: peers === "unique"
|
||||
? (key === "peer-one" ? "e" : "f").repeat(64)
|
||||
: (key === "peer-one" ? "1" : "2").repeat(64),
|
||||
...(key === "main"
|
||||
? {
|
||||
structural_fingerprint_sha256: "9".repeat(64),
|
||||
structural_fingerprint_algorithm: "fixture",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
evidence,
|
||||
),
|
||||
);
|
||||
const main = nodes[0],
|
||||
target = nodes[options.target === "peer-two" ? 2 : 1];
|
||||
if (main === undefined || target === undefined)
|
||||
throw new Error("Missing fixture nodes");
|
||||
return {
|
||||
main,
|
||||
graph: createJavaScriptApplicationGraph({
|
||||
schema: "JavaScriptApplicationGraph",
|
||||
root_node_ids: [main.node_id],
|
||||
nodes,
|
||||
edges: [
|
||||
createJavaScriptApplicationEdge({
|
||||
source_node_id:
|
||||
options.direction === "in" ? target.node_id : main.node_id,
|
||||
target_node_id:
|
||||
options.direction === "in" ? main.node_id : target.node_id,
|
||||
relation: options.relation ?? "imports",
|
||||
properties: {},
|
||||
evidence,
|
||||
}),
|
||||
],
|
||||
coverage: options.partial
|
||||
? { ...completeCoverage, status: "partial", omitted_count: null }
|
||||
: completeCoverage,
|
||||
limitations: options.partial ? ["Controlled incomplete inventory."] : [],
|
||||
}),
|
||||
};
|
||||
};
|
||||
const compare = (
|
||||
left: ReturnType<typeof graph>,
|
||||
right: ReturnType<typeof graph>,
|
||||
) => {
|
||||
const result = compareJavaScriptApplicationVersions({
|
||||
left: {
|
||||
evidenceId: `ev_${"1".repeat(64)}`,
|
||||
rootArtifactSha256: "a".repeat(64),
|
||||
graph: left.graph,
|
||||
},
|
||||
right: {
|
||||
evidenceId: `ev_${"2".repeat(64)}`,
|
||||
rootArtifactSha256: "b".repeat(64),
|
||||
graph: right.graph,
|
||||
},
|
||||
leftNativeEvidence: [],
|
||||
rightNativeEvidence: [],
|
||||
});
|
||||
const item = result.items.find(
|
||||
(item) => item.left_node_id === left.main.node_id,
|
||||
);
|
||||
if (item === undefined) throw new Error("Missing main comparison item");
|
||||
return { result, item };
|
||||
};
|
||||
|
||||
it.each(["ambiguous", "unmatched"] as const)(
|
||||
"keeps %s neighbor alignment unknown despite complete inventories",
|
||||
(peers) => {
|
||||
const { result, item } = compare(
|
||||
graph("a".repeat(64), "ambiguous"),
|
||||
graph("b".repeat(64), peers),
|
||||
);
|
||||
expect(item).toMatchObject({
|
||||
status: "unknown",
|
||||
dimensions: [],
|
||||
match: {
|
||||
status: "matched",
|
||||
basis: "exact-module-source-digest",
|
||||
confidence: "exact",
|
||||
},
|
||||
});
|
||||
expect(item.limitations.join(" ")).toMatch(
|
||||
/relationship.*identity.*unresolved/iu,
|
||||
);
|
||||
expect(item.limitations.join(" ")).not.toContain(
|
||||
"Incomplete graph coverage",
|
||||
);
|
||||
expect(result.coverage.status).toBe("complete-within-inputs");
|
||||
expect(
|
||||
result.graph.edges.filter((edge) => edge.relation === "changed_from"),
|
||||
).toEqual([]);
|
||||
expect(result.graph.nodes).toHaveLength(6);
|
||||
if (peers === "ambiguous") {
|
||||
expect(result.summary).toEqual({
|
||||
unchanged: 0,
|
||||
added: 0,
|
||||
removed: 0,
|
||||
changed: 0,
|
||||
unknown: 5,
|
||||
});
|
||||
expect(
|
||||
result.items.filter((i) => i.match.status === "ambiguous"),
|
||||
).toHaveLength(4);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps equal relationships to uniquely paired peers unchanged", () => {
|
||||
const { item } = compare(
|
||||
graph("a".repeat(64), "unique"),
|
||||
graph("b".repeat(64), "unique"),
|
||||
);
|
||||
expect(item).toMatchObject({ status: "unchanged", dimensions: [] });
|
||||
expect(item.limitations).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([{ relation: "invokes" as const }, { direction: "in" as const }])(
|
||||
"retains positive relation/direction differences beside unknown peer identity %j",
|
||||
(options) => {
|
||||
const { item } = compare(
|
||||
graph("a".repeat(64), "ambiguous"),
|
||||
graph("b".repeat(64), "ambiguous", options),
|
||||
);
|
||||
expect(item).toMatchObject({
|
||||
status: "changed",
|
||||
dimensions: ["relationships"],
|
||||
});
|
||||
expect(item.limitations.join(" ")).toMatch(
|
||||
/relationship.*identity.*unresolved/iu,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it("reports a resolved target change", () => {
|
||||
const { item } = compare(
|
||||
graph("a".repeat(64), "unique"),
|
||||
graph("b".repeat(64), "unique", { target: "peer-two" }),
|
||||
);
|
||||
expect(item).toMatchObject({
|
||||
status: "changed",
|
||||
dimensions: ["relationships"],
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves definite content change alongside unresolved relationships", () => {
|
||||
const { item } = compare(
|
||||
graph("a".repeat(64), "ambiguous"),
|
||||
graph("b".repeat(64), "ambiguous", { content: "8".repeat(64) }),
|
||||
);
|
||||
expect(item).toMatchObject({
|
||||
status: "changed",
|
||||
dimensions: ["content", "properties"],
|
||||
match: { basis: "structural-fingerprint" },
|
||||
});
|
||||
expect(item.limitations.join(" ")).toMatch(
|
||||
/relationship.*identity.*unresolved/iu,
|
||||
);
|
||||
});
|
||||
|
||||
it.each(["imports", "invokes"] as const)(
|
||||
"keeps partial inventory and neighbor identity uncertainty distinct (%s)",
|
||||
(relation) => {
|
||||
const { result, item } = compare(
|
||||
graph("a".repeat(64), "ambiguous"),
|
||||
graph("b".repeat(64), "ambiguous", { partial: true, relation }),
|
||||
);
|
||||
expect(item).toMatchObject({
|
||||
status: "unknown",
|
||||
dimensions:
|
||||
relation === "imports" ? ["coverage"] : ["relationships", "coverage"],
|
||||
});
|
||||
const changes = result.graph.edges.filter(
|
||||
(edge) => edge.relation === "changed_from",
|
||||
);
|
||||
expect(changes).toHaveLength(relation === "imports" ? 0 : 1);
|
||||
if (relation === "invokes")
|
||||
expect(changes[0]).toMatchObject({
|
||||
properties: { status: "unknown" },
|
||||
evidence: { coverage: { status: "partial" } },
|
||||
});
|
||||
expect(item.limitations.join(" ")).toContain("Incomplete graph coverage");
|
||||
expect(item.limitations.join(" ")).toMatch(
|
||||
/relationship.*identity.*unresolved/iu,
|
||||
);
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user