From 0a6157e13a2bea111b957f15517b68e9bed40c5a Mon Sep 17 00:00:00 2001 From: Nadia Rodionova <273119990+sosidudku1@users.noreply.github.com> Date: Thu, 12 Nov 2026 03:39:15 +0300 Subject: [PATCH] test(fusion): pin the spend cap reason when the cap also aborts the signal The pool asks the cap before the stop signal. A cap that aborts the same signal must still leave rows naming the cap, not a stop. --- src/tools/fusion/worker-runner.test.ts | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/tools/fusion/worker-runner.test.ts b/src/tools/fusion/worker-runner.test.ts index f29fe4c85..283ef97fc 100644 --- a/src/tools/fusion/worker-runner.test.ts +++ b/src/tools/fusion/worker-runner.test.ts @@ -111,6 +111,34 @@ describe("runWorkerTasks", () => { expect(results[2]!.error).toBe("not started: the turn was stopped"); }); + it("names the spend cap, not a stop, when the cap also aborts the turn's signal (ATO-1420)", async () => { + // The real wiring (fusion-delegate): reaching the cap aborts the same + // signal the pool reads. Both say "stop" by the next claim, and the + // row must name the cap. With the signal read first, these rows read + // "not started: the turn was stopped" and this test fails. + const stop = new AbortController(); + const capMessage = "the spend cap for this turn is reached"; + let capReached = false; + const { deps, calls } = harness(async () => { + capReached = true; + stop.abort(new Error(capMessage)); + return turnResult({ reason: "cancelled" }); + }); + const results = await runWorkerTasks(deps, { + ...BASE, + tasks: tasks(3), + maxWorkers: 1, + signal: stop.signal, + shouldStart: () => (capReached ? capMessage : undefined), + }); + expect(stop.signal.aborted).toBe(true); + expect(calls).toHaveLength(1); + expect(results.map((r) => r.status)).toEqual(["cancelled", "cancelled", "cancelled"]); + expect(results[1]!.error).toBe(`not started: ${capMessage}`); + expect(results[2]!.error).toBe(`not started: ${capMessage}`); + expect(results[1]!.error).not.toMatch(/the turn was stopped/); + }); + it("pins each worker turn to the local leg with the fusion origin and the worker filter", async () => { const { deps, calls } = harness(async ({ options }) => { options.eventHook?.({