← back to Govarbitrage

src/lib/rate-limit.test.ts

33 lines

import { describe, expect, it, beforeEach } from "vitest";
import { rateLimit, __resetRateLimit } from "./rate-limit";

beforeEach(() => __resetRateLimit());

describe("rateLimit (sliding window)", () => {
  it("allows up to the limit, then blocks", () => {
    const t0 = 1_000_000;
    for (let i = 1; i <= 5; i++) {
      expect(rateLimit("ip:a", 5, 60_000, t0).allowed).toBe(true);
    }
    const sixth = rateLimit("ip:a", 5, 60_000, t0);
    expect(sixth.allowed).toBe(false);
    expect(sixth.remaining).toBe(0);
    expect(sixth.retryAfterSec).toBeGreaterThan(0);
  });

  it("resets after the window elapses", () => {
    const t0 = 2_000_000;
    for (let i = 0; i < 5; i++) rateLimit("ip:b", 5, 60_000, t0);
    expect(rateLimit("ip:b", 5, 60_000, t0).allowed).toBe(false);
    // A moment past the window: fresh bucket.
    expect(rateLimit("ip:b", 5, 60_000, t0 + 60_001).allowed).toBe(true);
  });

  it("isolates buckets by key", () => {
    const t0 = 3_000_000;
    for (let i = 0; i < 5; i++) rateLimit("ip:c", 5, 60_000, t0);
    expect(rateLimit("ip:c", 5, 60_000, t0).allowed).toBe(false);
    expect(rateLimit("ip:d", 5, 60_000, t0).allowed).toBe(true);
  });
});