73 lines
2.5 KiB
TypeScript
73 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { RedditRequestCoordinator } from '../src/reddit/request-coordinator.js';
|
|
|
|
describe('RedditRequestCoordinator', () => {
|
|
it('paces requests and applies a shared cooldown to the next queued request', async () => {
|
|
let now = 0;
|
|
const sleeps: number[] = [];
|
|
const coordinator = new RedditRequestCoordinator(6_500, {
|
|
now: () => now,
|
|
sleep: async (ms) => { sleeps.push(ms); now += ms; },
|
|
});
|
|
|
|
await coordinator.schedule(async () => undefined);
|
|
coordinator.extendCooldown(30_000);
|
|
await coordinator.schedule(async () => undefined);
|
|
|
|
expect(sleeps).toEqual([31_000]);
|
|
});
|
|
|
|
it('serializes concurrent requests through one process-wide queue', async () => {
|
|
const order: string[] = [];
|
|
let releaseFirst!: () => void;
|
|
const firstFinished = new Promise<void>((resolve) => { releaseFirst = resolve; });
|
|
const coordinator = new RedditRequestCoordinator(0);
|
|
|
|
const first = coordinator.schedule(async () => {
|
|
order.push('first:start');
|
|
await firstFinished;
|
|
order.push('first:end');
|
|
});
|
|
const second = coordinator.schedule(async () => { order.push('second'); });
|
|
|
|
await Promise.resolve();
|
|
expect(order).toEqual(['first:start']);
|
|
releaseFirst();
|
|
await Promise.all([first, second]);
|
|
expect(order).toEqual(['first:start', 'first:end', 'second']);
|
|
});
|
|
|
|
it('does not release a queued request before a failing request records its cooldown', async () => {
|
|
let now = 0;
|
|
const sleeps: number[] = [];
|
|
const coordinator = new RedditRequestCoordinator(0, {
|
|
now: () => now,
|
|
sleep: async (ms) => { sleeps.push(ms); now += ms; },
|
|
});
|
|
|
|
const first = coordinator.schedule(async () => {
|
|
coordinator.extendCooldown(15_000);
|
|
throw new Error('429');
|
|
});
|
|
const second = coordinator.schedule(async () => undefined);
|
|
|
|
await expect(first).rejects.toThrow('429');
|
|
await second;
|
|
expect(sleeps).toEqual([16_000]);
|
|
});
|
|
|
|
it('paces a queued request after a failed attempt without an explicit cooldown', async () => {
|
|
let now = 0;
|
|
const sleeps: number[] = [];
|
|
const coordinator = new RedditRequestCoordinator(6_500, {
|
|
now: () => now,
|
|
sleep: async (ms) => { sleeps.push(ms); now += ms; },
|
|
});
|
|
|
|
await expect(coordinator.schedule(async () => { throw new Error('403'); })).rejects.toThrow('403');
|
|
await coordinator.schedule(async () => undefined);
|
|
|
|
expect(sleeps).toEqual([6_500]);
|
|
});
|
|
});
|