51 lines
1.6 KiB
TypeScript
51 lines
1.6 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createLoopRateLimiter } from "./loop-rate-limiter.js";
|
|
|
|
describe("createLoopRateLimiter", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("allows messages below the threshold", () => {
|
|
const limiter = createLoopRateLimiter({ windowMs: 10_000, maxHits: 3 });
|
|
limiter.record("conv:1");
|
|
limiter.record("conv:1");
|
|
expect(limiter.isRateLimited("conv:1")).toBe(false);
|
|
});
|
|
|
|
it("rate limits at the threshold", () => {
|
|
const limiter = createLoopRateLimiter({ windowMs: 10_000, maxHits: 3 });
|
|
limiter.record("conv:1");
|
|
limiter.record("conv:1");
|
|
limiter.record("conv:1");
|
|
expect(limiter.isRateLimited("conv:1")).toBe(true);
|
|
});
|
|
|
|
it("does not cross-contaminate conversations", () => {
|
|
const limiter = createLoopRateLimiter({ windowMs: 10_000, maxHits: 2 });
|
|
limiter.record("conv:1");
|
|
limiter.record("conv:1");
|
|
expect(limiter.isRateLimited("conv:1")).toBe(true);
|
|
expect(limiter.isRateLimited("conv:2")).toBe(false);
|
|
});
|
|
|
|
it("resets after the time window expires", () => {
|
|
const limiter = createLoopRateLimiter({ windowMs: 5_000, maxHits: 2 });
|
|
limiter.record("conv:1");
|
|
limiter.record("conv:1");
|
|
expect(limiter.isRateLimited("conv:1")).toBe(true);
|
|
|
|
vi.advanceTimersByTime(6_000);
|
|
expect(limiter.isRateLimited("conv:1")).toBe(false);
|
|
});
|
|
|
|
it("returns false for unknown conversations", () => {
|
|
const limiter = createLoopRateLimiter();
|
|
expect(limiter.isRateLimited("unknown")).toBe(false);
|
|
});
|
|
});
|