import { EventEmitter } from "node:events"; import { PassThrough } from "node:stream"; import { afterEach, expect, it, vi } from "vitest"; import { resolveXcrunTool, type NativeCommandLauncher, } from "../../../../src/native/CommandRunner.js"; const realpathState = vi.hoisted(() => ({ resolve: undefined as ((path: string) => Promise) | undefined, })); vi.mock("node:fs/promises", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, realpath: (path: string): Promise => { if (realpathState.resolve === undefined) throw new Error("realpath fixture was not configured"); return realpathState.resolve(path); }, }; }); const launcher: NativeCommandLauncher = async (options) => { const child = Object.assign(new EventEmitter(), { stdout: new PassThrough(), stderr: new PassThrough(), exitCode: null as number | null, signalCode: null as NodeJS.Signals | null, kill: () => true, }); setImmediate(() => { child.stdout.end("/controlled/native-tool\n"); child.stderr.end(); child.exitCode = 0; child.emit("exit", 0, null); child.emit("close", 0, null); }); return { process: child, ownership: { runId: options.runId, leaderPid: 48_100, processGroupId: 48_100, expectedParentPid: process.pid, }, cleanup: async () => ({ cleaned: true, signaled: false }), }; }; afterEach(() => { realpathState.resolve = undefined; }); it("keeps a realpath I/O failure primary when cancellation is observed before rejection", async () => { let realpathStarted!: () => void; const started = new Promise((resolve) => { realpathStarted = resolve; }); let rejectRealpath!: (cause: unknown) => void; realpathState.resolve = () => new Promise((_resolve, reject) => { rejectRealpath = reject; realpathStarted(); }); const controller = new AbortController(); const cancellationReason = { source: "caller", phase: "tool resolution" }; const pending = resolveXcrunTool("file", {}, controller.signal, launcher); await started; controller.abort(cancellationReason); const ioFailure = Object.assign(new Error("fixture realpath failure"), { code: "EACCES", }); rejectRealpath(ioFailure); const result = await pending; if (result.ok) throw new Error("Expected realpath I/O failure"); expect(result.error).toMatchObject({ reason: "io", cancellation: { reason: cancellationReason }, }); expect(result.error.cause).toBe(ioFailure); }); it.skipIf(process.platform === "win32")( "uses the AbortError cause as the exact tool-hash cancellation fact", async () => { let finishRealpath!: () => void; let realpathStarted!: () => void; const started = new Promise((resolve) => { realpathStarted = resolve; }); realpathState.resolve = () => new Promise((resolve) => { finishRealpath = () => resolve("/dev/zero"); realpathStarted(); }); const controller = new AbortController(); const cancellationReason = { source: "caller", phase: "tool hash" }; const pending = resolveXcrunTool("file", {}, controller.signal, launcher); await started; finishRealpath(); setImmediate(() => controller.abort(cancellationReason)); const result = await pending; if (result.ok) throw new Error("Expected hash cancellation"); expect(result.error).toMatchObject({ reason: "cancelled", cancellation: { reason: cancellationReason }, }); expect(result.error.cause).toMatchObject({ name: "AbortError", code: "ABORT_ERR", cause: cancellationReason, }); }, );