mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 13:52:32 -05:00
* 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 untilc324d31b4removed 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 andc324d31b4timed 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>
118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
import { afterEach, expect, it, vi } from "vitest";
|
|
|
|
const statFailures = vi.hoisted(() => new Map<string, unknown>());
|
|
|
|
vi.mock("node:fs/promises", async (importOriginal) => {
|
|
const original = await importOriginal<typeof import("node:fs/promises")>();
|
|
return {
|
|
...original,
|
|
stat: vi.fn(async (...args: Parameters<typeof original.stat>) => {
|
|
const failure = statFailures.get(String(args[0]));
|
|
if (failure !== undefined) throw failure;
|
|
return original.stat(...args);
|
|
}),
|
|
};
|
|
});
|
|
|
|
import { parseConfig } from "../../src/config/parseConfig.js";
|
|
import { classifyBinaryTargetFilesystemFailure } from "../../src/application/BinaryTargetResolver.js";
|
|
import type { BinarySession } from "../../src/application/binary/BinarySession.js";
|
|
import type { DirectAnalysisDependencies } from "../../src/application/DirectAnalysisDependencies.js";
|
|
import { runProviderAnalysis } from "../../src/application/DirectAnalysis.js";
|
|
|
|
afterEach(() => statFailures.clear());
|
|
|
|
it("retains the original filesystem cause on the shared typed error", () => {
|
|
const cause = Object.assign(new Error("permission denied"), {
|
|
code: "EACCES",
|
|
});
|
|
const failure = classifyBinaryTargetFilesystemFailure(
|
|
"/tmp/rea-stat-failure-cause.app",
|
|
cause,
|
|
);
|
|
|
|
expect(failure?.cause).toBe(cause);
|
|
expect(failure?.systemCode).toBe("EACCES");
|
|
});
|
|
|
|
it.each([
|
|
{ code: "ENOENT", projectedCode: "target_unavailable" },
|
|
{ code: "ENOTDIR", projectedCode: "target_unavailable" },
|
|
{ code: "EACCES", projectedCode: "access_denied", systemCode: "EACCES" },
|
|
{ code: "EPERM", projectedCode: "access_denied", systemCode: "EPERM" },
|
|
{ code: "EIO", projectedCode: "target_unavailable" },
|
|
])(
|
|
"stops CLI resource dispatch on stat failure $code and preserves its path",
|
|
async ({ code, projectedCode, systemCode }) => {
|
|
const path = `/tmp/rea-stat-failure-${code}.app`;
|
|
const cause = Object.assign(new Error(`${code}: injected stat failure`), {
|
|
code,
|
|
});
|
|
statFailures.set(path, cause);
|
|
const session = {
|
|
close: vi.fn(async () => ({ ok: true as const, value: null })),
|
|
open: vi.fn(),
|
|
openBundleResources: vi.fn(),
|
|
execute: vi.fn(),
|
|
} as unknown as BinarySession;
|
|
const dependencies: DirectAnalysisDependencies = {
|
|
readConfiguration: () => parseConfig({}),
|
|
createBinarySession: () => session,
|
|
createNativeDispatchBinarySession: () => session,
|
|
createArtifactBinarySession: () => session,
|
|
createNativeBinarySession: () => session,
|
|
createManagedBinarySession: () => session,
|
|
};
|
|
|
|
const output = await runProviderAnalysis(
|
|
dependencies,
|
|
path,
|
|
"inspect_keyed_archive",
|
|
{ path: "Resources/Archive.plist" },
|
|
);
|
|
|
|
expect(output).toMatchObject({
|
|
code: projectedCode,
|
|
details: {
|
|
path,
|
|
reason: expect.stringContaining(cause.message),
|
|
...(systemCode === undefined ? {} : { system_code: systemCode }),
|
|
},
|
|
});
|
|
expect(session.open).not.toHaveBeenCalled();
|
|
expect(session.openBundleResources).not.toHaveBeenCalled();
|
|
expect(session.execute).not.toHaveBeenCalled();
|
|
expect(session.close).toHaveBeenCalledOnce();
|
|
},
|
|
);
|
|
|
|
it("rethrows an unclassified stat failure instead of treating it as a file", async () => {
|
|
const path = "/tmp/rea-stat-failure-opaque.app";
|
|
const cause = new Error("opaque filesystem failure");
|
|
statFailures.set(path, cause);
|
|
const session = {
|
|
close: vi.fn(async () => ({ ok: true as const, value: null })),
|
|
open: vi.fn(),
|
|
openBundleResources: vi.fn(),
|
|
execute: vi.fn(),
|
|
} as unknown as BinarySession;
|
|
const dependencies: DirectAnalysisDependencies = {
|
|
readConfiguration: () => parseConfig({}),
|
|
createBinarySession: () => session,
|
|
createNativeDispatchBinarySession: () => session,
|
|
createArtifactBinarySession: () => session,
|
|
createNativeBinarySession: () => session,
|
|
createManagedBinarySession: () => session,
|
|
};
|
|
|
|
await expect(
|
|
runProviderAnalysis(dependencies, path, "inspect_keyed_archive", {
|
|
path: "Resources/Archive.plist",
|
|
}),
|
|
).rejects.toBe(cause);
|
|
expect(session.open).not.toHaveBeenCalled();
|
|
expect(session.openBundleResources).not.toHaveBeenCalled();
|
|
expect(session.execute).not.toHaveBeenCalled();
|
|
expect(session.close).toHaveBeenCalledOnce();
|
|
});
|