mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
8d198d6ec7
Classify wholly unknown or unavailable node observations at the version-item owner and retain their limitations across matched and unmatched comparisons. Preserve positive mixed observations and genuine additions/removals. Validated source regressions and public CLI/stdout MCP workflows in the 103-test focused gate; typecheck and lint pass. Fixes #5534.
208 lines
6.5 KiB
TypeScript
208 lines
6.5 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import {
|
|
createJavaScriptApplicationGraph,
|
|
createJavaScriptApplicationNode,
|
|
} from "./javascriptApplicationGraph.js";
|
|
import {
|
|
artifactEvidence,
|
|
completeCoverage,
|
|
} from "./javascriptApplicationGraph.fixture.js";
|
|
import { compareJavaScriptApplicationVersions } from "./javascriptApplicationVersionComparison.js";
|
|
|
|
const LEFT_SHA = "a".repeat(64);
|
|
const RIGHT_SHA = "b".repeat(64);
|
|
const LIMITATION =
|
|
"Source is outside the caller-selected source_paths; it was inventoried but not analyzed.";
|
|
const evidenceId = (character: string) => `ev_${character.repeat(64)}`;
|
|
const node = (sha256: string, path: string) =>
|
|
createJavaScriptApplicationNode({
|
|
kind: "javascript-asset",
|
|
identity: { strategy: "content-digest", stability: "global-exact", sha256 },
|
|
observations: [
|
|
{
|
|
label: path,
|
|
properties: { path },
|
|
evidence: artifactEvidence(sha256, path),
|
|
},
|
|
],
|
|
});
|
|
const unavailableScope = (
|
|
artifactSha: string,
|
|
state: "unknown" | "unavailable" = "unavailable",
|
|
) =>
|
|
createJavaScriptApplicationNode({
|
|
kind: "unknown",
|
|
identity: {
|
|
strategy: "artifact-local-key",
|
|
stability: "artifact-version",
|
|
artifact_sha256: artifactSha,
|
|
namespace: "static-parse-scope",
|
|
key: "select-javascript-sources:lib.js",
|
|
},
|
|
observations: [
|
|
{
|
|
label: "lib.js parse scope",
|
|
properties: { path: "lib.js", operation: "select-javascript-sources" },
|
|
evidence: {
|
|
...artifactEvidence(artifactSha, "lib.js"),
|
|
state,
|
|
confidence: "unknown",
|
|
limitations: [LIMITATION],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
const observedAssetWithUnavailableFact = () => {
|
|
const digest = "e".repeat(64);
|
|
return createJavaScriptApplicationNode({
|
|
kind: "javascript-asset",
|
|
identity: {
|
|
strategy: "content-digest",
|
|
stability: "global-exact",
|
|
sha256: digest,
|
|
},
|
|
observations: [
|
|
{
|
|
label: "new.js",
|
|
properties: { path: "new.js" },
|
|
evidence: artifactEvidence(digest, "new.js"),
|
|
},
|
|
{
|
|
label: "new.js parse scope",
|
|
properties: { operation: "parse-javascript" },
|
|
evidence: {
|
|
...artifactEvidence(digest, "new.js"),
|
|
state: "unavailable",
|
|
confidence: "unknown",
|
|
limitations: [LIMITATION],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
};
|
|
const graph = (nodes: ReturnType<typeof node>[]) =>
|
|
createJavaScriptApplicationGraph({
|
|
schema: "JavaScriptApplicationGraph",
|
|
root_node_ids: [nodes[0]!.node_id],
|
|
nodes,
|
|
edges: [],
|
|
coverage: completeCoverage,
|
|
limitations: [],
|
|
});
|
|
const compare = (
|
|
left: ReturnType<typeof node>[],
|
|
right: ReturnType<typeof node>[],
|
|
) =>
|
|
compareJavaScriptApplicationVersions({
|
|
left: {
|
|
evidenceId: evidenceId("1"),
|
|
rootArtifactSha256: LEFT_SHA,
|
|
graph: graph(left),
|
|
},
|
|
right: {
|
|
evidenceId: evidenceId("2"),
|
|
rootArtifactSha256: RIGHT_SHA,
|
|
graph: graph(right),
|
|
},
|
|
leftNativeEvidence: [],
|
|
rightNativeEvidence: [],
|
|
});
|
|
|
|
it("keeps unmatched unavailable scopes unknown in either direction", () => {
|
|
const leftRoot = node("d".repeat(64), "main.js");
|
|
const rightRoot = node("d".repeat(64), "main.js");
|
|
const leftScope = unavailableScope(LEFT_SHA);
|
|
const rightScope = unavailableScope(RIGHT_SHA);
|
|
const forward = compare([leftRoot], [rightRoot, rightScope]);
|
|
const reverse = compare([leftRoot, leftScope], [rightRoot]);
|
|
|
|
expect(
|
|
forward.items.find(
|
|
({ right_node_id }) => right_node_id === rightScope.node_id,
|
|
),
|
|
).toMatchObject({
|
|
status: "unknown",
|
|
dimensions: ["availability"],
|
|
limitations: [LIMITATION],
|
|
evidence_links: [evidenceId("1"), evidenceId("2")],
|
|
});
|
|
expect(
|
|
reverse.items.find(
|
|
({ left_node_id }) => left_node_id === leftScope.node_id,
|
|
),
|
|
).toMatchObject({
|
|
status: "unknown",
|
|
dimensions: ["availability"],
|
|
limitations: [LIMITATION],
|
|
});
|
|
expect(forward.summary).toMatchObject({ added: 0, removed: 0, unknown: 1 });
|
|
expect(reverse.summary).toMatchObject({ added: 0, removed: 0, unknown: 1 });
|
|
});
|
|
|
|
it("keeps matched unavailable scopes unknown with their source limitation", () => {
|
|
const leftRoot = node("d".repeat(64), "main.js");
|
|
const rightRoot = node("d".repeat(64), "main.js");
|
|
const leftScope = unavailableScope(LEFT_SHA);
|
|
const rightScope = unavailableScope(LEFT_SHA);
|
|
const result = compare([leftRoot, leftScope], [rightRoot, rightScope]);
|
|
|
|
expect(
|
|
result.items.find(({ left_node_id }) => left_node_id === leftScope.node_id),
|
|
).toMatchObject({
|
|
right_node_id: rightScope.node_id,
|
|
status: "unknown",
|
|
dimensions: [],
|
|
limitations: [LIMITATION],
|
|
});
|
|
expect(result.summary).toMatchObject({ unchanged: 1, unknown: 1 });
|
|
});
|
|
|
|
it("retains unknown scope facts and reports genuine entity changes", () => {
|
|
const leftRoot = node("d".repeat(64), "main.js");
|
|
const rightRoot = node("d".repeat(64), "main.js");
|
|
const unknown = unavailableScope(RIGHT_SHA, "unknown");
|
|
const added = node("e".repeat(64), "new.js");
|
|
const removed = node("f".repeat(64), "removed.js");
|
|
const mixedEvidenceAddition = observedAssetWithUnavailableFact();
|
|
const unknownResult = compare([leftRoot], [rightRoot, unknown]);
|
|
const addedResult = compare([leftRoot], [rightRoot, added]);
|
|
const removedResult = compare([leftRoot, removed], [rightRoot]);
|
|
const mixedResult = compare([leftRoot], [rightRoot, mixedEvidenceAddition]);
|
|
|
|
expect(
|
|
unknownResult.items.find(
|
|
({ right_node_id }) => right_node_id === unknown.node_id,
|
|
),
|
|
).toMatchObject({ status: "unknown", limitations: [LIMITATION] });
|
|
expect(unknownResult.summary).toMatchObject({ added: 0, unknown: 1 });
|
|
expect(
|
|
addedResult.items.find(
|
|
({ right_node_id }) => right_node_id === added.node_id,
|
|
),
|
|
).toMatchObject({ status: "added", dimensions: ["availability"] });
|
|
expect(addedResult.summary.added).toBe(1);
|
|
expect(
|
|
mixedResult.items.find(
|
|
({ right_node_id }) => right_node_id === mixedEvidenceAddition.node_id,
|
|
),
|
|
).toMatchObject({ status: "added", limitations: [LIMITATION] });
|
|
expect(mixedResult.summary.added).toBe(1);
|
|
expect(
|
|
removedResult.items.find(
|
|
({ left_node_id }) => left_node_id === removed.node_id,
|
|
),
|
|
).toMatchObject({ status: "removed", dimensions: ["availability"] });
|
|
expect(removedResult.summary.removed).toBe(1);
|
|
expect(
|
|
compare(
|
|
[leftRoot, node("c".repeat(64), "same.js")],
|
|
[rightRoot, node("c".repeat(64), "same.js")],
|
|
).summary,
|
|
).toMatchObject({
|
|
unchanged: 2,
|
|
added: 0,
|
|
removed: 0,
|
|
});
|
|
});
|