Files
fusion/scripts/__tests__/content-hash.test.mjs
gsxdsm 610d473e77 refactor(test): simplify-pass cleanups from 4-angle review
- content-hash: createRepoContentSnapshot — 2 repo-wide git spawns shared across all hash computations (was ~2 spawns x N packages x 2 passes, ~0.6-1.6s per cache-miss run); snapshot-equivalence test pins zero-spawn path
- test-changed: one hash memo + snapshot shared between applyCacheToPlan and recordCachePass (record pass now re-hashes nothing)
- ci-test-shard: listPackageTestFiles single source of truth for the test-file glob (was triplicated)
- check-test-inventory: curatedProjects defaults to projects (removes duplicated 11-entry list in spec)
- ensure-test-artifacts: drop detectMissingArtifacts passthrough alias
- drop dead statSync re-export; cross-reference comments on the two shared-input path lists

Skipped deliberately: --cold-start-probe/--check-timings-staleness removal (plan artifacts for U8 re-eval + scheduled refresh), best-fit dedup + threshold² (behavior-preserving refactor of verified shard math — follow-up), worker-budget duplication (pre-existing on main)
2026-06-03 19:12:28 -07:00

204 lines
8.2 KiB
JavaScript

/**
* Unit tests for scripts/lib/content-hash.mjs (U3).
*
* Runner: node --test scripts/__tests__/content-hash.test.mjs
*
* These tests use injectable gitFn/readFn stubs so they never touch the real
* repo or shell out to git.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { computeContentHash, createRepoContentSnapshot } from "../lib/content-hash.mjs";
/**
* Build a fake git runner from a description of the tree.
*
* @param {object} tree
* @param {Record<string,string>} tree.tracked path -> blob sha (clean tracked files)
* @param {string[]} [tree.dirty] tracked paths that are modified in worktree
* @param {string[]} [tree.untracked] untracked-not-ignored paths
*/
function fakeGit(tree) {
const { tracked = {}, dirty = [], untracked = [] } = tree;
// Honor the `-- <paths>` filter the way real git does (exact file or dir
// prefix); commands without a path filter return the whole tree.
const selected = (args, file) => {
const dashIdx = args.indexOf("--");
if (dashIdx === -1) return true;
const inputs = args.slice(dashIdx + 1);
return inputs.some((input) => file === input || file.startsWith(`${input}/`));
};
return (args) => {
if (args[0] === "ls-files") {
return Object.entries(tracked)
.filter(([file]) => selected(args, file))
.map(([file, sha]) => `100644 ${sha} 0\t${file}`)
.join("\n");
}
if (args[0] === "status") {
const lines = [];
for (const file of dirty) if (selected(args, file)) lines.push(` M ${file}`);
for (const file of untracked) if (selected(args, file)) lines.push(`?? ${file}`);
return lines.join("\n");
}
return null;
};
}
const readBytes = (contentByPath) => (absPath) => {
// absPath is rootDir + "/" + relPath; match on suffix.
for (const [rel, content] of Object.entries(contentByPath)) {
if (absPath.endsWith(rel)) return Buffer.from(content);
}
throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
};
const base = { rootDir: "/repo", inputPaths: ["packages/core/src"] };
test("computeContentHash is stable for identical tracked content", () => {
const git = fakeGit({ tracked: { "packages/core/src/a.ts": "aaa", "packages/core/src/b.ts": "bbb" } });
const h1 = computeContentHash({ ...base, gitFn: git, readFn: readBytes({}) });
const h2 = computeContentHash({ ...base, gitFn: git, readFn: readBytes({}) });
assert.equal(h1, h2);
assert.equal(h1.length, 64);
});
test("computeContentHash busts when a tracked blob sha changes (real source change)", () => {
const before = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" } }),
readFn: readBytes({}),
});
const after = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "zzz" } }),
readFn: readBytes({}),
});
assert.notEqual(before, after);
});
test("branch-switch with identical content yields the same hash (mtime-independent)", () => {
// Two 'branches' with the same tracked blob shas → identical hash, even though
// a real checkout would rewrite mtimes. The hash never reads mtime.
const git = fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" } });
const branchA = computeContentHash({ ...base, gitFn: git, readFn: readBytes({}) });
const branchB = computeContentHash({ ...base, gitFn: git, readFn: readBytes({}) });
assert.equal(branchA, branchB);
});
test("dirty tracked file is hashed by working-tree bytes, not the stale index sha", () => {
// Same index blob sha, but the worktree content differs → different hashes.
const clean = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" } }),
readFn: readBytes({ "packages/core/src/a.ts": "ON-DISK-V1" }),
});
const dirty = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" }, dirty: ["packages/core/src/a.ts"] }),
readFn: readBytes({ "packages/core/src/a.ts": "ON-DISK-V2" }),
});
assert.notEqual(clean, dirty);
});
test("two different working-tree contents of a dirty file produce different hashes", () => {
const v1 = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" }, dirty: ["packages/core/src/a.ts"] }),
readFn: readBytes({ "packages/core/src/a.ts": "V1" }),
});
const v2 = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" }, dirty: ["packages/core/src/a.ts"] }),
readFn: readBytes({ "packages/core/src/a.ts": "V2" }),
});
assert.notEqual(v1, v2);
});
test("untracked file is folded into the hash via its bytes", () => {
const without = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" } }),
readFn: readBytes({}),
});
const withUntracked = computeContentHash({
...base,
gitFn: fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" }, untracked: ["packages/core/src/new.ts"] }),
readFn: readBytes({ "packages/core/src/new.ts": "brand new" }),
});
assert.notEqual(without, withUntracked);
});
test("porcelain status parsing tolerates spacing variants (M path vs ' M path')", () => {
// git emits the worktree-modified code in either " M path" or "M path" form
// depending on staged/worktree state; both must be recognized as dirty.
const tracked = { "packages/core/src/a.ts": "aaa" };
const readFn = (absPath) => (absPath.endsWith("a.ts") ? Buffer.from("ON-DISK") : Buffer.from(""));
const variantGit = (statusLine) => (args) => {
if (args[0] === "ls-files") {
return Object.entries(tracked).map(([f, s]) => `100644 ${s} 0\t${f}`).join("\n");
}
if (args[0] === "status") return statusLine;
return null;
};
const clean = computeContentHash({ ...base, gitFn: variantGit(""), readFn: () => Buffer.from("") });
const variantA = computeContentHash({ ...base, gitFn: variantGit(" M packages/core/src/a.ts"), readFn });
const variantB = computeContentHash({ ...base, gitFn: variantGit("M packages/core/src/a.ts"), readFn });
assert.notEqual(clean, variantA, "leading-space variant must register as dirty");
assert.notEqual(clean, variantB, "trailing-space variant must register as dirty");
// Both variants describe the same dirty file/content → identical hash.
assert.equal(variantA, variantB);
});
test("versionPrefix busts the hash so a format bump invalidates all entries", () => {
const git = fakeGit({ tracked: { "packages/core/src/a.ts": "aaa" } });
const v1 = computeContentHash({ ...base, versionPrefix: "v1", gitFn: git, readFn: readBytes({}) });
const v2 = computeContentHash({ ...base, versionPrefix: "v2", gitFn: git, readFn: readBytes({}) });
assert.notEqual(v1, v2);
});
test("snapshot path produces identical hashes to the spawn path and spawns no git", () => {
const tree = {
tracked: {
"packages/core/src/a.ts": "aaa",
"packages/core/src/b.ts": "bbb",
"packages/engine/src/c.ts": "ccc",
"pnpm-lock.yaml": "lll",
},
dirty: ["packages/core/src/b.ts"],
untracked: ["packages/engine/src/new.ts"],
};
const readFn = readBytes({
"packages/core/src/b.ts": "B-ON-DISK",
"packages/engine/src/new.ts": "NEW-ON-DISK",
});
const snapshot = createRepoContentSnapshot({ rootDir: base.rootDir, gitFn: fakeGit(tree) });
for (const inputPaths of [["packages/core"], ["packages/engine"], ["pnpm-lock.yaml"], ["packages/core", "pnpm-lock.yaml"]]) {
const viaSpawn = computeContentHash({ ...base, inputPaths, gitFn: fakeGit(tree), readFn });
let spawnCalls = 0;
const viaSnapshot = computeContentHash({
...base,
inputPaths,
gitFn: () => {
spawnCalls += 1;
return null;
},
readFn,
snapshot,
});
assert.equal(viaSnapshot, viaSpawn, `hash mismatch for ${inputPaths.join(",")}`);
assert.equal(spawnCalls, 0, "snapshot path must not invoke git");
}
// Prefix selection must not match sibling dirs sharing a name prefix.
const coreOnly = computeContentHash({ ...base, inputPaths: ["packages/core"], readFn, snapshot });
const engineOnly = computeContentHash({ ...base, inputPaths: ["packages/engine"], readFn, snapshot });
assert.notEqual(coreOnly, engineOnly);
});