49 lines
1.7 KiB
TypeScript
49 lines
1.7 KiB
TypeScript
import { beforeEach, describe, expect, it } from 'vitest';
|
|
import { TooManyRequestsError } from '@bytelyst/errors';
|
|
import { assertRateLimit, rateLimitKey, resetRateLimits } from './rate-limit.js';
|
|
|
|
describe('rate limiting', () => {
|
|
beforeEach(() => {
|
|
resetRateLimits();
|
|
});
|
|
|
|
it('normalizes missing key parts', () => {
|
|
expect(rateLimitKey('prompt-run', '', null, 'user_1')).toBe('prompt-run:unknown:unknown:user_1');
|
|
});
|
|
|
|
it('allows requests up to the policy limit', () => {
|
|
const policy = { label: 'test endpoint', max: 2, windowMs: 1_000 };
|
|
|
|
assertRateLimit('test:user_1', policy, 1_000);
|
|
assertRateLimit('test:user_1', policy, 1_500);
|
|
|
|
expect(() => assertRateLimit('test:user_1', policy, 2_001)).not.toThrow();
|
|
});
|
|
|
|
it('throws a shared 429 error when the bucket is exhausted', () => {
|
|
const policy = { label: 'test endpoint', max: 2, windowMs: 1_000 };
|
|
|
|
assertRateLimit('test:user_1', policy, 1_000);
|
|
assertRateLimit('test:user_1', policy, 1_001);
|
|
|
|
expect(() => assertRateLimit('test:user_1', policy, 1_999)).toThrow(TooManyRequestsError);
|
|
|
|
try {
|
|
assertRateLimit('test:user_1', policy, 1_999);
|
|
} catch (error) {
|
|
expect(error).toBeInstanceOf(TooManyRequestsError);
|
|
const rateLimitError = error as TooManyRequestsError;
|
|
expect(rateLimitError.statusCode).toBe(429);
|
|
expect(rateLimitError.details).toEqual({ limit: 2, windowMs: 1_000 });
|
|
}
|
|
});
|
|
|
|
it('isolates buckets by key', () => {
|
|
const policy = { label: 'test endpoint', max: 1, windowMs: 1_000 };
|
|
|
|
assertRateLimit('test:user_1', policy, 1_000);
|
|
|
|
expect(() => assertRateLimit('test:user_2', policy, 1_001)).not.toThrow();
|
|
});
|
|
});
|