// src/utils/timeout.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { withTimeout } from './timeout';

describe('withTimeout', () => {
  beforeEach(() => {
    // Use fake timers to control setTimeout and clearTimeout
    vi.useFakeTimers();
  });

  afterEach(() => {
    // Restore real timers after each test
    vi.useRealTimers();
  });

  it('should resolve with the original promise value if it resolves before the timeout', async () => {
    const slowPromise = new Promise<string>(resolve => setTimeout(() => resolve('Success'), 100));
    const wrappedPromise = withTimeout(slowPromise, 500);

    // Advance timers just enough for the promise to resolve
    vi.advanceTimersByTime(100);

    // The promise should resolve successfully
    await expect(wrappedPromise).resolves.toBe('Success');
  });

  it('should reject with the original promise reason if it rejects before the timeout', async () => {
    const failingPromise = new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Original error')), 100)
    );
    const wrappedPromise = withTimeout(failingPromise, 500);

    // Advance timers just enough for the promise to reject
    vi.advanceTimersByTime(100);

    // The promise should reject with the original error
    await expect(wrappedPromise).rejects.toThrow('Original error');
  });

  it('should reject with a timeout error if the promise does not resolve in time', async () => {
    const verySlowPromise = new Promise<string>(resolve => setTimeout(() => resolve('Success'), 1000));
    const wrappedPromise = withTimeout(verySlowPromise, 500);

    // Advance timers past the timeout duration but before the promise resolves
    vi.advanceTimersByTime(500);

    // The promise should reject with the specific timeout error
    await expect(wrappedPromise).rejects.toThrow('Operation timed out after 0.5 seconds');
  });
});