/** * Test script for Phase 7: Advanced Rate Limiting + Header Redaction * Tests the new rate limiting system and header redaction functionality */ const http = require('http'); const BASE_URL = 'http://localhost:3333'; // Helper function to make HTTP requests function makeRequest(options, data = null) { return new Promise((resolve, reject) => { const req = http.request(options, (res) => { let body = ''; res.on('data', (chunk) => body += chunk); res.on('end', () => { try { const result = { statusCode: res.statusCode, headers: res.headers, body: res.headers['content-type'] && res.headers['content-type'].includes('application/json') ? JSON.parse(body) : body }; resolve(result); } catch (error) { resolve({ statusCode: res.statusCode, headers: res.headers, body: body }); } }); }); req.on('error', reject); if (data) { if (typeof data === 'string') { req.write(data); } else { req.write(JSON.stringify(data)); } } req.end(); }); } // Helper function to sleep function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function runTests() { console.log('๐Ÿงช Starting Phase 7: Advanced Rate Limiting + Header Redaction Tests\n'); let authToken = null; // Test 1: User Registration/Login console.log('1๏ธโƒฃ Testing user authentication...'); try { const loginResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v1/auth/login', method: 'POST', headers: { 'Content-Type': 'application/json', }, }, { email: 'rate-test@example.com', password: 'ratetest123' }); if (loginResult.statusCode === 200 && loginResult.body.success) { authToken = loginResult.body.data.token; console.log('โœ… User login successful'); } else { // Try to register if login fails const registerResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v1/auth/register', method: 'POST', headers: { 'Content-Type': 'application/json', }, }, { email: 'rate-test@example.com', name: 'Rate Test User', password: 'ratetest123', organizationName: 'Rate Test Org' }); if (registerResult.statusCode === 201 && registerResult.body.success) { authToken = registerResult.body.data.token; console.log('โœ… User registration successful'); } else { console.log('โŒ Authentication failed:', registerResult.body); return; } } } catch (error) { console.log('โŒ Authentication error:', error.message); return; } // Test 2: Rate Limit Headers console.log('\n2๏ธโƒฃ Testing rate limit headers...'); try { const trackResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, }, }, { url: 'https://example.com' }); console.log('Status Code:', trackResult.statusCode); console.log('Rate Limit Headers:'); console.log(' X-RateLimit-Limit:', trackResult.headers['x-ratelimit-limit']); console.log(' X-RateLimit-Remaining:', trackResult.headers['x-ratelimit-remaining']); console.log(' X-RateLimit-Reset:', trackResult.headers['x-ratelimit-reset']); console.log(' X-RateLimit-Tier:', trackResult.headers['x-ratelimit-tier']); if (trackResult.headers['x-ratelimit-limit']) { console.log('โœ… Rate limit headers present'); } else { console.log('โš ๏ธ Rate limit headers missing'); } } catch (error) { console.log('โŒ Rate limit headers test error:', error.message); } // Test 3: Anonymous vs Authenticated Rate Limits console.log('\n3๏ธโƒฃ Testing anonymous vs authenticated rate limits...'); try { // Anonymous request const anonResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', }, }, { url: 'https://httpbin.org/get' }); console.log('Anonymous Request:'); console.log(' Limit:', anonResult.headers['x-ratelimit-limit']); console.log(' Tier:', anonResult.headers['x-ratelimit-tier']); // Authenticated request const authResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, }, }, { url: 'https://httpbin.org/get' }); console.log('Authenticated Request:'); console.log(' Limit:', authResult.headers['x-ratelimit-limit']); console.log(' Tier:', authResult.headers['x-ratelimit-tier']); const anonLimit = parseInt(anonResult.headers['x-ratelimit-limit'] || '0'); const authLimit = parseInt(authResult.headers['x-ratelimit-limit'] || '0'); if (authLimit > anonLimit) { console.log('โœ… Authenticated users have higher rate limits'); } else { console.log('โš ๏ธ Rate limit difference not detected'); } } catch (error) { console.log('โŒ Rate limit comparison error:', error.message); } // Test 4: Legacy Endpoint Rate Limiting console.log('\n4๏ธโƒฃ Testing legacy endpoint rate limiting...'); try { const legacyResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/track', method: 'POST', headers: { 'Content-Type': 'application/json', }, }, { url: 'https://example.com' }); console.log('Legacy endpoint status:', legacyResult.statusCode); console.log('Legacy rate limit:', legacyResult.headers['x-ratelimit-limit']); if (legacyResult.headers['x-ratelimit-limit']) { console.log('โœ… Legacy endpoints have rate limiting'); } else { console.log('โš ๏ธ Legacy endpoints missing rate limiting'); } } catch (error) { console.log('โŒ Legacy rate limiting test error:', error.message); } // Test 5: Burst Protection console.log('\n5๏ธโƒฃ Testing burst protection...'); try { console.log('Making rapid authenticated requests...'); const requests = []; for (let i = 0; i < 15; i++) { requests.push( makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, }, }, { url: `https://httpbin.org/delay/0`, timeout: 5000 }) ); } const results = await Promise.allSettled(requests); const statusCodes = results.map(r => r.status === 'fulfilled' ? r.value.statusCode : 'error'); const rateLimited = statusCodes.filter(code => code === 429).length; const successful = statusCodes.filter(code => code === 200 || code === 201).length; console.log(`Results: ${successful} successful, ${rateLimited} rate limited`); if (rateLimited > 0) { console.log('โœ… Burst protection is working'); } else { console.log('โš ๏ธ Burst protection may not be active (or limits are very high)'); } } catch (error) { console.log('โŒ Burst protection test error:', error.message); } // Test 6: Header Redaction (indirect test via response logs) console.log('\n6๏ธโƒฃ Testing header redaction...'); try { const sensitiveHeadersResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, 'X-Secret-Key': 'secret123', 'Cookie': 'session=abc123', 'X-API-Key': 'api_key_12345', 'User-Agent': 'RedirectIntelligence Test Client v1.0', }, }, { url: 'https://httpbin.org/headers' }); console.log('Request with sensitive headers sent'); console.log('Status:', sensitiveHeadersResult.statusCode); // Check if the request was processed (indicating headers were handled properly) if (sensitiveHeadersResult.statusCode === 200 || sensitiveHeadersResult.statusCode === 201) { console.log('โœ… Request with sensitive headers processed successfully'); console.log(' (Header redaction occurs server-side in logs/storage)'); } else { console.log('โš ๏ธ Request with sensitive headers failed'); } } catch (error) { console.log('โŒ Header redaction test error:', error.message); } // Test 7: Bulk Rate Limiting console.log('\n7๏ธโƒฃ Testing bulk endpoint rate limiting...'); try { const bulkResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/bulk/jobs', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, }, }, { urls: [ { url: 'https://example.com', label: 'Test 1' } ] }); console.log('Bulk endpoint status:', bulkResult.statusCode); console.log('Bulk rate limit tier:', bulkResult.headers['x-ratelimit-tier']); console.log('Bulk rate limit:', bulkResult.headers['x-ratelimit-limit']); if (bulkResult.headers['x-ratelimit-tier']) { console.log('โœ… Bulk endpoints have tier-based rate limiting'); } else { console.log('โš ๏ธ Bulk rate limiting not detected'); } } catch (error) { console.log('โŒ Bulk rate limiting test error:', error.message); } // Test 8: Rate Limit Exceeded Response console.log('\n8๏ธโƒฃ Testing rate limit exceeded response...'); try { console.log('Making requests to approach rate limit...'); let lastResponse = null; // Make many requests quickly to trigger rate limiting for (let i = 0; i < 60; i++) { try { const response = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/track', // Use legacy endpoint for predictable low limits method: 'POST', headers: { 'Content-Type': 'application/json', }, }, { url: 'https://httpbin.org/get' }); lastResponse = response; if (response.statusCode === 429) { console.log('โœ… Rate limit exceeded (429) response received'); console.log('Response body:', response.body); console.log('Retry-After header:', response.headers['retry-after']); console.log('X-RateLimit headers:', { limit: response.headers['x-ratelimit-limit'], remaining: response.headers['x-ratelimit-remaining'], reset: response.headers['x-ratelimit-reset'], }); break; } // Small delay to avoid overwhelming the server await sleep(50); } catch (error) { console.log('Request error (expected for rate limiting):', error.message); } } if (!lastResponse || lastResponse.statusCode !== 429) { console.log('โš ๏ธ Rate limit not triggered (limits may be too high for testing)'); } } catch (error) { console.log('โŒ Rate limit exceeded test error:', error.message); } // Test 9: Different User Agent Handling console.log('\n9๏ธโƒฃ Testing different user agent handling...'); try { const userAgents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'curl/7.68.0', 'RedirectIntelligence/2.0 TestClient', ]; for (const ua of userAgents) { const result = await makeRequest({ hostname: 'localhost', port: 3333, path: '/api/v2/track', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}`, 'User-Agent': ua, }, }, { url: 'https://httpbin.org/user-agent' }); console.log(`User-Agent: ${ua.substring(0, 30)}... -> Status: ${result.statusCode}`); } console.log('โœ… User-Agent handling test completed'); } catch (error) { console.log('โŒ User-Agent test error:', error.message); } // Test 10: Health Check (should not be rate limited) console.log('\n๐Ÿ”Ÿ Testing health check endpoint...'); try { const healthResult = await makeRequest({ hostname: 'localhost', port: 3333, path: '/health', method: 'GET', }); console.log('Health check status:', healthResult.statusCode); console.log('Health check rate limited:', !!healthResult.headers['x-ratelimit-limit']); if (healthResult.statusCode === 200 && !healthResult.headers['x-ratelimit-limit']) { console.log('โœ… Health check endpoint is not rate limited'); } else { console.log('โš ๏ธ Health check endpoint may have rate limiting'); } } catch (error) { console.log('โŒ Health check test error:', error.message); } console.log('\n๐ŸŽ‰ Phase 7 testing completed!'); console.log('\nKey features tested:'); console.log('โœ“ Advanced rate limiting with user tiers'); console.log('โœ“ Rate limit headers in responses'); console.log('โœ“ Anonymous vs authenticated rate limits'); console.log('โœ“ Legacy endpoint rate limiting'); console.log('โœ“ Burst protection for rapid requests'); console.log('โœ“ Header redaction (server-side)'); console.log('โœ“ Bulk endpoint tier-based limiting'); console.log('โœ“ Rate limit exceeded responses'); console.log('โœ“ User-Agent handling'); console.log('โœ“ Health check endpoint exclusion'); } // Error handling process.on('uncaughtException', (error) => { console.log('\n๐Ÿ’ฅ Uncaught Exception:', error.message); process.exit(1); }); process.on('unhandledRejection', (reason) => { console.log('\n๐Ÿ’ฅ Unhandled Rejection:', reason); process.exit(1); }); // Run tests runTests().catch(error => { console.log('\n๐Ÿ’ฅ Test execution failed:', error.message); process.exit(1); });