Files
openclaw/src/agents/bash-tools.exec.pty-cleanup.test.ts
Onur cd44a0d01e fix: codex and similar processes keep dying on pty, solved by refactoring process spawning (#14257)
* exec: clean up PTY resources on timeout and exit

* cli: harden resume cleanup and watchdog stalled runs

* cli: productionize PTY and resume reliability paths

* docs: add PTY process supervision architecture plan

* docs: rewrite PTY supervision plan as pre-rewrite baseline

* docs: switch PTY supervision plan to one-go execution

* docs: add one-line root cause to PTY supervision plan

* docs: add OS contracts and test matrix to PTY supervision plan

* docs: define process-supervisor package placement and scope

* docs: tie supervisor plan to existing CI lanes

* docs: place PTY supervisor plan under src/process

* refactor(process): route exec and cli runs through supervisor

* docs(process): refresh PTY supervision plan

* wip

* fix(process): harden supervisor timeout and PTY termination

* fix(process): harden supervisor adapters env and wait handling

* ci: avoid failing formal conformance on comment permissions

* test(ui): fix cron request mock argument typing

* fix(ui): remove leftover conflict marker

* fix: supervise PTY processes (#14257) (openclaw#14257) (thanks @onutc)
2026-02-16 02:32:05 +01:00

74 lines
2.0 KiB
TypeScript

import { afterEach, expect, test, vi } from "vitest";
import { resetProcessRegistryForTests } from "./bash-process-registry";
afterEach(() => {
resetProcessRegistryForTests();
vi.resetModules();
vi.clearAllMocks();
});
test("exec disposes PTY listeners after normal exit", async () => {
const disposeData = vi.fn();
const disposeExit = vi.fn();
vi.doMock("@lydell/node-pty", () => ({
spawn: () => {
return {
pid: 0,
write: vi.fn(),
onData: (listener: (value: string) => void) => {
setTimeout(() => listener("ok"), 0);
return { dispose: disposeData };
},
onExit: (listener: (event: { exitCode: number; signal?: number }) => void) => {
setTimeout(() => listener({ exitCode: 0 }), 0);
return { dispose: disposeExit };
},
kill: vi.fn(),
};
},
}));
const { createExecTool } = await import("./bash-tools.exec");
const tool = createExecTool({ allowBackground: false });
const result = await tool.execute("toolcall", {
command: "echo ok",
pty: true,
});
expect(result.details.status).toBe("completed");
expect(disposeData).toHaveBeenCalledTimes(1);
expect(disposeExit).toHaveBeenCalledTimes(1);
});
test("exec tears down PTY resources on timeout", async () => {
const disposeData = vi.fn();
const disposeExit = vi.fn();
const kill = vi.fn();
vi.doMock("@lydell/node-pty", () => ({
spawn: () => {
return {
pid: 0,
write: vi.fn(),
onData: () => ({ dispose: disposeData }),
onExit: () => ({ dispose: disposeExit }),
kill,
};
},
}));
const { createExecTool } = await import("./bash-tools.exec");
const tool = createExecTool({ allowBackground: false });
await expect(
tool.execute("toolcall", {
command: "sleep 5",
pty: true,
timeout: 0.01,
}),
).rejects.toThrow("Command timed out");
expect(kill).toHaveBeenCalledTimes(1);
expect(disposeData).toHaveBeenCalledTimes(1);
expect(disposeExit).toHaveBeenCalledTimes(1);
});