test(process): share supervisor sigkill wait assertions

This commit is contained in:
Vincent Koc
2026-04-12 04:52:29 +01:00
parent ccf29464db
commit e1e20c424b
3 changed files with 74 additions and 44 deletions

View File

@@ -2,6 +2,10 @@ import type { ChildProcess } from "node:child_process";
import { EventEmitter } from "node:events";
import { PassThrough } from "node:stream";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
expectRealExitWinsOverSigkillFallback,
expectWaitStaysPendingUntilSigkillFallback,
} from "./test-support.js";
const { spawnWithFallbackMock, killProcessTreeMock } = vi.hoisted(() => ({
spawnWithFallbackMock: vi.fn(),
@@ -135,20 +139,9 @@ describe("createChildAdapter", () => {
vi.useFakeTimers();
const { adapter } = await createAdapterHarness({ pid: 4567 });
const waitPromise = adapter.wait();
const settled = vi.fn();
void waitPromise.then(() => settled());
adapter.kill();
await Promise.resolve();
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(3999);
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
await expect(waitPromise).resolves.toEqual({ code: null, signal: "SIGKILL" });
await expectWaitStaysPendingUntilSigkillFallback(adapter.wait(), () => {
adapter.kill();
});
});
it("prefers real child close over the SIGKILL fallback settle", async () => {
@@ -166,14 +159,16 @@ describe("createChildAdapter", () => {
return { ...stub, adapter };
})();
const waitPromise = adapter.wait();
adapter.kill();
emitClose(0, "SIGKILL");
await expect(waitPromise).resolves.toEqual({ code: 0, signal: "SIGKILL" });
await vi.advanceTimersByTimeAsync(4_001);
await expect(adapter.wait()).resolves.toEqual({ code: 0, signal: "SIGKILL" });
await expectRealExitWinsOverSigkillFallback({
waitPromise: adapter.wait(),
triggerKill: () => {
adapter.kill();
},
emitExit: () => {
emitClose(0, "SIGKILL");
},
expected: { code: 0, signal: "SIGKILL" },
});
expect(killMock).toHaveBeenCalledWith("SIGKILL");
});

View File

@@ -1,4 +1,8 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
expectRealExitWinsOverSigkillFallback,
expectWaitStaysPendingUntilSigkillFallback,
} from "./test-support.js";
const { spawnMock, ptyKillMock, killProcessTreeMock } = vi.hoisted(() => ({
spawnMock: vi.fn(),
@@ -98,20 +102,9 @@ describe("createPtyAdapter", () => {
args: ["-lc", "sleep 10"],
});
const waitPromise = adapter.wait();
const settled = vi.fn();
void waitPromise.then(() => settled());
adapter.kill();
await Promise.resolve();
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(3999);
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
await expect(waitPromise).resolves.toEqual({ code: null, signal: "SIGKILL" });
await expectWaitStaysPendingUntilSigkillFallback(adapter.wait(), () => {
adapter.kill();
});
});
it("prefers real PTY exit over SIGKILL fallback settle", async () => {
@@ -124,14 +117,16 @@ describe("createPtyAdapter", () => {
args: ["-lc", "sleep 10"],
});
const waitPromise = adapter.wait();
adapter.kill();
stub.emitExit({ exitCode: 0, signal: 9 });
await expect(waitPromise).resolves.toEqual({ code: 0, signal: 9 });
await vi.advanceTimersByTimeAsync(4_001);
await expect(adapter.wait()).resolves.toEqual({ code: 0, signal: 9 });
await expectRealExitWinsOverSigkillFallback({
waitPromise: adapter.wait(),
triggerKill: () => {
adapter.kill();
},
emitExit: () => {
stub.emitExit({ exitCode: 0, signal: 9 });
},
expected: { code: 0, signal: 9 },
});
});
it("resolves wait when exit fires before wait is called", async () => {

View File

@@ -0,0 +1,40 @@
import { expect, vi } from "vitest";
type WaitResult = {
code: number | null;
signal: number | NodeJS.Signals | null;
};
export async function expectWaitStaysPendingUntilSigkillFallback(
waitPromise: Promise<WaitResult>,
triggerKill: () => void,
): Promise<void> {
const settled = vi.fn();
void waitPromise.then(() => settled());
triggerKill();
await Promise.resolve();
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(3999);
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
await expect(waitPromise).resolves.toEqual({ code: null, signal: "SIGKILL" });
}
export async function expectRealExitWinsOverSigkillFallback(params: {
waitPromise: Promise<WaitResult>;
triggerKill: () => void;
emitExit: () => void;
expected: WaitResult;
}): Promise<void> {
params.triggerKill();
params.emitExit();
await expect(params.waitPromise).resolves.toEqual(params.expected);
await vi.advanceTimersByTimeAsync(4_001);
await expect(params.waitPromise).resolves.toEqual(params.expected);
}