import { describe, expect, it } from "vitest"; import { raceSignal } from "./pi-runtime.js"; describe("Adversarial Stress Test: raceSignal and Tool Cancellation Races", () => { it("Race 1: in-flight tool aborting right as executeTool resolves in the same microtask", async () => { // Subcase 1A: synchronous abort immediately following resolve() // Event loop detail: Abort event fires synchronously, preempting the queued promise.then() microtask. { const controller = new AbortController(); let resolveTool!: (val: string) => void; const toolPromise = new Promise((res) => { resolveTool = res; }); const raced = raceSignal(toolPromise, controller.signal); resolveTool("success"); controller.abort(new Error("interrupted")); await expect(raced).rejects.toThrow("interrupted"); } // Subcase 1B: microtask drained after resolve() before abort() // Once promise.then() microtask executes, resolve() settles the raced promise and removes the abort listener. { const controller = new AbortController(); let resolveTool!: (val: string) => void; const toolPromise = new Promise((res) => { resolveTool = res; }); const raced = raceSignal(toolPromise, controller.signal); resolveTool("success"); await Promise.resolve(); // drain microtask controller.abort(new Error("too late")); await expect(raced).resolves.toBe("success"); } // Subcase 1C: tool promise already resolved before raceSignal called { const controller = new AbortController(); const alreadyResolved = Promise.resolve("immediate"); const raced = raceSignal(alreadyResolved, controller.signal); await Promise.resolve(); controller.abort(new Error("aborted")); await expect(raced).resolves.toBe("immediate"); } }); it("Race 2: listener cleanup guarantees no memory leak after resolve or abort", async () => { const controller = new AbortController(); // Run 100 resolved calls for (let i = 0; i < 100; i++) { await raceSignal(Promise.resolve(`res-${i}`), controller.signal); } // Run 100 rejected calls (non-abort) for (let i = 0; i < 100; i++) { await expect( raceSignal(Promise.reject(new Error(`err-${i}`)), controller.signal), ).rejects.toThrow(`err-${i}`); } // Now abort controller.abort(new Error("final abort")); // Run 50 calls on already-aborted signal for (let i = 0; i < 50; i++) { await expect(raceSignal(Promise.resolve("late"), controller.signal)).rejects.toThrow( "final abort", ); } }); it("Race 3: rapid back-to-back aborts while multiple tools are queued concurrently", async () => { const NUM_TOOLS = 25; const controllers = Array.from({ length: 5 }, () => new AbortController()); const resolvers: Array<(val: string) => void> = []; const promises = Array.from({ length: NUM_TOOLS }, (_, index) => { const controller = controllers[index % controllers.length]!; const pending = new Promise((resolve) => { resolvers.push(resolve); }); return raceSignal(pending, controller.signal); }); // Rapidly abort controllers while tools are pending controllers.forEach((c, idx) => { c.abort(new Error(`Aborted batch ${idx}`)); }); // Settle all pending promises afterwards (simulating slow tools finishing late) resolvers.forEach((res, idx) => { res(`tool-${idx}-resolved`); }); const results = await Promise.allSettled(promises); expect(results).toHaveLength(NUM_TOOLS); results.forEach((r, idx) => { expect(r.status).toBe("rejected"); if (r.status === "rejected") { const batch = idx % controllers.length; expect((r as PromiseRejectedResult).reason.message).toBe(`Aborted batch ${batch}`); } }); }); it("Race 4: long-running tool cancellation under signal timeout", async () => { const timeoutSignal = AbortSignal.timeout(25); const longRunningTool = new Promise((resolve) => { setTimeout(() => resolve("tool finished after 200ms"), 200); }); const start = Date.now(); await expect(raceSignal(longRunningTool, timeoutSignal)).rejects.toThrow(); const elapsed = Date.now() - start; // Must have rejected around 25ms, well before the 200ms tool completion expect(elapsed).toBeLessThan(100); }); it("Race 5: abort with non-Error string reason propagates exact reason", async () => { const controller = new AbortController(); const pending = new Promise(() => {}); const raced = raceSignal(pending, controller.signal); controller.abort("user_barge_in_detected"); await expect(raced).rejects.toBe("user_barge_in_detected"); }); it("Race 6: high-concurrency fuzz stress test (100 parallel randomized tool races)", async () => { const randomDelays = Array.from({ length: 100 }, () => Math.floor(Math.random() * 20)); const results = await Promise.all( randomDelays.map(async (toolDelay, idx) => { const abortDelay = Math.floor(Math.random() * 20); const controller = new AbortController(); const toolPromise = new Promise((resolve) => { setTimeout(() => resolve(`tool-${idx}-ok`), toolDelay); }); setTimeout(() => controller.abort(new Error(`abort-${idx}`)), abortDelay); try { const res = await raceSignal(toolPromise, controller.signal); return { status: "fulfilled" as const, res, toolDelay, abortDelay }; } catch (err) { return { status: "rejected" as const, err: (err as Error).message, toolDelay, abortDelay, }; } }), ); expect(results).toHaveLength(100); for (const r of results) { if (r.status === "fulfilled") { expect(r.res).toContain("-ok"); // Tool resolved before abort fired (within timing margin) expect(r.toolDelay).toBeLessThanOrEqual(r.abortDelay + 10); } else { expect(r.err).toContain("abort-"); } } }); it("Race 7: underlying tool promise runs uncancelled in background because executeTool has no signal parameter", async () => { let underlyingToolFinished = false; const controller = new AbortController(); const longRunningTool = new Promise((resolve) => { setTimeout(() => { underlyingToolFinished = true; resolve("done"); }, 50); }); const raced = raceSignal(longRunningTool, controller.signal); controller.abort(new Error("user barge-in")); // Raced promise rejects immediately await expect(raced).rejects.toThrow("user barge-in"); expect(underlyingToolFinished).toBe(false); // Wait for the underlying tool timer to elapse await new Promise((r) => setTimeout(r, 60)); // Notice: underlying task still completed in background because raceSignal does not / cannot abort the executor itself expect(underlyingToolFinished).toBe(true); }); });