Something went wrong. Try again.
TypeScript client for accessing TLE Community.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121import Lacuna from '../lacuna';import { rpcLimitHandler } from './rpc-limit-handler';import type { ResponseEvent, ResponseHandler } from './responses';import type { ServerRequest, ServerResponse } from './server';
// A pure unit test: driving the real 1010 path would mean firing 200+ live// requests just to trip the server's per-minute limit, which is exactly what// this suite's session caching and --runInBand exist to avoid. The handler is// a function of the event, so a synthetic event exercises it fully.
const request: ServerRequest = { module: 'empire', method: 'get_status', params: {}, addSession: true,};
const slowDown = { code: 1010, message: 'Slow down', data: {} };
const eventFor = ( response: ServerResponse<any>, attempt = 0, retry: () => Promise<ServerResponse<any>> = jest.fn()): ResponseEvent => ({ request, response, attempt, retry }) as ResponseEvent;
let lacuna: Lacuna;let handler: ResponseHandler;
beforeEach(() => { jest.useFakeTimers(); console.warn = jest.fn(); lacuna = new Lacuna(); handler = rpcLimitHandler(lacuna, { maxRetries: 3, baseDelayMs: 1000, maxDelayMs: 5000 });});
afterEach(() => { jest.useRealTimers();});
/** Runs the handler while letting its backoff timer fire immediately. */async function run(event: ResponseEvent) { const pending = handler(event); await jest.runAllTimersAsync(); return pending;}
test('ignores anything that is not an RPC limit error', async () => { const retry = jest.fn();
expect(await run(eventFor({ result: { ok: 1 } }, 0, retry))).toBeUndefined(); expect( await run(eventFor({ error: { code: 1002, message: 'nope', data: {} } }, 0, retry)) ).toBeUndefined();
expect(retry).not.toHaveBeenCalled();});
test('retries an RPC limit error and returns the retry response', async () => { const recovered = { result: { ok: 1 } }; const retry = jest.fn().mockResolvedValue(recovered);
const handled = await run(eventFor({ error: slowDown }, 0, retry));
expect(retry).toHaveBeenCalledTimes(1); expect(handled).toBe(recovered); expect(console.warn).toHaveBeenCalledWith( expect.stringContaining('RPC limit hit on empire/get_status, retrying in 1000ms (1/3)') );});
test('backs off progressively as attempts climb', async () => { const retry = jest.fn().mockResolvedValue({ result: {} });
for (const attempt of [0, 1, 2]) { await run(eventFor({ error: slowDown }, attempt, retry)); }
const logged = (console.warn as jest.Mock).mock.calls.map((c) => c[0] as string); expect(logged[0]).toContain('retrying in 1000ms'); expect(logged[1]).toContain('retrying in 2000ms'); // 1000 * 2^2 = 4000, still under the 5000ms ceiling. expect(logged[2]).toContain('retrying in 4000ms');});
test('clamps the delay at maxDelayMs on later attempts', async () => { const retry = jest.fn().mockResolvedValue({ result: {} });
await run(eventFor({ error: slowDown }, 2, retry));
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('retrying in 4000ms'));
(console.warn as jest.Mock).mockClear(); handler = rpcLimitHandler(lacuna, { maxRetries: 10, baseDelayMs: 1000, maxDelayMs: 5000 });
await run(eventFor({ error: slowDown }, 5, retry));
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('retrying in 5000ms'));});
test('gives up once maxRetries is reached, leaving the error in place', async () => { const retry = jest.fn();
const handled = await run(eventFor({ error: slowDown }, 3, retry));
expect(retry).not.toHaveBeenCalled(); expect(handled).toBeUndefined(); expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('after 3 retries - giving up'));});
test('defaults to 5 retries with a 1s base delay', async () => { const retry = jest.fn().mockResolvedValue({ result: {} }); handler = rpcLimitHandler(lacuna);
await run(eventFor({ error: slowDown }, 4, retry)); expect(retry).toHaveBeenCalledTimes(1); expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('(5/5)'));
await run(eventFor({ error: slowDown }, 5, retry)); expect(retry).toHaveBeenCalledTimes(1);});