// A4/B3 regression: the PRODUCTION binder must enforce the auth manifest's // rate-limit budgets. Unlike bind-production.test.ts (which mocks every // feature binder), this file runs the REAL auth production binder against a // stubbed Payload local API and drives sign-in through the app router. import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("@repo/core-cms", () => ({ default: Promise.resolve({}) })); const payloadStub = vi.hoisted(() => ({ secret: "test-secret", jobs: { queue: vi.fn() }, // No user ever matches → every sign-in fails and consumes budget. find: vi.fn(async () => ({ docs: [] })), findByID: vi.fn(async () => null), create: vi.fn(async ({ data }: { data: Record }) => data), })); vi.mock("payload", () => ({ getPayload: vi.fn(async () => payloadStub) })); // Other features are irrelevant here — mock their binders so this test only // boots the auth production path. vi.mock("@repo/blog/di/bind-production", () => ({ bindProductionBlog: vi.fn(), })); vi.mock("@repo/marketing-pages/di/bind-production", () => ({ bindProductionMarketingPages: vi.fn(), })); vi.mock("@repo/navigation/di/bind-production", () => ({ bindProductionNavigation: vi.fn(), })); vi.mock("@repo/media/di/bind-production", () => ({ bindProductionMedia: vi.fn(), })); describe("bindAllProduction rate limiting (A4/B3)", () => { beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); }); it("returns TOO_MANY_REQUESTS once the manifest ip budget is exhausted", async () => { const { bindAllProduction } = await import("./bind-production"); await bindAllProduction(); const { appRouter } = await import("@repo/core-api"); const { authManifest } = await import("@repo/auth"); const caller = appRouter.createCaller({ clientIp: "203.0.113.7" }); const attempt = () => caller.auth.signIn({ username: "ghost", password: "wrong-password" }); // The manifest is the budget's source of truth: 5 failed attempts pass // through (UNAUTHORIZED), the 6th trips the ip bucket. const ipBudget = authManifest.useCases.signIn.rateLimit.find( (b) => b.name === "ip", ); expect(ipBudget).toBeDefined(); for (let i = 0; i < ipBudget!.budget; i++) { await expect(attempt()).rejects.toMatchObject({ code: "UNAUTHORIZED" }); } await expect(attempt()).rejects.toMatchObject({ code: "TOO_MANY_REQUESTS", }); }); it("keeps other client IPs unaffected by an exhausted bucket", async () => { const { bindAllProduction } = await import("./bind-production"); await bindAllProduction(); const { appRouter } = await import("@repo/core-api"); const { authManifest } = await import("@repo/auth"); const throttled = appRouter.createCaller({ clientIp: "198.51.100.9" }); const ipBudget = authManifest.useCases.signIn.rateLimit.find( (b) => b.name === "ip", )!; for (let i = 0; i < ipBudget.budget; i++) { await expect( throttled.auth.signIn({ username: `user${i}x`, password: "wrong-password", }), ).rejects.toMatchObject({ code: "UNAUTHORIZED" }); } await expect( throttled.auth.signIn({ username: "user0x", password: "wrong-password" }), ).rejects.toMatchObject({ code: "TOO_MANY_REQUESTS" }); // A different IP still gets an ordinary auth failure, not a throttle. const fresh = appRouter.createCaller({ clientIp: "192.0.2.55" }); await expect( fresh.auth.signIn({ username: "someoneelse", password: "wrong-password", }), ).rejects.toMatchObject({ code: "UNAUTHORIZED" }); }); });