fix(process): wait for windows exit code settlement

This commit is contained in:
Ayaan Zaidi
2026-03-28 16:37:29 +05:30
parent bef4fa55f5
commit c3c1f9df54
2 changed files with 34 additions and 1 deletions

View File

@@ -330,7 +330,7 @@ export async function runCommandWithTimeout(
child.stderr?.destroy();
}, 250);
});
child.on("close", (code, signal) => {
const resolveFromClose = (code: number | null, signal: NodeJS.Signals | null) => {
if (settled) {
return;
}
@@ -363,6 +363,19 @@ export async function runCommandWithTimeout(
termination,
noOutputTimedOut,
});
};
child.on("close", (code, signal) => {
if (
childExitState == null &&
code == null &&
signal == null &&
child.exitCode == null &&
child.signalCode == null
) {
setImmediate(() => resolveFromClose(code, signal));
return;
}
resolveFromClose(code, signal);
});
});
}

View File

@@ -31,6 +31,7 @@ function createMockChild(params?: {
closeCode?: number | null;
closeSignal?: NodeJS.Signals | null;
exitCode?: number | null;
exitCodeAfterClose?: number | null;
signal?: NodeJS.Signals | null;
}): MockChild {
const child = new EventEmitter() as MockChild;
@@ -47,6 +48,11 @@ function createMockChild(params?: {
child.killed = false;
queueMicrotask(() => {
child.emit("close", params?.closeCode ?? 0, params?.closeSignal ?? params?.signal ?? null);
if (params?.exitCodeAfterClose !== undefined) {
setImmediate(() => {
child.exitCode = params.exitCodeAfterClose ?? null;
});
}
});
return child;
}
@@ -117,6 +123,20 @@ describe("windows command wrapper behavior", () => {
}
});
it("waits one tick when Windows close reports null before exitCode settles", async () => {
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
const child = createMockChild({ closeCode: null, exitCode: null, exitCodeAfterClose: 0 });
spawnMock.mockImplementation(() => child);
try {
const result = await runCommandWithTimeout(["npm", "--version"], { timeoutMs: 1000 });
expect(result.code).toBe(0);
} finally {
platformSpy.mockRestore();
}
});
it("uses cmd.exe wrapper with windowsVerbatimArguments in runExec for .cmd shims", async () => {
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
const expectedComSpec = process.env.ComSpec ?? "cmd.exe";