refactor: dedupe command dispatch and process poll tests
This commit is contained in:
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user