Files
reverse-engineering-anything/tests/boundary/filesystem/ciTestProjects.test.ts
T
morluto c8269ca497 fix(ci): own mock isolation and normalize Git patch directories
Convert native directory separators only at Git patch argument production. Exercise fresh, applied and invalid dependency patches in private copies, preserving protectNTFS, and select the regression for Windows CI.

Replace the spelling-based mock inventory with one binding-aware Oxlint rule. Canonical test projects own isolation for both Vitest and lint; external temporary CLI probes avoid concurrent test discovery.

Validated actual local Git and Oxlint execution, resolved-policy parity, CI selection and report tests. Native Windows verification remains with CI. Refs: #7050
2026-10-25 09:49:26 +08:00

284 lines
8.8 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { describe, expect, it } from "vitest";
import {
BUILT_TEST_PROJECTS,
SOURCE_TEST_PROJECTS,
projectForTest,
} from "../../../scripts/lib/test-projects.mjs";
import {
developmentTestPlan,
parseDevelopmentTestRequest,
} from "../../../scripts/lib/development-tests.mjs";
import {
execFileOutput,
execFileOutputFailure,
} from "../../../src/process/ExecFileOutput.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { writeRepositoryOxlintConfiguration } from "../../fixtures/oxlintConfiguration.js";
const repository = process.cwd();
const oxlint = join(repository, "node_modules/oxlint/bin/oxlint");
const oxlintConfig = join(repository, ".oxlintrc.json");
const runOxlint = async (
cwd: string,
config: string,
files: readonly string[],
) => {
let stdout: string;
let exitCode: string | number = 0;
try {
({ stdout } = await execFileOutput(
process.execPath,
[oxlint, "--threads=1", "--config", config, "--format", "json", ...files],
{
cwd,
timeout: 60_000,
maxBuffer: 8 * 1024 * 1024,
stopSignal: "SIGTERM",
},
));
} catch (cause) {
const failure = execFileOutputFailure(cause);
if (
failure === undefined ||
failure.code !== 1 ||
failure.signal !== null ||
failure.killed ||
failure.outputTruncated
)
throw cause;
stdout = failure.stdout;
exitCode = failure.code;
}
return {
exitCode,
report: JSON.parse(stdout) as {
diagnostics?: Array<{ code?: string; severity: string }>;
},
};
};
describe("canonical test-project ownership", () => {
it("assigns every tracked test file exactly once to source or built", () => {
const files = execFileSync("git", ["ls-files", "*.test.ts"], {
cwd: repository,
encoding: "utf8",
})
.trim()
.split("\n")
.filter(Boolean);
const grouped = new Map<string, string[]>();
for (const file of files) {
const project = projectForTest(file);
grouped.set(project.group, [...(grouped.get(project.group) ?? []), file]);
}
expect(grouped.get("source")).not.toBeUndefined();
expect(grouped.get("built")).not.toBeUndefined();
expect(
(grouped.get("source")?.length ?? 0) +
(grouped.get("built")?.length ?? 0),
).toBe(files.length);
expect(
SOURCE_TEST_PROJECTS.reduce(
(count, project) => count + project.include.length,
0,
),
).toBeGreaterThan(0);
expect(
BUILT_TEST_PROJECTS.every((project) => project.group === "built"),
).toBe(true);
expect(() => projectForTest("tests/unowned/example.test.ts")).toThrow(
"No test project owns",
);
});
it("owns the Hopper real-provider conformance test as built and requires preparation", () => {
const testPath =
"tests/conformance/providers/hopper/realHopperSearch.test.ts";
expect(projectForTest(testPath)).toMatchObject({
name: "boundary",
group: "built",
});
expect(() => parseDevelopmentTestRequest("local", [testPath])).toThrow(
`Use test:focused for built test projects: ${testPath}`,
);
const request = parseDevelopmentTestRequest("focused", [testPath]);
expect(developmentTestPlan(request)).toMatchObject({ needsBuild: true });
});
it("uses canonical selectors in Vitest while retaining independent runner policy", async () => {
const config = await readFile(join(repository, "vitest.config.ts"), "utf8");
expect(config).toContain(
"[...SOURCE_TEST_PROJECTS, ...BUILT_TEST_PROJECTS]",
);
expect(config).toContain("PROJECT_POLICIES");
expect(config).not.toContain('"src/{contracts,domain}/**/*.test.ts"');
});
it.each([
{ flags: [], timeout: 30_000, lines: 68 },
{ flags: ["--coverage"], timeout: 60_000, lines: 68 },
{ flags: ["--coverage.enabled"], timeout: 60_000, lines: 68 },
{ flags: ["--coverage=false"], timeout: 30_000, lines: 68 },
{
flags: ["--coverage.enabled=false"],
timeout: 30_000,
lines: 68,
},
{
flags: ["--coverage.provider=v8"],
timeout: 30_000,
lines: 68,
},
{
flags: ["--coverage.enabled", "--shard", "1/6"],
timeout: 60_000,
lines: 0,
},
{
flags: ["--coverage.enabled=false", "--shard=1/6"],
timeout: 30_000,
lines: 0,
},
])(
"resolves worker resources from actual CLI options: $flags",
async ({ flags, timeout, lines }) => {
const root = await createTestTempDirectory("rea-vitest-worker-policy-");
const script = join(root, "worker-policy.mjs");
await writeFile(
script,
`
import config from ${JSON.stringify(pathToFileURL(join(repository, "vitest.config.ts")).href)};
const project = config.test.projects.find(project => project.test.name === 'cli-boundary');
console.log(JSON.stringify({
execArgv: project.test.execArgv,
timeout: config.test.testTimeout,
lines: config.test.coverage.thresholds.lines,
}));
`,
);
const result = await execFileOutput(
process.execPath,
["--import", "tsx", script, ...flags],
{
cwd: repository,
timeout: 10_000,
maxBuffer: 64 * 1024,
stopSignal: "SIGTERM",
},
);
expect(JSON.parse(result.stdout)).toEqual({
execArgv: ["--max-old-space-size=512"],
timeout,
lines,
});
},
);
});
describe("test module isolation", () => {
it.each([
[
"tests/process-global/evidenceBundleHeadroom.test.ts",
"process-global",
"built",
],
[
"tests/process-global/javascriptAnalysisCancellation.test.ts",
"process-global",
"built",
],
])("keeps %s in its isolated preparation group", (path, name, group) => {
expect(projectForTest(path)).toMatchObject({ name, group });
});
it("uses one isolation policy for Vitest and binding-aware mock lint", async () => {
const files = execFileSync("git", ["ls-files", "*.test.ts"], {
cwd: repository,
encoding: "utf8",
})
.trim()
.split("\n")
.filter(Boolean);
const root = await createTestTempDirectory("rea-vitest-project-policy-");
const script = join(root, "project-isolation.mjs");
await writeFile(
script,
`
import config from ${JSON.stringify(pathToFileURL(join(repository, "vitest.config.ts")).href)};
console.log(JSON.stringify(Object.fromEntries(
config.test.projects.map(project => [project.test.name, project.test.isolate]),
)));
`,
);
const result = await execFileOutput(
process.execPath,
["--import", "tsx", script],
{
cwd: repository,
timeout: 10_000,
maxBuffer: 64 * 1024,
stopSignal: "SIGTERM",
},
);
const isolated = JSON.parse(result.stdout) as Record<string, boolean>;
expect(files.length).toBeGreaterThan(0);
expect(
files.filter((file) => {
const project = projectForTest(file);
return isolated[project.name] !== (project.isolate !== false);
}),
).toEqual([]);
const { report } = await runOxlint(repository, oxlintConfig, files);
expect(
report.diagnostics?.filter(
({ code, severity }) =>
code === "rea(no-shared-module-mock)" && severity === "error",
),
).toEqual([]);
});
it("rejects actual Vitest module-mock aliases only in shared projects", async () => {
const root = await createTestTempDirectory("rea-vitest-mock-probe-");
const sharedDirectory = join(root, "tests/evaluation");
const isolatedDirectory = join(root, "tests/process-global");
await mkdir(sharedDirectory, { recursive: true });
await mkdir(isolatedDirectory, { recursive: true });
const config = await writeRepositoryOxlintConfiguration(root);
const probe = async (directory: string, source: string) => {
const path = join(directory, "mock-probe.test.ts");
await writeFile(path, source);
return runOxlint(root, config, [path]);
};
const shared = await probe(
sharedDirectory,
'import { vi as testing } from "vitest"; testing.mock("./dependency.js");',
);
const isolated = await probe(
isolatedDirectory,
'import * as testing from "vitest"; testing.vi.doMock("./dependency.js");',
);
expect(shared.exitCode).toBe(1);
expect(
shared.report.diagnostics?.filter(
({ code, severity }) =>
code === "rea(no-shared-module-mock)" && severity === "error",
),
).toHaveLength(1);
expect(isolated.exitCode).toBe(0);
expect(
isolated.report.diagnostics?.filter(
({ code }) => code === "rea(no-shared-module-mock)",
),
).toEqual([]);
});
});