refactor: dedupe command dispatch and process poll tests

This commit is contained in:
Peter Steinberger
2026-02-18 04:03:42 +00:00
parent adac9cb67f
commit 05b7bd2c22
3 changed files with 217 additions and 363 deletions

View File

@@ -37,24 +37,46 @@ function createBackgroundSession(id: string): ProcessSession {
};
}
function createProcessSessionHarness(sessionId: string) {
const processTool = createProcessTool();
const session = createBackgroundSession(sessionId);
addSession(session);
return { processTool, session };
}
async function pollSession(
processTool: ReturnType<typeof createProcessTool>,
callId: string,
sessionId: string,
timeout?: number | string,
) {
return processTool.execute(callId, {
action: "poll",
sessionId,
...(timeout === undefined ? {} : { timeout }),
});
}
function retryMs(result: Awaited<ReturnType<ReturnType<typeof createProcessTool>["execute"]>>) {
return (result.details as { retryInMs?: number }).retryInMs;
}
function pollStatus(result: Awaited<ReturnType<ReturnType<typeof createProcessTool>["execute"]>>) {
return (result.details as { status?: string }).status;
}
test("process poll waits for completion when timeout is provided", async () => {
vi.useFakeTimers();
try {
const processTool = createProcessTool();
const sessionId = "sess";
const session = createBackgroundSession(sessionId);
addSession(session);
const { processTool, session } = createProcessSessionHarness(sessionId);
setTimeout(() => {
appendOutput(session, "stdout", "done\n");
markExited(session, 0, null, "completed");
}, 10);
const pollPromise = processTool.execute("toolcall", {
action: "poll",
sessionId,
timeout: 2000,
});
const pollPromise = pollSession(processTool, "toolcall", sessionId, 2000);
let resolved = false;
void pollPromise.finally(() => {
@@ -77,20 +99,14 @@ test("process poll waits for completion when timeout is provided", async () => {
test("process poll accepts string timeout values", async () => {
vi.useFakeTimers();
try {
const processTool = createProcessTool();
const sessionId = "sess-2";
const session = createBackgroundSession(sessionId);
addSession(session);
const { processTool, session } = createProcessSessionHarness(sessionId);
setTimeout(() => {
appendOutput(session, "stdout", "done\n");
markExited(session, 0, null, "completed");
}, 10);
const pollPromise = processTool.execute("toolcall", {
action: "poll",
sessionId,
timeout: "2000",
});
const pollPromise = pollSession(processTool, "toolcall", sessionId, "2000");
await vi.advanceTimersByTimeAsync(350);
const poll = await pollPromise;
const details = poll.details as { status?: string; aggregated?: string };
@@ -102,77 +118,39 @@ test("process poll accepts string timeout values", async () => {
});
test("process poll exposes adaptive retryInMs for repeated no-output polls", async () => {
const processTool = createProcessTool();
const sessionId = "sess-retry";
const session = createBackgroundSession(sessionId);
addSession(session);
const { processTool } = createProcessSessionHarness(sessionId);
const poll1 = await processTool.execute("toolcall-1", {
action: "poll",
sessionId,
});
const poll2 = await processTool.execute("toolcall-2", {
action: "poll",
sessionId,
});
const poll3 = await processTool.execute("toolcall-3", {
action: "poll",
sessionId,
});
const poll4 = await processTool.execute("toolcall-4", {
action: "poll",
sessionId,
});
const poll5 = await processTool.execute("toolcall-5", {
action: "poll",
sessionId,
});
const polls = await Promise.all([
pollSession(processTool, "toolcall-1", sessionId),
pollSession(processTool, "toolcall-2", sessionId),
pollSession(processTool, "toolcall-3", sessionId),
pollSession(processTool, "toolcall-4", sessionId),
pollSession(processTool, "toolcall-5", sessionId),
]);
expect((poll1.details as { retryInMs?: number }).retryInMs).toBe(5000);
expect((poll2.details as { retryInMs?: number }).retryInMs).toBe(10000);
expect((poll3.details as { retryInMs?: number }).retryInMs).toBe(30000);
expect((poll4.details as { retryInMs?: number }).retryInMs).toBe(60000);
expect((poll5.details as { retryInMs?: number }).retryInMs).toBe(60000);
expect(polls.map((poll) => retryMs(poll))).toEqual([5000, 10000, 30000, 60000, 60000]);
});
test("process poll resets retryInMs when output appears and clears on completion", async () => {
const processTool = createProcessTool();
const sessionId = "sess-reset";
const session = createBackgroundSession(sessionId);
addSession(session);
const { processTool, session } = createProcessSessionHarness(sessionId);
const poll1 = await processTool.execute("toolcall-1", {
action: "poll",
sessionId,
});
const poll2 = await processTool.execute("toolcall-2", {
action: "poll",
sessionId,
});
expect((poll1.details as { retryInMs?: number }).retryInMs).toBe(5000);
expect((poll2.details as { retryInMs?: number }).retryInMs).toBe(10000);
const poll1 = await pollSession(processTool, "toolcall-1", sessionId);
const poll2 = await pollSession(processTool, "toolcall-2", sessionId);
expect(retryMs(poll1)).toBe(5000);
expect(retryMs(poll2)).toBe(10000);
appendOutput(session, "stdout", "step complete\n");
const pollWithOutput = await processTool.execute("toolcall-output", {
action: "poll",
sessionId,
});
expect((pollWithOutput.details as { retryInMs?: number }).retryInMs).toBe(5000);
const pollWithOutput = await pollSession(processTool, "toolcall-output", sessionId);
expect(retryMs(pollWithOutput)).toBe(5000);
markExited(session, 0, null, "completed");
const pollCompleted = await processTool.execute("toolcall-completed", {
action: "poll",
sessionId,
});
const completedDetails = pollCompleted.details as { status?: string; retryInMs?: number };
expect(completedDetails.status).toBe("completed");
expect(completedDetails.retryInMs).toBeUndefined();
const pollCompleted = await pollSession(processTool, "toolcall-completed", sessionId);
expect(pollStatus(pollCompleted)).toBe("completed");
expect(retryMs(pollCompleted)).toBeUndefined();
const pollFinished = await processTool.execute("toolcall-finished", {
action: "poll",
sessionId,
});
const finishedDetails = pollFinished.details as { status?: string; retryInMs?: number };
expect(finishedDetails.status).toBe("completed");
expect(finishedDetails.retryInMs).toBeUndefined();
const pollFinished = await pollSession(processTool, "toolcall-finished", sessionId);
expect(pollStatus(pollFinished)).toBe("completed");
expect(retryMs(pollFinished)).toBeUndefined();
});