mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 07:54:07 +00:00
fix(test): detect signaled memory fd gateway exits
This commit is contained in:
@@ -342,7 +342,11 @@ function sampleFds({ label, pid, workspaceRealPath }) {
|
||||
return sample;
|
||||
}
|
||||
|
||||
async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
|
||||
export function hasChildExited(child) {
|
||||
return child.exitCode !== null || child.signalCode !== null;
|
||||
}
|
||||
|
||||
export async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
|
||||
const startedAt = Date.now();
|
||||
let outputState = { tail: "", readySeen: false };
|
||||
const append = (chunk) => {
|
||||
@@ -357,7 +361,7 @@ async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
|
||||
if (outputState.readySeen && findGatewayPid(port)) {
|
||||
return;
|
||||
}
|
||||
if (child.exitCode !== null) {
|
||||
if (hasChildExited(child)) {
|
||||
throw new Error(`gateway exited before ready; see ${logPath}`);
|
||||
}
|
||||
await sleep(100);
|
||||
@@ -365,26 +369,41 @@ async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
|
||||
throw new Error(`gateway did not become ready within ${timeoutMs}ms; see ${logPath}`);
|
||||
}
|
||||
|
||||
async function stopGateway({ child, port }) {
|
||||
if (child.exitCode === null) {
|
||||
export async function stopGateway({ child, port }) {
|
||||
return stopGatewayWithRuntime({
|
||||
child,
|
||||
port,
|
||||
findGatewayPidFn: findGatewayPid,
|
||||
killProcess: process.kill,
|
||||
});
|
||||
}
|
||||
|
||||
export async function stopGatewayWithRuntime({
|
||||
child,
|
||||
port,
|
||||
findGatewayPidFn,
|
||||
killProcess,
|
||||
listenerSettleDelayMs = 500,
|
||||
}) {
|
||||
if (!hasChildExited(child)) {
|
||||
child.kill("SIGINT");
|
||||
for (let i = 0; i < 50; i += 1) {
|
||||
if (child.exitCode !== null) {
|
||||
if (hasChildExited(child)) {
|
||||
break;
|
||||
}
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
const listenerPid = findGatewayPid(port);
|
||||
const listenerPid = findGatewayPidFn(port);
|
||||
if (listenerPid) {
|
||||
try {
|
||||
process.kill(listenerPid, "SIGTERM");
|
||||
killProcess(listenerPid, "SIGTERM");
|
||||
} catch {}
|
||||
await sleep(500);
|
||||
const stillListening = findGatewayPid(port);
|
||||
await sleep(listenerSettleDelayMs);
|
||||
const stillListening = findGatewayPidFn(port);
|
||||
if (stillListening) {
|
||||
try {
|
||||
process.kill(stillListening, "SIGKILL");
|
||||
killProcess(stillListening, "SIGKILL");
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
GATEWAY_READY_OUTPUT_MAX_CHARS,
|
||||
hasChildExited,
|
||||
stopGatewayWithRuntime,
|
||||
updateGatewayReadyOutputState,
|
||||
waitForGatewayReady,
|
||||
} from "../../scripts/check-memory-fd-repro.mjs";
|
||||
|
||||
describe("check-memory-fd-repro", () => {
|
||||
it("treats signaled gateway children as exited", () => {
|
||||
expect(hasChildExited({ exitCode: null, signalCode: "SIGTERM" })).toBe(true);
|
||||
expect(hasChildExited({ exitCode: 0, signalCode: null })).toBe(true);
|
||||
expect(hasChildExited({ exitCode: null, signalCode: null })).toBe(false);
|
||||
});
|
||||
|
||||
it("fails gateway readiness immediately after signal exits", async () => {
|
||||
const child = {
|
||||
exitCode: null,
|
||||
signalCode: "SIGTERM",
|
||||
stderr: new EventEmitter(),
|
||||
stdout: new EventEmitter(),
|
||||
};
|
||||
|
||||
await expect(
|
||||
waitForGatewayReady({ child, port: 9, logPath: "gateway.log", timeoutMs: 10_000 }),
|
||||
).rejects.toThrow("gateway exited before ready");
|
||||
});
|
||||
|
||||
it("does not signal already exited children during gateway cleanup", async () => {
|
||||
const child = {
|
||||
exitCode: null,
|
||||
kill: vi.fn(),
|
||||
signalCode: "SIGTERM",
|
||||
};
|
||||
const findGatewayPidFn = vi.fn(() => null);
|
||||
const killProcess = vi.fn();
|
||||
|
||||
await expect(
|
||||
stopGatewayWithRuntime({ child, findGatewayPidFn, killProcess, port: 9 }),
|
||||
).resolves.toBeUndefined();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
expect(findGatewayPidFn).toHaveBeenCalledWith(9);
|
||||
expect(killProcess).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("bounds gateway readiness output while keeping newest logs", () => {
|
||||
const first = updateGatewayReadyOutputState({ tail: "abc", readySeen: false }, "def", 8);
|
||||
expect(first).toEqual({ tail: "abcdef", readySeen: false });
|
||||
@@ -39,11 +79,7 @@ describe("check-memory-fd-repro", () => {
|
||||
});
|
||||
|
||||
it("preserves previous readiness once seen", () => {
|
||||
const state = updateGatewayReadyOutputState(
|
||||
{ tail: "old", readySeen: true },
|
||||
"new output",
|
||||
8,
|
||||
);
|
||||
const state = updateGatewayReadyOutputState({ tail: "old", readySeen: true }, "new output", 8);
|
||||
|
||||
expect(state.readySeen).toBe(true);
|
||||
expect(state.tail).toBe("w output");
|
||||
|
||||
Reference in New Issue
Block a user