import { describe, it, expect } from 'vitest'; import { createAdminRateLimiter, FORCE_UNLOCK_LIMIT } from './admin-rate-limit.js'; describe('AdminRateLimiter', () => { it('allows up to maxRequests within the window', () => { const limiter = createAdminRateLimiter({ windowMs: 60_000, maxRequests: 3 }); const now = new Date('2026-05-12T00:00:00Z'); expect(limiter.check('admin-1', now).allowed).toBe(true); expect(limiter.check('admin-1', now).allowed).toBe(true); expect(limiter.check('admin-1', now).allowed).toBe(true); }); it('denies the (maxRequests+1)th request and returns retryAfterSeconds', () => { const limiter = createAdminRateLimiter({ windowMs: 60_000, maxRequests: 2 }); const now = new Date('2026-05-12T00:00:00Z'); limiter.check('admin-1', now); limiter.check('admin-1', now); const denied = limiter.check('admin-1', now); expect(denied.allowed).toBe(false); expect(denied.retryAfterSeconds).toBeGreaterThan(0); expect(denied.retryAfterSeconds).toBeLessThanOrEqual(60); }); it('isolates buckets between users', () => { const limiter = createAdminRateLimiter({ windowMs: 60_000, maxRequests: 1 }); const now = new Date('2026-05-12T00:00:00Z'); expect(limiter.check('alice', now).allowed).toBe(true); expect(limiter.check('bob', now).allowed).toBe(true); expect(limiter.check('alice', now).allowed).toBe(false); }); it('starts a new window after windowMs elapses', () => { const limiter = createAdminRateLimiter({ windowMs: 1000, maxRequests: 1 }); const t0 = new Date('2026-05-12T00:00:00Z'); const t1 = new Date('2026-05-12T00:00:01.500Z'); expect(limiter.check('admin-1', t0).allowed).toBe(true); expect(limiter.check('admin-1', t0).allowed).toBe(false); expect(limiter.check('admin-1', t1).allowed).toBe(true); }); it('reset(userId) clears one bucket', () => { const limiter = createAdminRateLimiter({ windowMs: 60_000, maxRequests: 1 }); const now = new Date('2026-05-12T00:00:00Z'); limiter.check('admin-1', now); expect(limiter.check('admin-1', now).allowed).toBe(false); limiter.reset('admin-1'); expect(limiter.check('admin-1', now).allowed).toBe(true); }); it('resetAll() clears all buckets', () => { const limiter = createAdminRateLimiter({ windowMs: 60_000, maxRequests: 1 }); const now = new Date('2026-05-12T00:00:00Z'); limiter.check('alice', now); limiter.check('bob', now); limiter.resetAll(); expect(limiter.check('alice', now).allowed).toBe(true); expect(limiter.check('bob', now).allowed).toBe(true); }); it('FORCE_UNLOCK_LIMIT default is 10/hr', () => { expect(FORCE_UNLOCK_LIMIT.maxRequests).toBe(10); expect(FORCE_UNLOCK_LIMIT.windowMs).toBe(60 * 60 * 1000); }); });