wulf-pulse/dev/test-rate-limiter.ts
root 6eee14f8af Add comprehensive admin features and multi-system integration
- Add admin dashboard with sync controls and data browser
- Implement RMM, Auvik, and Addigy organization mappings
- Add chunked ticket sync with progress tracking
- Implement entity sync service with rate limiting
- Add analytics engine and performance optimizer
- Create data browser for all PSA entities
- Add navigation components and UI improvements
- Implement background processing and sync services
- Add comprehensive documentation and migration scripts
- Update configuration items with multi-system support
- Enhance contact management and purchase history
- Add issue type assignment and LLM analyzer
- Improve error handling and logging utilities
2025-11-19 14:18:16 -05:00

246 lines
8.8 KiB
TypeScript

/**
* 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);
});