import { describe, it, expect } from 'vitest'; import { createCancelRegistry } from '../cancel-registry.js'; /** * F1 — per-task abort wiring. The registry is the missing link between the Stop * route and the in-flight external run: register an AbortController per task id, * cancel(taskId) aborts its signal, the run's .finally deletes it. Pure (no DB / * child / IO) so the abort + idempotency contract is unit-testable in isolation. */ describe('CancelRegistry (F1 abort wiring)', () => { it('register hands back a fresh controller; cancel aborts its signal', () => { const reg = createCancelRegistry(); const ac = reg.register('t1'); expect(ac.signal.aborted).toBe(false); expect(reg.has('t1')).toBe(true); expect(reg.cancel('t1')).toBe(true); expect(ac.signal.aborted).toBe(true); }); it('cancel on an unknown task returns false (native task / cancel-before-register)', () => { const reg = createCancelRegistry(); expect(reg.has('nope')).toBe(false); expect(reg.cancel('nope')).toBe(false); }); it('double-Stop is idempotent: a second cancel never throws and the signal stays aborted', () => { const reg = createCancelRegistry(); const ac = reg.register('t1'); expect(reg.cancel('t1')).toBe(true); // The run-function has not hit its .finally yet, so the entry is still // present — a rapid second Stop re-aborts (abort() no-ops) without throwing. expect(() => reg.cancel('t1')).not.toThrow(); expect(reg.cancel('t1')).toBe(true); expect(ac.signal.aborted).toBe(true); }); it('cancel after delete returns false (cancel-after-natural-exit is safe)', () => { const reg = createCancelRegistry(); reg.register('t1'); reg.delete('t1'); expect(reg.has('t1')).toBe(false); expect(reg.cancel('t1')).toBe(false); }); it('delete of an unknown id is a no-op (never throws)', () => { const reg = createCancelRegistry(); expect(() => reg.delete('ghost')).not.toThrow(); }); });