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((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]); }); });