141 lines
3.8 KiB
TypeScript
141 lines
3.8 KiB
TypeScript
import { afterEach, expect, test } from "vitest";
|
|
|
|
import { createExecTool } from "./bash-tools.exec";
|
|
import {
|
|
getFinishedSession,
|
|
getSession,
|
|
resetProcessRegistryForTests,
|
|
} from "./bash-process-registry";
|
|
import { killProcessTree } from "./shell-utils";
|
|
|
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
|
afterEach(() => {
|
|
resetProcessRegistryForTests();
|
|
});
|
|
|
|
test("background exec is not killed when tool signal aborts", async () => {
|
|
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
|
|
const abortController = new AbortController();
|
|
|
|
const result = await tool.execute(
|
|
"toolcall",
|
|
{ command: 'node -e "setTimeout(() => {}, 5000)"', background: true },
|
|
abortController.signal,
|
|
);
|
|
|
|
expect(result.details.status).toBe("running");
|
|
const sessionId = (result.details as { sessionId: string }).sessionId;
|
|
|
|
abortController.abort();
|
|
|
|
await sleep(150);
|
|
|
|
const running = getSession(sessionId);
|
|
const finished = getFinishedSession(sessionId);
|
|
|
|
try {
|
|
expect(finished).toBeUndefined();
|
|
expect(running?.exited).toBe(false);
|
|
} finally {
|
|
const pid = running?.pid;
|
|
if (pid) killProcessTree(pid);
|
|
}
|
|
});
|
|
|
|
test("background exec still times out after tool signal abort", async () => {
|
|
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
|
|
const abortController = new AbortController();
|
|
|
|
const result = await tool.execute(
|
|
"toolcall",
|
|
{
|
|
command: 'node -e "setTimeout(() => {}, 5000)"',
|
|
background: true,
|
|
timeout: 0.2,
|
|
},
|
|
abortController.signal,
|
|
);
|
|
|
|
expect(result.details.status).toBe("running");
|
|
const sessionId = (result.details as { sessionId: string }).sessionId;
|
|
|
|
abortController.abort();
|
|
|
|
let finished = getFinishedSession(sessionId);
|
|
const deadline = Date.now() + (process.platform === "win32" ? 10_000 : 2_000);
|
|
while (!finished && Date.now() < deadline) {
|
|
await sleep(20);
|
|
finished = getFinishedSession(sessionId);
|
|
}
|
|
|
|
const running = getSession(sessionId);
|
|
|
|
try {
|
|
expect(finished).toBeTruthy();
|
|
expect(finished?.status).toBe("failed");
|
|
} finally {
|
|
const pid = running?.pid;
|
|
if (pid) killProcessTree(pid);
|
|
}
|
|
});
|
|
|
|
test("yielded background exec is not killed when tool signal aborts", async () => {
|
|
const tool = createExecTool({ allowBackground: true, backgroundMs: 10 });
|
|
const abortController = new AbortController();
|
|
|
|
const result = await tool.execute(
|
|
"toolcall",
|
|
{ command: 'node -e "setTimeout(() => {}, 5000)"', yieldMs: 5 },
|
|
abortController.signal,
|
|
);
|
|
|
|
expect(result.details.status).toBe("running");
|
|
const sessionId = (result.details as { sessionId: string }).sessionId;
|
|
|
|
abortController.abort();
|
|
|
|
await sleep(150);
|
|
|
|
const running = getSession(sessionId);
|
|
const finished = getFinishedSession(sessionId);
|
|
|
|
try {
|
|
expect(finished).toBeUndefined();
|
|
expect(running?.exited).toBe(false);
|
|
} finally {
|
|
const pid = running?.pid;
|
|
if (pid) killProcessTree(pid);
|
|
}
|
|
});
|
|
|
|
test("yielded background exec still times out", async () => {
|
|
const tool = createExecTool({ allowBackground: true, backgroundMs: 10 });
|
|
|
|
const result = await tool.execute("toolcall", {
|
|
command: 'node -e "setTimeout(() => {}, 5000)"',
|
|
yieldMs: 5,
|
|
timeout: 0.2,
|
|
});
|
|
|
|
expect(result.details.status).toBe("running");
|
|
const sessionId = (result.details as { sessionId: string }).sessionId;
|
|
|
|
let finished = getFinishedSession(sessionId);
|
|
const deadline = Date.now() + (process.platform === "win32" ? 10_000 : 2_000);
|
|
while (!finished && Date.now() < deadline) {
|
|
await sleep(20);
|
|
finished = getFinishedSession(sessionId);
|
|
}
|
|
|
|
const running = getSession(sessionId);
|
|
|
|
try {
|
|
expect(finished).toBeTruthy();
|
|
expect(finished?.status).toBe("failed");
|
|
} finally {
|
|
const pid = running?.pid;
|
|
if (pid) killProcessTree(pid);
|
|
}
|
|
});
|