mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 03:11:10 +00:00
* ACP: sanitize terminal tool titles Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com> * Config: refresh config baseline and stabilize restart pid test --------- Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
176 lines
5.7 KiB
TypeScript
176 lines
5.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const spawnSyncMock = vi.hoisted(() => vi.fn());
|
|
const resolveLsofCommandSyncMock = vi.hoisted(() => vi.fn());
|
|
const resolveGatewayPortMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("node:child_process", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("node:child_process")>();
|
|
return {
|
|
...actual,
|
|
spawnSync: (...args: Parameters<typeof actual.spawnSync>) => spawnSyncMock(...args),
|
|
};
|
|
});
|
|
|
|
vi.mock("./ports-lsof.js", () => ({
|
|
resolveLsofCommandSync: (...args: unknown[]) => resolveLsofCommandSyncMock(...args),
|
|
}));
|
|
|
|
vi.mock("../config/paths.js", () => ({
|
|
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
|
|
}));
|
|
|
|
let __testing: typeof import("./restart-stale-pids.js").__testing;
|
|
let cleanStaleGatewayProcessesSync: typeof import("./restart-stale-pids.js").cleanStaleGatewayProcessesSync;
|
|
let findGatewayPidsOnPortSync: typeof import("./restart-stale-pids.js").findGatewayPidsOnPortSync;
|
|
|
|
let currentTimeMs = 0;
|
|
|
|
beforeEach(async () => {
|
|
vi.resetModules();
|
|
vi.doMock("node:child_process", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("node:child_process")>();
|
|
return {
|
|
...actual,
|
|
spawnSync: (...args: Parameters<typeof actual.spawnSync>) => spawnSyncMock(...args),
|
|
};
|
|
});
|
|
vi.doMock("./ports-lsof.js", () => ({
|
|
resolveLsofCommandSync: (...args: unknown[]) => resolveLsofCommandSyncMock(...args),
|
|
}));
|
|
vi.doMock("../config/paths.js", () => ({
|
|
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
|
|
}));
|
|
({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } =
|
|
await import("./restart-stale-pids.js"));
|
|
spawnSyncMock.mockReset();
|
|
resolveLsofCommandSyncMock.mockReset();
|
|
resolveGatewayPortMock.mockReset();
|
|
|
|
currentTimeMs = 0;
|
|
resolveLsofCommandSyncMock.mockReturnValue("/usr/sbin/lsof");
|
|
resolveGatewayPortMock.mockReturnValue(18789);
|
|
__testing.setSleepSyncOverride((ms) => {
|
|
currentTimeMs += ms;
|
|
});
|
|
__testing.setDateNowOverride(() => currentTimeMs);
|
|
});
|
|
|
|
afterEach(() => {
|
|
__testing.setSleepSyncOverride(null);
|
|
__testing.setDateNowOverride(null);
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe.runIf(process.platform !== "win32")("findGatewayPidsOnPortSync", () => {
|
|
it("parses lsof output and filters non-openclaw/current processes", () => {
|
|
const gatewayPidA = process.pid + 1000;
|
|
const gatewayPidB = process.pid + 2000;
|
|
const foreignPid = process.pid + 3000;
|
|
spawnSyncMock.mockReturnValue({
|
|
error: undefined,
|
|
status: 0,
|
|
stdout: [
|
|
`p${process.pid}`,
|
|
"copenclaw",
|
|
`p${gatewayPidA}`,
|
|
"copenclaw-gateway",
|
|
`p${foreignPid}`,
|
|
"cnode",
|
|
`p${gatewayPidB}`,
|
|
"cOpenClaw",
|
|
].join("\n"),
|
|
});
|
|
|
|
const pids = findGatewayPidsOnPortSync(18789);
|
|
|
|
expect(pids).toEqual([gatewayPidA, gatewayPidB]);
|
|
expect(spawnSyncMock).toHaveBeenCalledWith(
|
|
"/usr/sbin/lsof",
|
|
["-nP", "-iTCP:18789", "-sTCP:LISTEN", "-Fpc"],
|
|
expect.objectContaining({ encoding: "utf8", timeout: 2000 }),
|
|
);
|
|
});
|
|
|
|
it("returns empty when lsof fails", () => {
|
|
spawnSyncMock.mockReturnValue({
|
|
error: undefined,
|
|
status: 1,
|
|
stdout: "",
|
|
stderr: "lsof failed",
|
|
});
|
|
|
|
expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe.runIf(process.platform !== "win32")("cleanStaleGatewayProcessesSync", () => {
|
|
it("kills stale gateway pids discovered on the gateway port", () => {
|
|
const stalePidA = process.pid + 1000;
|
|
const stalePidB = process.pid + 2000;
|
|
spawnSyncMock
|
|
.mockReturnValueOnce({
|
|
error: undefined,
|
|
status: 0,
|
|
stdout: [`p${stalePidA}`, "copenclaw", `p${stalePidB}`, "copenclaw-gateway"].join("\n"),
|
|
})
|
|
.mockReturnValue({
|
|
error: undefined,
|
|
status: 1,
|
|
stdout: "",
|
|
});
|
|
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
|
|
|
|
const killed = cleanStaleGatewayProcessesSync();
|
|
|
|
expect(killed).toEqual([stalePidA, stalePidB]);
|
|
expect(resolveGatewayPortMock).toHaveBeenCalledWith(undefined, process.env);
|
|
expect(killSpy).toHaveBeenCalledWith(stalePidA, "SIGTERM");
|
|
expect(killSpy).toHaveBeenCalledWith(stalePidB, "SIGTERM");
|
|
expect(killSpy).toHaveBeenCalledWith(stalePidA, "SIGKILL");
|
|
expect(killSpy).toHaveBeenCalledWith(stalePidB, "SIGKILL");
|
|
});
|
|
|
|
it("uses explicit port override when provided", () => {
|
|
const stalePid = process.pid + 1000;
|
|
spawnSyncMock
|
|
.mockReturnValueOnce({
|
|
error: undefined,
|
|
status: 0,
|
|
stdout: [`p${stalePid}`, "copenclaw"].join("\n"),
|
|
})
|
|
.mockReturnValue({
|
|
error: undefined,
|
|
status: 1,
|
|
stdout: "",
|
|
});
|
|
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
|
|
|
|
const killed = cleanStaleGatewayProcessesSync(19999);
|
|
|
|
expect(killed).toEqual([stalePid]);
|
|
expect(resolveGatewayPortMock).not.toHaveBeenCalled();
|
|
expect(spawnSyncMock).toHaveBeenCalledWith(
|
|
"/usr/sbin/lsof",
|
|
["-nP", "-iTCP:19999", "-sTCP:LISTEN", "-Fpc"],
|
|
expect.objectContaining({ encoding: "utf8", timeout: 2000 }),
|
|
);
|
|
expect(killSpy).toHaveBeenCalledWith(stalePid, "SIGTERM");
|
|
expect(killSpy).toHaveBeenCalledWith(stalePid, "SIGKILL");
|
|
});
|
|
|
|
it("returns empty when no stale listeners are found", () => {
|
|
spawnSyncMock.mockReturnValue({
|
|
error: undefined,
|
|
status: 0,
|
|
stdout: "",
|
|
});
|
|
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
|
|
|
|
const killed = cleanStaleGatewayProcessesSync();
|
|
|
|
expect(killed).toEqual([]);
|
|
expect(killSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|