import { describe, it, expect } from 'vitest';
import { withTimeout, TimeoutError } from '../src/util/with-timeout.js';

describe('withTimeout', () => {
  it('resolves when the operation finishes in time', async () => {
    const out = await withTimeout(async () => 'ok', 100, 'fast op');
    expect(out).toBe('ok');
  });

  it('rejects with TimeoutError when the operation hangs', async () => {
    const start = Date.now();
    await expect(
      withTimeout(() => new Promise<never>(() => { /* never resolves */ }), 50, 'hang op'),
    ).rejects.toBeInstanceOf(TimeoutError);
    // Returned promptly, not after the (never) inner promise.
    expect(Date.now() - start).toBeLessThan(1000);
  });

  it('aborts the signal on timeout (fetch-based providers can cancel)', async () => {
    let aborted = false;
    await expect(
      withTimeout((signal) => {
        signal.addEventListener('abort', () => { aborted = true; });
        return new Promise<never>(() => { /* never */ });
      }, 50, 'abort op'),
    ).rejects.toBeInstanceOf(TimeoutError);
    expect(aborted).toBe(true);
  });

  it('propagates the operation error (not a timeout) when it rejects fast', async () => {
    await expect(
      withTimeout(async () => { throw new Error('boom'); }, 100, 'err op'),
    ).rejects.toThrow('boom');
  });
});
