import { HttpClient } from '../http-client';
import { fetchMock } from '../../../test/fetch-mock';

describe('HttpClient', () => {
  beforeAll(() => {
    vi.useFakeTimers();
    fetchMock.enableMocks();
  });

  afterEach(() => {
    fetchMock.resetMocks();
  });

  it('should throw an HttpError on non-OK responses', async () => {
    const client = new HttpClient();

    fetchMock.mockResponse('Not Found', { status: 404, statusText: 'Not Found' });

    await expect(client.fetch('/', {})).rejects.toThrowError(/404: Not Found/);

    // 404 is non-retryable
    expect(fetch).toHaveBeenCalledOnce();
  });

  it('should retry 500 errors', async () => {
    const client = new HttpClient();

    fetchMock.mockResponses(
      ['Internal Server Error', { status: 500, statusText: 'Internal Server Error' }],
      ['OK', { status: 200, statusText: 'OK' }],
    );

    const promise = expect(client.fetch('/', {})).resolves.toMatchObject({ status: 200, statusText: 'OK' });
    await vi.runAllTimersAsync();
    await promise;

    expect(fetch).toHaveBeenCalledTimes(2);
  });

  it('should not retry AbortError', async () => {
    const client = new HttpClient();

    fetchMock.mockResponse('OK', { status: 200, statusText: 'OK' });

    const abortController = new AbortController();

    const promise = expect(client.fetch('/', { signal: abortController.signal })).rejects.toThrowError(
      'The operation was aborted.',
    );

    abortController.abort();

    await vi.runAllTimersAsync();
    await promise;

    expect(fetch).toHaveBeenCalledOnce();
  });
});