Files
rea/tests/boundary/filesystem/referenceSourceIgnorePolicy.test.ts
T

175 lines
6.0 KiB
TypeScript

import { mkdir, readFile, symlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { execFileOutput } from "../../../src/process/ExecFileOutput.js";
import { importReferenceSource } from "../../support/referenceSourceResourceScope.js";
const importTree = async (root: string) => {
const result = await importReferenceSource({
root,
caller: "ignore-policy-test",
policy: { secretPatterns: [] },
});
if (!result.ok) throw result.error;
return result.value;
};
it("scopes nested ignore rules and negations like Git while preserving mandatory exclusions", async () => {
const root = await createTestTempDirectory("rea-reference-scoped-policy-");
await execFileOutput("git", ["init", "--initial-branch=main", root]);
await execFileOutput("git", [
"-C",
root,
"config",
"core.ignorecase",
"false",
]);
await mkdir(join(root, "packages", "widget", "blocked"), { recursive: true });
await mkdir(join(root, "packages", "widget", "node_modules"));
await mkdir(join(root, "sibling"));
await writeFile(join(root, ".gitignore"), "*.tmp\nblocked/\n");
await writeFile(
join(root, "packages", "widget", ".gitignore"),
"/local.js\n!keep.tmp\n!node_modules/\n",
);
await writeFile(
join(root, "packages", "widget", "blocked", ".gitignore"),
"!inside.js\n",
);
const excluded = [
"drop.tmp",
"packages/widget/local.js",
"packages/widget/blocked/inside.js",
];
const included = [
"packages/widget/keep.tmp",
"packages/widget/main.js",
"sibling/local.js",
];
for (const path of [
...excluded,
...included,
"packages/widget/node_modules/dependency.js",
])
await writeFile(join(root, path), "export const value = 1;\n");
const oracle = await execFileOutput("git", [
"-C",
root,
"check-ignore",
"--no-index",
...excluded,
...included,
]);
expect(oracle.stdout.trim().split(/\r?\n/u)).toEqual(excluded);
const graph = await importTree(root);
const files = graph.entries
.filter(({ kind }) => kind === "file")
.map(({ path }) => path);
for (const path of included) expect(files).toContain(path);
for (const path of [
...excluded,
"packages/widget/node_modules/dependency.js",
])
expect(files).not.toContain(path);
expect(graph.exclusions).toContainEqual({
path: "packages/widget/local.js",
entry_kind: "file",
reason: "project-ignored",
pattern: "/local.js",
});
expect(graph.exclusions).toContainEqual({
path: "packages/widget/node_modules",
entry_kind: "directory",
reason: "default-ignored",
pattern: "node_modules/",
});
expect(
await readFile(join(root, "packages", "widget", "keep.tmp"), "utf8"),
).toBe("export const value = 1;\n");
});
it.each([false, true])(
"honors local core.ignorecase=%s against Git's check-ignore oracle",
async (ignoreCase) => {
const root = await createTestTempDirectory("rea-reference-ignore-case-");
await execFileOutput("git", ["init", "--initial-branch=main", root]);
await execFileOutput("git", [
"-C",
root,
"config",
"core.ignorecase",
String(ignoreCase),
]);
await writeFile(join(root, ".gitignore"), "*.TMP\n");
await writeFile(join(root, "upper.TMP"), "upper\n");
await writeFile(join(root, "lower.tmp"), "lower\n");
const oracle = await execFileOutput("git", [
"-C",
root,
"check-ignore",
"--no-index",
"upper.TMP",
"lower.tmp",
]);
const expected = oracle.stdout.trim().split(/\r?\n/u);
const graph = await importTree(root);
expect(
graph.exclusions
.filter(({ reason }) => reason === "project-ignored")
.map(({ path }) => path),
).toEqual([...expected].sort());
expect(graph.entries.some(({ path }) => path === "lower.tmp")).toBe(
!ignoreCase,
);
},
);
it.skipIf(process.platform === "win32")(
"binds Git pointer/configuration reads to physical symlink-parent selection",
async () => {
const root = await createTestTempDirectory("rea-reference-git-parent-");
const source = join(root, "source");
const physical = join(root, "physical");
await mkdir(source);
await mkdir(join(physical, "child"), { recursive: true });
await mkdir(join(physical, "metadata"));
await mkdir(join(source, "metadata"));
await symlink(join(physical, "child"), join(source, "alias"));
const intendedHead = "1".repeat(40);
for (const [directory, head, ignoreCase] of [
[join(physical, "metadata"), intendedHead, false],
[join(source, "metadata"), "2".repeat(40), true],
] as const) {
await writeFile(join(directory, "HEAD"), `${head}\n`);
await writeFile(
join(directory, "config"),
`[core]\nignorecase = ${String(ignoreCase)}\n`,
);
}
await writeFile(join(source, ".git"), "gitdir: alias/../metadata\n");
await writeFile(join(source, ".gitignore"), "*.TMP\n");
await writeFile(join(source, "lower.tmp"), "selected policy\n");
const graph = await importTree(source);
expect(graph.entries.some(({ path }) => path === "lower.tmp")).toBe(true);
expect(graph.vcs).toMatchObject({ kind: "git", head: intendedHead });
},
);
it("keeps a present but unresolved Git common-directory pointer from selecting private decoy configuration", async () => {
const root = await createTestTempDirectory("rea-reference-common-missing-");
await mkdir(join(root, ".git"));
await writeFile(join(root, ".git", "HEAD"), `${"1".repeat(40)}\n`);
await writeFile(join(root, ".git", "config"), "[core]\nignorecase = true\n");
await writeFile(join(root, ".git", "commondir"), "../missing-common\n");
const result = await importReferenceSource({
root,
caller: "ignore-policy-test",
policy: { secretPatterns: [] },
});
expect(result).toMatchObject({
ok: false,
error: { code: "io", message: expect.stringContaining("missing-common") },
});
});