Files
memind/api-core-retry.test.mjs
T

87 lines
2.1 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { fetchWithTransientRetry } from './src/api/core.ts';
async function withMockFetch(mock, run) {
const originalFetch = globalThis.fetch;
globalThis.fetch = mock;
try {
await run();
} finally {
globalThis.fetch = originalFetch;
}
}
test('idempotent requests retry transient network failures', async () => {
let calls = 0;
await withMockFetch(
async () => {
calls += 1;
if (calls < 3) throw new TypeError('fetch failed');
return new Response('{}', { status: 200 });
},
async () => {
const response = await fetchWithTransientRetry('/api/sessions', undefined, 1_000, [0, 0]);
assert.equal(response.status, 200);
},
);
assert.equal(calls, 3);
});
test('idempotent requests retry transient gateway responses', async () => {
let calls = 0;
await withMockFetch(
async () => {
calls += 1;
return calls === 1
? new Response('temporary', { status: 503 })
: new Response('{}', { status: 200 });
},
async () => {
const response = await fetchWithTransientRetry('/api/sessions', undefined, 1_000, [0]);
assert.equal(response.status, 200);
},
);
assert.equal(calls, 2);
});
test('non-idempotent requests are never retried', async () => {
let calls = 0;
await withMockFetch(
async () => {
calls += 1;
throw new TypeError('fetch failed');
},
async () => {
await assert.rejects(
fetchWithTransientRetry(
'/api/agent/start',
{ method: 'POST', body: '{}' },
1_000,
[0, 0],
),
/fetch failed/,
);
},
);
assert.equal(calls, 1);
});
test('aborted requests are never retried', async () => {
let calls = 0;
await withMockFetch(
async () => {
calls += 1;
throw new DOMException('Aborted', 'AbortError');
},
async () => {
await assert.rejects(
fetchWithTransientRetry('/api/sessions', undefined, 1_000, [0, 0]),
(error) => error instanceof DOMException && error.name === 'AbortError',
);
},
);
assert.equal(calls, 1);
});