refactor(acp): split session tests and share rate limiter

This commit is contained in:
Peter Steinberger
2026-02-19 14:55:00 +01:00
parent 19348050be
commit f8b61bb4ed
6 changed files with 240 additions and 184 deletions

View File

@@ -0,0 +1,31 @@
import { describe, expect, it } from "vitest";
import { createFixedWindowRateLimiter } from "./fixed-window-rate-limit.js";
describe("fixed-window rate limiter", () => {
it("blocks after max requests until window reset", () => {
let nowMs = 1_000;
const limiter = createFixedWindowRateLimiter({
maxRequests: 2,
windowMs: 1_000,
now: () => nowMs,
});
expect(limiter.consume()).toMatchObject({ allowed: true, remaining: 1 });
expect(limiter.consume()).toMatchObject({ allowed: true, remaining: 0 });
expect(limiter.consume()).toMatchObject({ allowed: false, retryAfterMs: 1_000 });
nowMs += 1_000;
expect(limiter.consume()).toMatchObject({ allowed: true, remaining: 1 });
});
it("supports explicit reset", () => {
const limiter = createFixedWindowRateLimiter({
maxRequests: 1,
windowMs: 10_000,
});
expect(limiter.consume().allowed).toBe(true);
expect(limiter.consume().allowed).toBe(false);
limiter.reset();
expect(limiter.consume().allowed).toBe(true);
});
});

View File

@@ -0,0 +1,48 @@
export type FixedWindowRateLimiter = {
consume: () => {
allowed: boolean;
retryAfterMs: number;
remaining: number;
};
reset: () => void;
};
export function createFixedWindowRateLimiter(params: {
maxRequests: number;
windowMs: number;
now?: () => number;
}): FixedWindowRateLimiter {
const maxRequests = Math.max(1, Math.floor(params.maxRequests));
const windowMs = Math.max(1, Math.floor(params.windowMs));
const now = params.now ?? Date.now;
let count = 0;
let windowStartMs = 0;
return {
consume() {
const nowMs = now();
if (nowMs - windowStartMs >= windowMs) {
windowStartMs = nowMs;
count = 0;
}
if (count >= maxRequests) {
return {
allowed: false,
retryAfterMs: Math.max(0, windowStartMs + windowMs - nowMs),
remaining: 0,
};
}
count += 1;
return {
allowed: true,
retryAfterMs: 0,
remaining: Math.max(0, maxRequests - count),
};
},
reset() {
count = 0;
windowStartMs = 0;
},
};
}