import { describe, expect, it } from "vitest"; import { getClientIp, SlidingWindowRateLimiter, type RateLimitRule } from "./rateLimit"; const shortRule: RateLimitRule = { scope: "test", maxAttempts: 2, windowMs: 1_000, }; describe("SlidingWindowRateLimiter", () => { it("bloque après la limite et autorise à nouveau une fois la fenêtre expirée", () => { const limiter = new SlidingWindowRateLimiter(); expect(limiter.consume(shortRule, "user-1", 1_000)).toMatchObject({ allowed: true, remaining: 1 }); expect(limiter.consume(shortRule, "user-1", 1_100)).toMatchObject({ allowed: true, remaining: 0 }); expect(limiter.consume(shortRule, "user-1", 1_200)).toMatchObject({ allowed: false, retryAfterMs: 800 }); // À 2 001 ms, la tentative de 1 100 ms est encore dans la fenêtre glissante. expect(limiter.consume(shortRule, "user-1", 2_001)).toMatchObject({ allowed: true, remaining: 0 }); }); it("isole les compteurs de chaque clé", () => { const limiter = new SlidingWindowRateLimiter(); limiter.consume(shortRule, "user-1", 1_000); limiter.consume(shortRule, "user-1", 1_100); expect(limiter.consume(shortRule, "user-2", 1_200)).toMatchObject({ allowed: true, remaining: 1 }); }); it("extrait uniquement la première adresse client transmise par le proxy", () => { expect(getClientIp({ "x-forwarded-for": "203.0.113.8, 10.0.0.1" })).toBe("203.0.113.8"); expect(getClientIp({})).toBe("unknown-client"); }); });