
























@@ -0,0 +1,92 @@
1+import { afterEach, describe, expect, it, vi } from "vitest";
2+import { drainPendingToolTasks } from "./pending-tool-task-drain.js";
3+4+function deferredTask() {
5+let resolve!: () => void;
6+let reject!: (error: Error) => void;
7+const promise = new Promise<void>((res, rej) => {
8+resolve = res;
9+reject = rej;
10+});
11+return { promise, resolve, reject };
12+}
13+14+async function flushPromises() {
15+await Promise.resolve();
16+await Promise.resolve();
17+}
18+19+describe("drainPendingToolTasks", () => {
20+afterEach(() => {
21+vi.useRealTimers();
22+});
23+24+it("settles immediately when there are no pending tasks", async () => {
25+await expect(drainPendingToolTasks({ tasks: new Set() })).resolves.toEqual({
26+kind: "settled",
27+});
28+});
29+30+it("waits for all pending tasks to settle", async () => {
31+const first = deferredTask();
32+const second = deferredTask();
33+const tasks = new Set([first.promise, second.promise]);
34+35+const drain = drainPendingToolTasks({ tasks, idleTimeoutMs: 1_000 });
36+first.resolve();
37+await flushPromises();
38+expect(tasks.size).toBe(1);
39+second.resolve();
40+41+await expect(drain).resolves.toEqual({ kind: "settled" });
42+expect(tasks.size).toBe(0);
43+});
44+45+it("resets the idle timeout after each completed task", async () => {
46+vi.useFakeTimers();
47+const first = deferredTask();
48+const second = deferredTask();
49+const onTimeout = vi.fn();
50+const tasks = new Set([first.promise, second.promise]);
51+52+const drain = drainPendingToolTasks({ tasks, idleTimeoutMs: 100, onTimeout });
53+54+await vi.advanceTimersByTimeAsync(80);
55+first.resolve();
56+await Promise.resolve();
57+await vi.advanceTimersByTimeAsync(80);
58+expect(onTimeout).not.toHaveBeenCalled();
59+60+second.resolve();
61+await expect(drain).resolves.toEqual({ kind: "settled" });
62+expect(onTimeout).not.toHaveBeenCalled();
63+});
64+65+it("returns timeout when no pending task settles before the idle window", async () => {
66+vi.useFakeTimers();
67+const stuck = deferredTask();
68+const onTimeout = vi.fn();
69+const tasks = new Set([stuck.promise]);
70+71+const drain = drainPendingToolTasks({ tasks, idleTimeoutMs: 100, onTimeout });
72+await vi.advanceTimersByTimeAsync(100);
73+74+await expect(drain).resolves.toEqual({ kind: "timeout", remaining: 1 });
75+expect(onTimeout).toHaveBeenCalledWith(expect.stringContaining("1 task(s) still pending"));
76+expect(tasks.size).toBe(1);
77+});
78+79+it("treats rejected tasks as drained progress", async () => {
80+const failed = deferredTask();
81+const later = deferredTask();
82+const tasks = new Set([failed.promise, later.promise]);
83+84+const drain = drainPendingToolTasks({ tasks, idleTimeoutMs: 1_000 });
85+failed.reject(new Error("send failed"));
86+await flushPromises();
87+expect(tasks.size).toBe(1);
88+later.resolve();
89+90+await expect(drain).resolves.toEqual({ kind: "settled" });
91+});
92+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。