import { test, expect, describe, beforeEach } from 'bun:test' import { Hono } from 'hono' import { rateLimit, authRateLimit, leadRateLimit, publicApiRateLimit, adminRateLimit } from '../src/server/rate-limit' describe('Rate Limiting', () => { let app: Hono beforeEach(() => { app = new Hono() }) describe('rateLimit function', () => { test('should allow requests under the limit', async () => { app.get('/test-allow', rateLimit(5, 60000), (c) => c.json({ success: true })) const res = await app.request('/test-allow?testId=1') expect(res.status).toBe(200) expect(await res.json()).toEqual({ success: true }) }) test('should block requests over the limit', async () => { app.get('/test-block', rateLimit(1, 60000), (c) => c.json({ success: true })) // First request should succeed const res1 = await app.request('/test-block?testId=2') expect(res1.status).toBe(200) // Second request should be blocked const res2 = await app.request('/test-block?testId=2') expect(res2.status).toBe(429) expect(await res2.json()).toEqual({ success: false, error: 'Too many requests' }) }) test('should reset counter after window expires', async () => { app.get('/test-reset', rateLimit(1, 100), (c) => c.json({ success: true })) // First request should succeed const res1 = await app.request('/test-reset?testId=3') expect(res1.status).toBe(200) // Second request should be blocked const res2 = await app.request('/test-reset?testId=3') expect(res2.status).toBe(429) // Wait for window to expire await new Promise(resolve => setTimeout(resolve, 150)) // Third request should succeed after window reset const res3 = await app.request('/test-reset?testId=3') expect(res3.status).toBe(200) }) test('should set rate limit headers', async () => { app.get('/test-headers', rateLimit(5, 60000), (c) => c.json({ success: true })) const res = await app.request('/test-headers?testId=4') expect(res.headers.get('X-RateLimit-Limit')).toBe('5') expect(res.headers.get('X-RateLimit-Remaining')).toBe('4') expect(res.headers.get('X-RateLimit-Reset')).toBeDefined() }) }) describe('Predefined rate limiters', () => { test('authRateLimit should allow 10 requests per minute', async () => { const appAuth = new Hono() appAuth.get('/auth-test', authRateLimit, (c) => c.json({ success: true })) // Make 10 requests with same test ID, all should succeed for (let i = 0; i < 10; i++) { const res = await appAuth.request('/auth-test?testId=auth-same') expect(res.status).toBe(200) } // 11th request with same test ID should be blocked const resBlocked = await appAuth.request('/auth-test?testId=auth-same') expect(resBlocked.status).toBe(429) }) test('leadRateLimit should allow 20 requests per minute', async () => { app.get('/lead-test-block', leadRateLimit, (c) => c.json({ success: true })) // Make 20 requests with same test ID, all should succeed for (let i = 0; i < 20; i++) { const res = await app.request('/lead-test-block?testId=lead-same') expect(res.status).toBe(200) } // 21st request with same test ID should be blocked const resBlocked = await app.request('/lead-test-block?testId=lead-same') expect(resBlocked.status).toBe(429) }) test('publicApiRateLimit should allow 100 requests per minute', async () => { app.get('/public-test-block', publicApiRateLimit, (c) => c.json({ success: true })) // Make 100 requests with same test ID, all should succeed for (let i = 0; i < 100; i++) { const res = await app.request('/public-test-block?testId=public-same') expect(res.status).toBe(200) } // 101st request with same test ID should be blocked const resBlocked = await app.request('/public-test-block?testId=public-same') expect(resBlocked.status).toBe(429) }) test('adminRateLimit should allow 1000 requests per minute', async () => { app.get('/admin-test-block', adminRateLimit, (c) => c.json({ success: true })) // Make 1000 requests with same test ID, all should succeed // We'll test with a smaller number for practicality for (let i = 0; i < 100; i++) { // Using 100 for faster test const res = await app.request('/admin-test-block?testId=admin-same') expect(res.status).toBe(200) } // Note: We're not testing the 1001st request being blocked since that would take too long }) }) })