mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 20:21:45 +00:00
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
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 });
|
|
|
|
const second = updateGatewayReadyOutputState(first, "ghijkl", 8);
|
|
expect(second).toEqual({ tail: "efghijkl", readySeen: false });
|
|
expect(second.tail).toHaveLength(8);
|
|
expect(GATEWAY_READY_OUTPUT_MAX_CHARS).toBeGreaterThan(1024);
|
|
});
|
|
|
|
it("keeps readiness after a coalesced noisy chunk truncates the marker", () => {
|
|
const state = updateGatewayReadyOutputState(
|
|
{ tail: "", readySeen: false },
|
|
`[gateway] ready\n${"x".repeat(10_000)}`,
|
|
64,
|
|
);
|
|
|
|
expect(state.readySeen).toBe(true);
|
|
expect(state.tail).toHaveLength(64);
|
|
expect(state.tail).not.toContain("[gateway] ready");
|
|
});
|
|
|
|
it("recognizes readiness split across the existing tail and new chunk", () => {
|
|
const state = updateGatewayReadyOutputState(
|
|
{ tail: "[gateway] rea", readySeen: false },
|
|
"dy\n",
|
|
64,
|
|
);
|
|
|
|
expect(state.readySeen).toBe(true);
|
|
expect(state.tail).toBe("[gateway] ready\n");
|
|
});
|
|
|
|
it("preserves previous readiness once seen", () => {
|
|
const state = updateGatewayReadyOutputState({ tail: "old", readySeen: true }, "new output", 8);
|
|
|
|
expect(state.readySeen).toBe(true);
|
|
expect(state.tail).toBe("w output");
|
|
});
|
|
});
|