/** * Test script for Rate Limiter functionality * Run with: npx tsx dev/test-rate-limiter.ts */ import { RateLimiter } from '../lib/services/rate-limiter'; // Mock API call function async function mockApiCall(id: number, delay: number = 10): Promise<{ id: number; timestamp: number }> { await new Promise(resolve => setTimeout(resolve, delay)); return { id, timestamp: Date.now() }; } async function testRateLimiter() { console.log('๐Ÿ” Testing Rate Limiter Functionality\n'); try { // Test 1: Basic throttling with 5 requests/second console.log('Test 1: Basic throttling (5 requests/second)'); const limiter1 = new RateLimiter(5); const startTime1 = Date.now(); const results1: any[] = []; // Queue 10 requests (should take ~2 seconds with 5 req/sec limit) const promises1 = Array.from({ length: 10 }, (_, i) => limiter1.throttle(() => mockApiCall(i + 1)) ); for (const promise of promises1) { const result = await promise; results1.push(result); } const duration1 = Date.now() - startTime1; console.log(`โœ… Completed 10 requests in ${duration1}ms`); console.log(` Expected: ~2000ms (10 requests รท 5 req/sec)`); console.log(` Within acceptable range: ${duration1 >= 1800 && duration1 <= 2500 ? 'Yes' : 'No'}\n`); // Test 2: High-volume throttling with 10 requests/second console.log('Test 2: High-volume throttling (10 requests/second)'); const limiter2 = new RateLimiter(10); const startTime2 = Date.now(); const results2: any[] = []; // Queue 25 requests (should take ~2.5 seconds with 10 req/sec limit) const promises2 = Array.from({ length: 25 }, (_, i) => limiter2.throttle(() => mockApiCall(i + 1)) ); for (const promise of promises2) { const result = await promise; results2.push(result); } const duration2 = Date.now() - startTime2; console.log(`โœ… Completed 25 requests in ${duration2}ms`); console.log(` Expected: ~2500ms (25 requests รท 10 req/sec)`); console.log(` Within acceptable range: ${duration2 >= 2300 && duration2 <= 3000 ? 'Yes' : 'No'}\n`); // Test 3: Verify rate limit enforcement console.log('Test 3: Verify rate limit enforcement (10 requests/second)'); const limiter3 = new RateLimiter(10); const timestamps: number[] = []; // Execute 15 requests and track timestamps const promises3 = Array.from({ length: 15 }, (_, i) => limiter3.throttle(async () => { const now = Date.now(); timestamps.push(now); return mockApiCall(i + 1, 1); }) ); await Promise.all(promises3); // Check that no more than 10 requests happened in any 1-second window let maxRequestsInWindow = 0; for (let i = 0; i < timestamps.length; i++) { const windowStart = timestamps[i]; const windowEnd = windowStart + 1000; const requestsInWindow = timestamps.filter(t => t >= windowStart && t < windowEnd).length; maxRequestsInWindow = Math.max(maxRequestsInWindow, requestsInWindow); } console.log(`โœ… Maximum requests in any 1-second window: ${maxRequestsInWindow}`); console.log(` Rate limit respected: ${maxRequestsInWindow <= 10 ? 'Yes' : 'No'}\n`); // Test 4: Queue length tracking console.log('Test 4: Queue length tracking'); const limiter4 = new RateLimiter(5); // Queue multiple requests without awaiting const promises4 = Array.from({ length: 20 }, (_, i) => limiter4.throttle(() => mockApiCall(i + 1, 50)) ); // Check queue length immediately after queuing await new Promise(resolve => setTimeout(resolve, 10)); const queueLength = limiter4.getQueueLength(); console.log(`โœ… Queue length after queuing 20 requests: ${queueLength}`); console.log(` Queue has pending requests: ${queueLength > 0 ? 'Yes' : 'No'}`); // Wait for all to complete await Promise.all(promises4); const finalQueueLength = limiter4.getQueueLength(); console.log(`โœ… Queue length after completion: ${finalQueueLength}`); console.log(` Queue is empty: ${finalQueueLength === 0 ? 'Yes' : 'No'}\n`); // Test 5: Current request count tracking console.log('Test 5: Current request count tracking'); const limiter5 = new RateLimiter(10); const requestCounts: number[] = []; // Execute requests and track current count const promises5 = Array.from({ length: 15 }, (_, i) => limiter5.throttle(async () => { const count = limiter5.getCurrentRequestCount(); requestCounts.push(count); return mockApiCall(i + 1, 1); }) ); await Promise.all(promises5); const maxCount = Math.max(...requestCounts); console.log(`โœ… Maximum concurrent request count: ${maxCount}`); console.log(` Never exceeded limit: ${maxCount <= 10 ? 'Yes' : 'No'}\n`); // Test 6: Reset functionality console.log('Test 6: Reset functionality'); const limiter6 = new RateLimiter(5); // Queue some requests const promises6 = Array.from({ length: 10 }, (_, i) => limiter6.throttle(() => mockApiCall(i + 1, 100)) ); // Wait a bit then reset await new Promise(resolve => setTimeout(resolve, 50)); const queueBeforeReset = limiter6.getQueueLength(); limiter6.reset(); const queueAfterReset = limiter6.getQueueLength(); console.log(`โœ… Queue length before reset: ${queueBeforeReset}`); console.log(`โœ… Queue length after reset: ${queueAfterReset}`); console.log(` Reset cleared queue: ${queueAfterReset === 0 ? 'Yes' : 'No'}\n`); // Test 7: Error handling console.log('Test 7: Error handling'); const limiter7 = new RateLimiter(10); let errorCaught = false; try { await limiter7.throttle(async () => { throw new Error('Mock API error'); }); } catch (error) { errorCaught = true; } console.log(`โœ… Error properly propagated: ${errorCaught ? 'Yes' : 'No'}`); // Verify limiter still works after error const resultAfterError = await limiter7.throttle(() => mockApiCall(1)); console.log(`โœ… Limiter functional after error: ${resultAfterError.id === 1 ? 'Yes' : 'No'}\n`); // Test 8: Parallel execution within limit console.log('Test 8: Parallel execution within limit'); const limiter8 = new RateLimiter(10); const startTime8 = Date.now(); // Queue 10 requests that each take 100ms // With 10 req/sec limit, they should execute in parallel (not sequentially) const promises8 = Array.from({ length: 10 }, (_, i) => limiter8.throttle(() => mockApiCall(i + 1, 100)) ); await Promise.all(promises8); const duration8 = Date.now() - startTime8; console.log(`โœ… Completed 10 requests (100ms each) in ${duration8}ms`); console.log(` Executed in parallel: ${duration8 < 500 ? 'Yes' : 'No'}`); console.log(` (Sequential would take ~1000ms, parallel ~100ms)\n`); // Test 9: Stress test with many requests console.log('Test 9: Stress test (100 requests at 10 req/sec)'); const limiter9 = new RateLimiter(10); const startTime9 = Date.now(); const promises9 = Array.from({ length: 100 }, (_, i) => limiter9.throttle(() => mockApiCall(i + 1, 1)) ); await Promise.all(promises9); const duration9 = Date.now() - startTime9; console.log(`โœ… Completed 100 requests in ${duration9}ms`); console.log(` Expected: ~10000ms (100 requests รท 10 req/sec)`); console.log(` Within acceptable range: ${duration9 >= 9500 && duration9 <= 11000 ? 'Yes' : 'No'}\n`); // Test 10: Different rate limits console.log('Test 10: Custom rate limits'); const limiter10a = new RateLimiter(2); // 2 req/sec const limiter10b = new RateLimiter(20); // 20 req/sec const startTime10a = Date.now(); await Promise.all( Array.from({ length: 6 }, (_, i) => limiter10a.throttle(() => mockApiCall(i + 1, 1)) ) ); const duration10a = Date.now() - startTime10a; const startTime10b = Date.now(); await Promise.all( Array.from({ length: 40 }, (_, i) => limiter10b.throttle(() => mockApiCall(i + 1, 1)) ) ); const duration10b = Date.now() - startTime10b; console.log(`โœ… 6 requests at 2 req/sec: ${duration10a}ms (expected ~3000ms)`); console.log(`โœ… 40 requests at 20 req/sec: ${duration10b}ms (expected ~2000ms)`); console.log(` Both within acceptable ranges: ${ (duration10a >= 2700 && duration10a <= 3500) && (duration10b >= 1800 && duration10b <= 2500) ? 'Yes' : 'No' }\n`); console.log('๐ŸŽ‰ All rate limiter tests completed successfully!'); } catch (error) { console.error('โŒ Test failed with error:', error); throw error; } } // Run the tests testRateLimiter() .then(() => { console.log('\nโœ… Test script completed successfully'); process.exit(0); }) .catch((error) => { console.error('\nโŒ Test script failed:', error); process.exit(1); });