import assert from 'node:assert/strict'; import { access } from 'node:fs/promises'; import { test } from 'node:test'; import { loadTypescript } from './support/load-typescript.mjs'; const moduleURL = new URL('../src/analytics/Transport.ts', import.meta.url); assert.equal(await access(moduleURL).then(() => true, () => false), true, 'bounded telemetry transport module must exist'); const { createTransport } = await loadTypescript(moduleURL); const flush = () => new Promise(resolve => setImmediate(resolve)); const response = (status, value) => ({ status, json: async () => value }); test('POST sends only serialized JSON and GET returns a valid count', async () => { const sent = []; const transport = createTransport({ fetch: async (path, init) => { sent.push({ path, ...init }); return response(init.method === 'POST' ? 204 : 200, { count: 1234 }); } }); assert.deepEqual(await transport.request('/api/visits', { visitor_id: 'visitor' }), { ok: true }); assert.equal(sent[0].body, '{"visitor_id":"visitor"}'); assert.equal(sent[0].headers['Content-Type'], 'application/json'); assert.equal(sent[0].method, 'POST'); assert.equal(sent[0].credentials, 'same-origin'); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: true, count: 1234 }); assert.equal(sent[1].method, 'GET'); assert.equal(sent[1].body, undefined); assert.equal(sent[1].cache, 'no-store'); }); for (const failure of ['network', 429, 500, 503]) { test(`${failure} retries after 500 and 1500 ms with an identical payload, never a fourth attempt`, async () => { const sent = [], delays = []; const body = { dog_id: 'original' }; const transport = createTransport({ fetch: async (_path, init) => { sent.push(init.body); body.dog_id = 'edited'; if (failure === 'network') throw Error('offline'); return response(failure); }, sleep: async ms => { delays.push(ms); }, }); assert.deepEqual(await transport.request('/api/arrivals', body), { ok: false }); assert.deepEqual(delays, [500, 1500]); assert.deepEqual(sent, ['{"dog_id":"original"}', '{"dog_id":"original"}', '{"dog_id":"original"}']); }); } test('a later successful retry finishes without further attempts', async () => { let calls = 0; const delays = []; const transport = createTransport({ fetch: async () => response(++calls === 1 ? 503 : 204), sleep: async ms => { delays.push(ms); } }); assert.deepEqual(await transport.request('/api/arrivals', { dog_id: 'id' }), { ok: true }); assert.equal(calls, 2); assert.deepEqual(delays, [500]); }); for (const status of [400, 403, 404, 415]) { test(`${status} is not retried`, async () => { let calls = 0; const transport = createTransport({ fetch: async () => { calls++; return response(status); }, sleep: async () => assert.fail('must not retry') }); assert.deepEqual(await transport.request('/api/visits', { visitor_id: 'id' }), { ok: false }); assert.equal(calls, 1); }); } test('the 3000 ms deadline aborts the actual fetch and is cleared after completion', async t => { t.mock.timers.enable({ apis: ['setTimeout'] }); let signal; const transport = createTransport({ fetch: (_path, init) => { signal = init.signal; return new Promise(() => {}); }, retryDelays: [] }); const pending = transport.request('/api/arrivals/count'); await flush(); t.mock.timers.tick(2999); assert.equal(signal.aborted, false); t.mock.timers.tick(1); await flush(); assert.equal(signal.aborted, true); assert.deepEqual(await pending, { ok: false }); let completedSignal; const successful = createTransport({ fetch: async (_path, init) => { completedSignal = init.signal; return response(204); } }); await successful.request('/api/visits', { visitor_id: 'id' }); t.mock.timers.tick(10000); assert.equal(completedSignal.aborted, false, 'completed attempt timeout was removed'); }); test('deadline also covers a stalled JSON body read', async t => { t.mock.timers.enable({ apis: ['setTimeout'] }); let signal; const transport = createTransport({ fetch: async (_path, init) => { signal = init.signal; return { status: 200, json: () => new Promise(() => {}) }; }, retryDelays: [] }); const pending = transport.request('/api/arrivals/count'); await flush(); t.mock.timers.tick(3000); await flush(); assert.deepEqual(await pending, { ok: false }); assert.equal(signal.aborted, true); }); test('caller abort cancels an in-flight request, timeout and further retries', async t => { t.mock.timers.enable({ apis: ['setTimeout'] }); const caller = new AbortController(); let calls = 0, signal; const transport = createTransport({ fetch: (_path, init) => { calls++; signal = init.signal; return new Promise(() => {}); } }); const pending = transport.request('/api/visits', { visitor_id: 'id' }, caller.signal); await flush(); caller.abort(); await flush(); assert.deepEqual(await pending, { ok: false }); assert.equal(signal.aborted, true); t.mock.timers.tick(100000); await flush(); assert.equal(calls, 1); }); test('caller abort cancels default delayed retry and never makes another request', async t => { t.mock.timers.enable({ apis: ['setTimeout'] }); const caller = new AbortController(); let calls = 0; const transport = createTransport({ fetch: async () => { calls++; throw Error('offline'); } }); const pending = transport.request('/api/arrivals', { dog_id: 'id' }, caller.signal); await flush(); t.mock.timers.tick(499); await flush(); assert.equal(calls, 1); caller.abort(); await flush(); assert.deepEqual(await pending, { ok: false }); t.mock.timers.tick(100000); await flush(); assert.equal(calls, 1); }); test('caller abort settles even an injected sleep that ignores cancellation', async () => { const caller = new AbortController(); let calls = 0, sleepSignal; const transport = createTransport({ fetch: async () => { calls++; throw Error('offline'); }, sleep: (_ms, signal) => { sleepSignal = signal; return new Promise(() => {}); } }); const pending = transport.request('/api/arrivals', { dog_id: 'id' }, caller.signal); await flush(); caller.abort(); await flush(); assert.deepEqual(await pending, { ok: false }); assert.equal(sleepSignal.aborted, true); assert.equal(calls, 1); }); test('already-aborted requests are inert', async () => { const caller = new AbortController(); caller.abort(); const transport = createTransport({ fetch: () => assert.fail('must not fetch') }); assert.deepEqual(await transport.request('/api/arrivals/count', undefined, caller.signal), { ok: false }); }); test('malformed count values and malformed JSON never become displayed zero', async () => { for (const value of [null, {}, [], { count: -1 }, { count: 0.5 }, { count: '2' }, { count: Number.MAX_SAFE_INTEGER + 1 }, { count: NaN }, { count: Infinity }]) { const transport = createTransport({ fetch: async () => response(200, value) }); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: false }); } for (const count of [0, 1, Number.MAX_SAFE_INTEGER]) { assert.deepEqual(await createTransport({ fetch: async () => response(200, { count }) }).request('/api/arrivals/count'), { ok: true, count }); } assert.deepEqual(await createTransport({ fetch: async () => ({ status: 200, json: async () => { throw Error('bad JSON'); } }), retryDelays: [] }).request('/api/arrivals/count'), { ok: false }); }); test('serialization and injected dependency failures are contained without rejection', async () => { const circular = {}; circular.self = circular; const transport = createTransport({ fetch: () => { throw Error('missing'); }, sleep: () => { throw Error('clock unavailable'); } }); assert.deepEqual(await transport.request('/api/visits', circular), { ok: false }); assert.deepEqual(await transport.request('/api/visits', { visitor_id: 'id' }), { ok: false }); }); test('non-contract success statuses cannot register an arrival or invent a count', async () => { assert.deepEqual(await createTransport({ fetch: async () => response(200, {}) }).request('/api/arrivals', { dog_id: 'id' }), { ok: false }); assert.deepEqual(await createTransport({ fetch: async () => response(204) }).request('/api/arrivals/count'), { ok: false }); }); test('network failure reading a count response body retries and can recover', async () => { const delays = []; let calls = 0; const transport = createTransport({ fetch: async () => ++calls === 1 ? new Response(new ReadableStream({ start(controller) { controller.error(new TypeError('network reset')); } }), { status: 200 }) : Response.json({ count: 14 }), sleep: async ms => { delays.push(ms); }, }); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: true, count: 14 }); assert.equal(calls, 2); assert.deepEqual(delays, [500]); }); test('permanent network failure reading count bodies has exactly two delayed retries', async () => { const delays = []; let calls = 0; const transport = createTransport({ fetch: async () => { calls++; return new Response(new ReadableStream({ start(controller) { controller.error(new TypeError('network reset')); } }), { status: 200 }); }, sleep: async ms => { delays.push(ms); }, }); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: false }); assert.equal(calls, 3); assert.deepEqual(delays, [500, 1500]); }); test('syntax-invalid JSON is not a retryable network failure', async () => { let calls = 0; const transport = createTransport({ fetch: async () => { calls++; return new Response('{invalid', { status: 200 }); }, sleep: async () => assert.fail('invalid JSON must not retry') }); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: false }); assert.equal(calls, 1); }); test('retryable and terminal unread response bodies are cancelled before the attempt completes', async () => { for (const [status, body, expectedAttempts] of [[429, { dog_id: 'id' }, 3], [503, { dog_id: 'id' }, 3], [200, { dog_id: 'id' }, 1], [403, undefined, 1]]) { let calls = 0, cancelled = 0; const cancelledBeforeRetry = []; const caller = new AbortController(); const transport = createTransport({ fetch: async () => { calls++; return new Response(new ReadableStream({ cancel() { cancelled++; } }), { status }); }, sleep: async () => { cancelledBeforeRetry.push(cancelled); }, }); assert.deepEqual(await transport.request('/api/arrivals', body, caller.signal), { ok: false }); assert.equal(calls, expectedAttempts); assert.equal(cancelled, calls); assert.deepEqual(cancelledBeforeRetry, expectedAttempts === 3 ? [1, 2] : []); caller.abort(); await flush(); assert.equal(cancelled, calls, 'later caller abort leaves no open body behind'); } }); test('response-body cleanup failures cannot reject or stall the bounded request', async () => { for (const cancel of [() => { throw Error('cancel failed'); }, () => Promise.reject(Error('cancel failed')), () => new Promise(() => {})]) { let cancelled = 0, signal; const transport = createTransport({ fetch: async (_path, init) => { signal = init.signal; return { status: 403, body: { cancel: () => { cancelled++; return cancel(); } } }; } }); assert.deepEqual(await transport.request('/api/arrivals/count'), { ok: false }); assert.equal(cancelled, 1); assert.equal(signal.aborted, true); } });