Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/services/auth/auth-rate-limit.server.test.ts
T

55 lines
2.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { cache } from "~/utils/cache.server";
import { checkAuthRateLimit, getAuthRateLimitKey } from "./auth-rate-limit.server";
describe("auth rate limiting", () => {
beforeEach(() => {
cache.clear();
vi.spyOn(Date, "now").mockReturnValue(new Date("2026-05-25T12:00:00.000Z").getTime());
});
afterEach(() => {
vi.restoreAllMocks();
cache.clear();
});
it("builds stable keys from action, client ip, and normalized identifier", () => {
const request = new Request("https://example.test/login", {
headers: { "x-forwarded-for": "203.0.113.10, 198.51.100.5" },
});
expect(getAuthRateLimitKey({ request, action: "login", identifier: " Ada@Example.COM " })).toBe(
getAuthRateLimitKey({ request, action: "login", identifier: "ada@example.com" })
);
expect(getAuthRateLimitKey({ request, action: "login", identifier: "ada@example.com" })).not.toBe(
getAuthRateLimitKey({ request, action: "forgot-password", identifier: "ada@example.com" })
);
});
it("allows requests up to the limit and returns retry timing after the limit", () => {
const request = new Request("https://example.test/login", {
headers: { "x-client-ip": "203.0.113.10" },
});
const options = { request, action: "login" as const, identifier: "ada@example.com", limit: 2, windowMs: 30_000 };
expect(checkAuthRateLimit(options)).toEqual({ allowed: true });
expect(checkAuthRateLimit(options)).toEqual({ allowed: true });
expect(checkAuthRateLimit(options)).toEqual({ allowed: false, retryAfterSeconds: 30 });
});
it("starts a new window after the cached counter expires", () => {
const request = new Request("https://example.test/mfa", {
headers: { "x-client-ip": "203.0.113.10" },
});
const options = { request, action: "mfa" as const, identifier: "ada@example.com", limit: 1, windowMs: 10_000 };
expect(checkAuthRateLimit(options)).toEqual({ allowed: true });
expect(checkAuthRateLimit(options)).toEqual({ allowed: false, retryAfterSeconds: 10 });
vi.mocked(Date.now).mockReturnValue(new Date("2026-05-25T12:00:11.000Z").getTime());
expect(checkAuthRateLimit(options)).toEqual({ allowed: true });
});
});