mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:20:42 +00:00
test(process): share supervisor sigkill wait assertions
This commit is contained in:
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
40
src/process/supervisor/adapters/test-support.ts
Normal file
40
src/process/supervisor/adapters/test-support.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user