fix(javascript): keep source-module semantics independent of sibling bytes

This commit is contained in:
morluto committed 2026-10-15 03:43:29 +08:00
1 parent a9588be4eb
commit 30a9139b87
2 files changed
+65 -1

No files matched your search

@@ -6,6 +6,7 @@ import { describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
import { parseJavaScriptApplicationGraph } from "../../../../src/domain/javascript/javascriptApplicationGraph.js";
import { compareJavaScriptApplicationVersions } from "../../../../src/domain/javascript/javascriptApplicationVersionComparison.js";
import {
findExportNode,
@@ -15,6 +16,70 @@ import {
writeFixtureFiles,
} from "../../../support/javascriptApplicationFixture.js";
describe("cross-version source module identity", () => {
it("keeps a source module unchanged when only a sibling file changes", async () => {
const root = await createTestTempDirectory("rea-source-module-sibling-");
const versions = await Promise.all(
["left", "right"].map(async (name) => {
const path = join(root, name);
await writeFixtureFiles(path, {
"main.js": 'require("./util.js");\n',
"util.js": `module.exports = ${JSON.stringify(name)};\n`,
});
return reconstructJavaScriptFixture(path);
}),
);
const [left, right] = versions;
if (left === undefined || right === undefined)
throw new Error("Missing JavaScript application version");
const leftModule = findSourceModule(left.graph, "main.js");
const rightModule = findSourceModule(right.graph, "main.js");
if (leftModule === undefined || rightModule === undefined)
throw new Error("Missing main source module");
const leftObservation = leftModule.observations.find(
({ properties }) => properties.semantic_role === "source-module",
);
const rightObservation = rightModule.observations.find(
({ properties }) => properties.semantic_role === "source-module",
);
expect(leftObservation?.properties).not.toHaveProperty("container_sha256");
expect(rightObservation?.properties).not.toHaveProperty("container_sha256");
expect(leftObservation?.properties).toEqual(rightObservation?.properties);
expect(leftObservation?.properties.source_sha256).toBe(
rightObservation?.properties.source_sha256,
);
const comparison = compareJavaScriptApplicationVersions({
left: {
evidenceId: `ev_${"a".repeat(64)}`,
rootArtifactSha256: left.root_artifact_sha256,
graph: left.graph,
},
right: {
evidenceId: `ev_${"b".repeat(64)}`,
rootArtifactSha256: right.root_artifact_sha256,
graph: right.graph,
},
leftNativeEvidence: [],
rightNativeEvidence: [],
});
expect(
comparison.items.find(
({ left_node_id }) => left_node_id === leftModule.node_id,
),
).toMatchObject({
status: "unchanged",
right_node_id: rightModule.node_id,
match: {
status: "matched",
basis: "exact-module-source-digest",
confidence: "exact",
},
});
});
});
describe("TypeScript source module loader semantics", () => {
it("publishes each source module's own content digest", async () => {
const root = await createTestTempDirectory("rea-source-module-digest-");