api-client.test.ts
1 import test from 'node:test' 2 import assert from 'node:assert/strict' 3 4 import { api } from './api-client' 5 6 const originalFetch = global.fetch 7 8 test.afterEach(() => { 9 global.fetch = originalFetch 10 }) 11 12 test('dedupes concurrent GET requests for the same path', async () => { 13 let calls = 0 14 global.fetch = (async () => { 15 calls += 1 16 await new Promise((resolve) => setTimeout(resolve, 25)) 17 return new Response(JSON.stringify({ ok: true }), { 18 headers: { 'content-type': 'application/json' }, 19 status: 200, 20 }) 21 }) as unknown as typeof fetch 22 23 const [first, second] = await Promise.all([ 24 api<{ ok: boolean }>('GET', '/dedupe-check'), 25 api<{ ok: boolean }>('GET', '/dedupe-check'), 26 ]) 27 28 assert.deepEqual(first, { ok: true }) 29 assert.deepEqual(second, { ok: true }) 30 assert.equal(calls, 1) 31 }) 32 33 test('does not dedupe non-GET requests', async () => { 34 let calls = 0 35 global.fetch = (async () => { 36 calls += 1 37 return new Response(JSON.stringify({ ok: true }), { 38 headers: { 'content-type': 'application/json' }, 39 status: 200, 40 }) 41 }) as unknown as typeof fetch 42 43 await Promise.all([ 44 api<{ ok: boolean }>('POST', '/dedupe-check', { hello: 'one' }), 45 api<{ ok: boolean }>('POST', '/dedupe-check', { hello: 'two' }), 46 ]) 47 48 assert.equal(calls, 2) 49 }) 50 51 test('retries GET requests that fail with TimeoutError', async () => { 52 let calls = 0 53 global.fetch = (async () => { 54 calls += 1 55 if (calls === 1) { 56 const error = new Error('Request timed out after 12000ms') 57 error.name = 'TimeoutError' 58 throw error 59 } 60 return new Response(JSON.stringify({ ok: true }), { 61 headers: { 'content-type': 'application/json' }, 62 status: 200, 63 }) 64 }) as unknown as typeof fetch 65 66 const result = await api<{ ok: boolean }>('GET', '/timeout-retry') 67 68 assert.deepEqual(result, { ok: true }) 69 assert.equal(calls, 2) 70 })