Files
2026-09-16 01:47:28 -07:00

60 lines
2.5 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { createFetchHttpClient } from '../src/http/types.js';
type FetchImpl = typeof fetch;
function okResponse(body: string, status = 200): Response {
return new Response(body, { status, headers: { 'content-type': 'application/json' } });
}
describe('createFetchHttpClient', () => {
it('rejects a non-positive timeout at construction', () => {
expect(() => createFetchHttpClient({ timeoutMs: 0 })).toThrow(/timeoutMs/);
});
it('attaches an AbortSignal deadline to every request and parses JSON bodies', async () => {
const fetchImpl = vi.fn<FetchImpl>().mockResolvedValue(okResponse('{"arenaSessionId":"s1"}'));
const client = createFetchHttpClient({ timeoutMs: 30_000, fetchImpl });
const res = await client.request({ method: 'POST', url: 'https://arena.invalid/v1/login', body: { a: 1 } });
expect(res).toEqual({ status: 200, body: { arenaSessionId: 's1' } });
const init = fetchImpl.mock.calls[0]?.[1];
expect(init?.signal).toBeInstanceOf(AbortSignal);
expect(init?.signal?.aborted).toBe(false);
expect(init?.method).toBe('POST');
expect(init?.body).toBe('{"a":1}');
expect((init?.headers as Record<string, string>)['Content-Type']).toBe('application/json');
});
it('rejects with a TimeoutError when the upstream does not answer within timeoutMs', async () => {
// A fetch that only settles when its signal aborts — i.e. a hung socket.
const fetchImpl: FetchImpl = (_url, init) =>
new Promise<Response>((_resolve, reject) => {
init?.signal?.addEventListener('abort', () => reject(init.signal?.reason as Error));
});
const client = createFetchHttpClient({ timeoutMs: 20, fetchImpl });
await expect(client.request({ method: 'GET', url: 'https://arena.invalid/v1/items' })).rejects.toMatchObject({
name: 'TimeoutError',
});
});
it('returns raw text when the body is not JSON and null when empty', async () => {
const fetchImpl = vi
.fn<FetchImpl>()
.mockResolvedValueOnce(okResponse('not json', 502))
.mockResolvedValueOnce(new Response(null, { status: 204 }));
const client = createFetchHttpClient({ timeoutMs: 1000, fetchImpl });
expect(await client.request({ method: 'GET', url: 'https://arena.invalid/x' })).toEqual({
status: 502,
body: 'not json',
});
expect(await client.request({ method: 'GET', url: 'https://arena.invalid/y' })).toEqual({
status: 204,
body: null,
});
});
});