Files
reverse-engineering-anything/tests/boundary/filesystem/referenceSourceGitMetadataExclusion.test.ts
Wenyu Chiou e01fcfb4b2 fix(reference): Exclude Git pointer files from source identities (#7667)
* fix(reference): Exclude Git pointer files from source identities

Match .git pointer files in the default import policy after the existing
directory rules, preserving ordinary-checkout exclusion provenance and Git
HEAD discovery. Cover linked-worktree identities, repeated imports, nested
metadata, similarly named source files, and configured-secret precedence.

Independent review: parent independent review APPROVE for Draft CI on patch
61bddf108952c77858fa71c333b4bb8a05ab33e8b03f5c6bccd879cd19c7c379 and source
tree 2d2372a9ec. Review triggers: delegated
shipping output, source inventory/file I/O behavior, and more than 50 lines.

Validation: npm run check passed with Node 24.22.1/npm 11.16.0 on Windows;
tracked Vitest regression 1 passed/3 Windows skips; test discovery passed.
Eight offline Linux production imports passed on cached Node 22.19.0 as
supporting evidence only. Pinned Linux/macOS Vitest cases await hosted CI.

Fixes #7551

* test(reference): Isolate worktree fixtures from host Git settings

Route every fixture Git command through one wrapper with a filtered child
environment, owned empty global config/template/hook paths, and no system
config reads. Keep resource cleanup under the existing temporary owner.
These settings are scoped to synthetic child processes; host Git settings
and this repository's commit/push checks are unchanged.

Targeted independent review: parent review APPROVE on patch
4be9a2af3e40edac3d64f687170e966a87fcf762f1877f2c55016e120bcd90ac and tree
a2ba91ee9c. The reviewer confirmed the native
proof helper hash, complete fixture command coverage, and owned cleanup.
Review triggers: delegated follow-up and fixture file/process boundaries.

Validation: npm run check:fast passed; targeted Vitest 1 passed/3 native
Windows skips. On pinned Node 24.22.1, the actual helper rejects inherited
hooks/templates while commit, worktree creation, HEAD and status checks pass.
The unisolated control copies the injected template and its commit fails.

Refs #7551
2026-11-03 14:49:28 +08:00

209 lines
7.1 KiB
TypeScript

import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import ignore from "ignore";
import { describe, expect, it } from "vitest";
import { DEFAULT_REFERENCE_SOURCE_IGNORE_PATTERNS } from "../../../src/application/ReferenceSourceImportTypes.js";
import { createHistoricalSourceManifest } from "../../../src/domain/referenceSourceGraph.js";
import { execFileOutput } from "../../../src/process/ExecFileOutput.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { importReferenceSource } from "../../support/referenceSourceResourceScope.js";
const importTree = async (
root: string,
secretPatterns: readonly string[] = [],
excludePaths: readonly string[] = [],
) => {
const result = await importReferenceSource({
root,
caller: "git-metadata-test",
policy: { secretPatterns },
excludePaths,
});
if (!result.ok) throw result.error;
return result.value;
};
const fixtureGit = (
directory: string,
configuration: string,
args: readonly string[],
) =>
execFileOutput(
"git",
["-c", `core.hooksPath=${join(configuration, "no-hooks")}`, ...args],
{
cwd: directory,
env: {
...Object.fromEntries(
Object.entries(process.env).filter(
([name]) => !name.toUpperCase().startsWith("GIT_"),
),
),
GIT_CONFIG_GLOBAL: join(configuration, "gitconfig"),
GIT_CONFIG_NOSYSTEM: "1",
GIT_TEMPLATE_DIR: join(configuration, "no-template"),
},
},
);
it("excludes Git metadata files and directories without broadening directory-only defaults", () => {
const matcher = ignore().add([...DEFAULT_REFERENCE_SOURCE_IGNORE_PATTERNS]);
for (const path of [".git", ".git/", "nested/.git", "nested/.git/"])
expect(matcher.ignores(path), path).toBe(true);
for (const path of [".gitignore", ".github", ".gitkeep", "dist"])
expect(matcher.ignores(path), path).toBe(false);
expect(matcher.ignores("dist/")).toBe(true);
expect(matcher.test(".git").rule?.pattern).toBe(".git");
expect(matcher.test(".git/").rule?.pattern).toBe(".git/");
});
describe.skipIf(process.platform === "win32")(
"reference source Git metadata exclusions",
() => {
it("keeps linked-worktree identities stable while retaining HEAD and ordinary-checkout provenance", async () => {
const root = await createTestTempDirectory("rea-reference-git-metadata-");
const repository = join(root, "repository");
await Promise.all([
mkdir(join(root, "no-hooks")),
mkdir(join(root, "no-template")),
writeFile(join(root, "gitconfig"), ""),
]);
await mkdir(join(repository, "src"), { recursive: true });
await writeFile(
join(repository, "src", "main.js"),
"export const value = 7;\n",
);
await writeFile(join(repository, ".gitignore"), "*.log\n");
const git = (...args: string[]) => fixtureGit(repository, root, args);
await git("init", "--initial-branch=main");
await git("add", ".gitignore", "src/main.js");
await git(
"-c",
"user.name=REA fixture",
"-c",
"user.email=fixture@example.invalid",
"-c",
"commit.gpgsign=false",
"commit",
"-m",
"fixture",
);
const head = (await git("rev-parse", "HEAD")).stdout.trim();
const leftRoot = join(root, "left");
const rightRoot = join(root, "right");
for (const worktree of [leftRoot, rightRoot]) {
await git("worktree", "add", "--detach", worktree, head);
expect(
(await fixtureGit(worktree, root, ["status", "--porcelain"])).stdout,
).toBe("");
}
expect(await readFile(join(leftRoot, ".git"), "utf8")).not.toBe(
await readFile(join(rightRoot, ".git"), "utf8"),
);
const importSelectedTree = async (source: string) => {
const graph = await importTree(source);
expect(graph.vcs).toEqual({ kind: "git", head, dirty: null });
expect(
graph.entries
.filter(({ kind }) => kind === "file")
.map(({ path }) => path),
).toEqual([".gitignore", "src/main.js"]);
expect(
graph.entries.some(
({ path }) => path === ".git" || path.startsWith(".git/"),
),
).toBe(false);
return graph;
};
const left = await importSelectedTree(leftRoot);
const right = await importSelectedTree(rightRoot);
const repeated = await importSelectedTree(leftRoot);
expect(createHistoricalSourceManifest(left)).toEqual(
createHistoricalSourceManifest(right),
);
expect(createHistoricalSourceManifest(repeated)).toEqual(
createHistoricalSourceManifest(left),
);
for (const graph of [left, right])
expect(graph.exclusions).toEqual([
{
path: ".git",
entry_kind: "file",
reason: "default-ignored",
pattern: ".git",
},
]);
const ordinary = await importSelectedTree(repository);
expect(ordinary.exclusions).toEqual([
{
path: ".git",
entry_kind: "directory",
reason: "default-ignored",
pattern: ".git/",
},
]);
});
it.each(["file", "directory"] as const)(
"prunes nested .git %s metadata and preserves configured-secret precedence",
async (kind) => {
const root = await createTestTempDirectory("rea-reference-nested-git-");
const nested = join(root, "nested");
await mkdir(nested);
if (kind === "file")
await writeFile(
join(nested, ".git"),
"gitdir: /checkout-specific/metadata\n",
);
else {
await mkdir(join(nested, ".git", "hooks"), { recursive: true });
await writeFile(
join(nested, ".git", "hooks", "pre-commit"),
"metadata\n",
);
}
for (const name of [
".gitignore",
".github",
".gitkeep",
"dist",
"source.js",
])
await writeFile(join(nested, name), "");
for (const secretPatterns of [[], [".git"]]) {
const graph = await importTree(
root,
secretPatterns,
secretPatterns.length === 0 ? [] : ["!.git"],
);
expect(graph.exclusions).toEqual([
{
path: "nested/.git",
entry_kind: kind,
reason:
secretPatterns.length === 0
? "default-ignored"
: "configured-secret",
pattern:
secretPatterns.length === 0 && kind === "directory"
? ".git/"
: ".git",
},
]);
expect(
graph.entries
.filter(({ kind }) => kind === "file")
.map(({ path }) => path),
).toEqual([
"nested/.github",
"nested/.gitignore",
"nested/.gitkeep",
"nested/dist",
"nested/source.js",
]);
}
},
);
},
);