import assert from "node:assert/strict"; import test from "node:test"; import { consumeRequestRateLimit, resetRequestRateLimitMaintenanceForTests, } from "../src/lib/request-rate-limit.ts"; test("a user is allowed up to the window limit and then must wait", () => { const store = new Map(); const first = consumeRequestRateLimit({ key: "search:user-1", limit: 2, windowMs: 60_000, now: 1_000, store, }); const second = consumeRequestRateLimit({ key: "search:user-1", limit: 2, windowMs: 60_000, now: 2_000, store, }); const blocked = consumeRequestRateLimit({ key: "search:user-1", limit: 2, windowMs: 60_000, now: 3_000, store, }); const otherUser = consumeRequestRateLimit({ key: "search:user-2", limit: 2, windowMs: 60_000, now: 3_000, store, }); const afterWindow = consumeRequestRateLimit({ key: "search:user-1", limit: 2, windowMs: 60_000, now: 61_500, store, }); assert.deepEqual(first, { ok: true }); assert.deepEqual(second, { ok: true }); assert.deepEqual(blocked, { ok: false, retryAfterSeconds: 58 }); assert.deepEqual(otherUser, { ok: true }); assert.deepEqual(afterWindow, { ok: true }); }); test("keys past the longest seen window are swept, and a one-hour key survives 61 seconds", () => { resetRequestRateLimitMaintenanceForTests(); const shortWindow = new Map(); consumeRequestRateLimit({ key: "old", limit: 5, windowMs: 60_000, now: 0, store: shortWindow, }); for (let index = 0; index < 998; index += 1) { consumeRequestRateLimit({ key: `pad:${index}`, limit: 5, windowMs: 60_000, now: 0, store: shortWindow, }); } consumeRequestRateLimit({ key: "trigger", limit: 5, windowMs: 60_000, now: 61_000, store: shortWindow, }); assert.equal(shortWindow.has("old"), false); assert.equal(shortWindow.get("old"), undefined); assert.equal(shortWindow.has("trigger"), true); assert.equal(shortWindow.size, 1); resetRequestRateLimitMaintenanceForTests(); const mixed = new Map(); consumeRequestRateLimit({ key: "synastry:person", limit: 8, windowMs: 60 * 60_000, now: 0, store: mixed, }); consumeRequestRateLimit({ key: "ephemeris:person", limit: 30, windowMs: 60_000, now: 0, store: mixed, }); for (let index = 0; index < 998; index += 1) { consumeRequestRateLimit({ key: `later:${index}`, limit: 5, windowMs: 60_000, now: 61_000, store: mixed, }); } assert.equal(mixed.has("synastry:person"), true); assert.equal(mixed.has("ephemeris:person"), true); resetRequestRateLimitMaintenanceForTests(); });