import { beforeEach, describe, expect, mock, test } from "bun:test";
const env: Record<string, string> = { PUBLIC_URL: "https://forge.example.com" };
mock.module("cloudflare:workers", () => ({ env }));
mock.module("@tanstack/react-start/server", () => ({ getRequestHeader: () => null }));
const hits = new Map<string, number>();
const deletes: number[] = [];
mock.module("./db", () => ({
first: async (_sql: string, key: string, window: number) => {
const id = `${key}@${window}`;
const next = (hits.get(id) ?? 0) + 1;
hits.set(id, next);
return { hits: next };
},
run: async (_sql: string, cutoff: number) => {
deletes.push(cutoff);
},
}));
const { enforceHourlyLimit, pruneHourlyLimits } = await import("./limits");
const HOUR = 60 * 60 * 1000;
const base = 1_000 * HOUR;
beforeEach(() => {
hits.clear();
deletes.length = 0;
delete env.RATE_LIMITS_DISABLED;
});
describe("enforceHourlyLimit", () => {
test("allows up to max then rejects with 429", async () => {
const max = 3;
for (let i = 0; i < max; i++) await enforceHourlyLimit("sudo:u1", max, base);
await expect(enforceHourlyLimit("sudo:u1", max, base)).rejects.toMatchObject({ status: 429 });
});
test("keys are independent", async () => {
await enforceHourlyLimit("sudo:u1", 1, base);