upload current sources
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
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,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user