Files
rea/tests/boundary/filesystem/ciTestProjects.test.ts
T
Kaoru0822 6d356b8434 test(ci): keep module mocks in isolated test projects (#5886)
* test(ci): keep module mocks in isolated test projects

A module mock registered after another file in the same worker loaded the
module never applies, so a mocking test in a project that shares one module
graph passes or fails with file scheduling. That is how the environment
export source tests failed on hosted runs until c324d31b4 removed their
mocks.

Guard the rule: every tracked test using vi.mock or vi.doMock must belong
to an isolated project. Move the last shared-graph case,
DirectAnalysisStatFailure, which mocks node:fs/promises, into process-global
beside the other module-mocking workflow tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(pe): share one compiled CLI process across PE resource inspections

The acceptance test started a fresh CLI process for each of its fixtures
inside one 30 s test deadline. Hosted runs took 24-27 s and c324d31b4 timed
out. Every inspection there reads no stdin and sets no deadline, so use the
fixture's file-scoped compiled CLI process. Local time drops from 8.5 s to
about 3.9 s with the same assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:51:24 -07:00

185 lines
6.0 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { 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 } from "../../../src/process/ExecFileOutput.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const repository = process.cwd();
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: [], heap: 384, timeout: 30_000, lines: 68 },
{ flags: ["--coverage"], heap: 512, timeout: 60_000, lines: 68 },
{ flags: ["--coverage.enabled"], heap: 512, timeout: 60_000, lines: 68 },
{ flags: ["--coverage=false"], heap: 384, timeout: 30_000, lines: 68 },
{
flags: ["--coverage.enabled=false"],
heap: 384,
timeout: 30_000,
lines: 68,
},
{
flags: ["--coverage.provider=v8"],
heap: 384,
timeout: 30_000,
lines: 68,
},
{
flags: ["--coverage.enabled", "--shard", "1/6"],
heap: 512,
timeout: 60_000,
lines: 0,
},
{
flags: ["--coverage.enabled=false", "--shard=1/6"],
heap: 384,
timeout: 30_000,
lines: 0,
},
])(
"resolves worker resources from actual CLI options: $flags",
async ({ flags, heap, 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=${String(heap)}`],
timeout,
lines,
});
},
);
});
describe("test module isolation", () => {
it("keeps module mocks out of projects that share one module graph", async () => {
// A mock registered after another file in the same worker loaded the
// module never applies, so the test's outcome depends on file scheduling.
const root = await createTestTempDirectory("rea-vitest-mock-isolation-");
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 !== false]),
)));
`,
);
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>;
const mocking = execFileSync(
"git",
["grep", "-l", "-E", String.raw`vi\.(mock|doMock)\(`, "--", "*.test.ts"],
{ cwd: repository, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean);
expect(mocking.length).toBeGreaterThan(0);
expect(
mocking.filter((file) => isolated[projectForTest(file).name] !== true),
).toEqual([]);
});
});