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();
  });
});
