const autocannon = require('autocannon'); const { performance } = require('perf_hooks'); class LoadTester { constructor() { this.baseUrl = process.env.TEST_URL || 'http://localhost:3049'; this.results = []; } async runPurchaseLoadTest(eventId = 1, options = {}) { const defaultOptions = { url: `${this.baseUrl}/buy/${eventId}`, connections: 5000, duration: 30, method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), ...options }; console.log(`\nšŸš€ Starting load test for Event ${eventId}`); console.log(`šŸ“Š Connections: ${defaultOptions.connections}`); console.log(`ā±ļø Duration: ${defaultOptions.duration}s`); console.log(`šŸŽÆ Target: ${defaultOptions.url}\n`); const startTime = performance.now(); try { const result = await autocannon(defaultOptions); const endTime = performance.now(); const testResult = { eventId, timestamp: new Date().toISOString(), duration: endTime - startTime, ...result }; this.results.push(testResult); this.printResults(testResult); return testResult; } catch (error) { console.error('āŒ Load test failed:', error); throw error; } } async runMultiEventLoadTest(eventIds = [1, 2, 3], options = {}) { console.log(`\nšŸŽÆ Running multi-event load test for events: ${eventIds.join(', ')}`); const promises = eventIds.map(eventId => this.runPurchaseLoadTest(eventId, { connections: Math.floor((options.connections || 5000) / eventIds.length), duration: options.duration || 30 }) ); try { const results = await Promise.all(promises); this.printSummary(results); return results; } catch (error) { console.error('āŒ Multi-event load test failed:', error); throw error; } } async runHealthCheckTest(options = {}) { const defaultOptions = { url: `${this.baseUrl}/health`, connections: 100, duration: 10, ...options }; console.log('\nšŸ„ Running health check load test...'); try { const result = await autocannon(defaultOptions); console.log(`āœ… Health check test completed`); console.log(`šŸ“ˆ RPS: ${result.requests.average}`); console.log(`⚔ Latency: ${result.latency.average}ms`); return result; } catch (error) { console.error('āŒ Health check test failed:', error); throw error; } } async runMetricsTest(options = {}) { const defaultOptions = { url: `${this.baseUrl}/metrics`, connections: 50, duration: 10, ...options }; console.log('\nšŸ“Š Running metrics endpoint test...'); try { const result = await autocannon(defaultOptions); console.log(`āœ… Metrics test completed`); console.log(`šŸ“ˆ RPS: ${result.requests.average}`); console.log(`⚔ Latency: ${result.latency.average}ms`); return result; } catch (error) { console.error('āŒ Metrics test failed:', error); throw error; } } printResults(result) { console.log('šŸ“‹ LOAD TEST RESULTS'); console.log('═'.repeat(50)); console.log(`šŸŽÆ Event ID: ${result.eventId}`); console.log(`ā±ļø Duration: ${(result.duration / 1000).toFixed(2)}s`); console.log(`šŸ“Š Total Requests: ${result.requests.total}`); console.log(`šŸ“ˆ Requests/sec: ${result.requests.average.toFixed(2)}`); console.log(`⚔ Avg Latency: ${result.latency.average.toFixed(2)}ms`); console.log(`šŸ”„ Max Latency: ${result.latency.max}ms`); console.log(`āœ… Success Rate: ${((result.requests.total - result.non2xx) / result.requests.total * 100).toFixed(2)}%`); console.log(`āŒ Errors: ${result.non2xx}`); console.log(`šŸ”— Connections: ${result.connections}`); console.log(`šŸ“¦ Throughput: ${(result.throughput.average / 1024 / 1024).toFixed(2)} MB/s`); console.log('═'.repeat(50)); } printSummary(results) { console.log('\nšŸ“Š MULTI-EVENT TEST SUMMARY'); console.log('═'.repeat(60)); const totalRequests = results.reduce((sum, r) => sum + r.requests.total, 0); const avgRPS = results.reduce((sum, r) => sum + r.requests.average, 0); const avgLatency = results.reduce((sum, r) => sum + r.latency.average, 0) / results.length; const totalErrors = results.reduce((sum, r) => sum + r.non2xx, 0); console.log(`šŸŽÆ Events Tested: ${results.length}`); console.log(`šŸ“Š Total Requests: ${totalRequests}`); console.log(`šŸ“ˆ Combined RPS: ${avgRPS.toFixed(2)}`); console.log(`⚔ Avg Latency: ${avgLatency.toFixed(2)}ms`); console.log(`āœ… Overall Success Rate: ${((totalRequests - totalErrors) / totalRequests * 100).toFixed(2)}%`); console.log(`āŒ Total Errors: ${totalErrors}`); console.log('═'.repeat(60)); // Individual event breakdown results.forEach((result, index) => { console.log(`\nšŸ“‹ Event ${result.eventId}:`); console.log(` šŸ“ˆ RPS: ${result.requests.average.toFixed(2)}`); console.log(` ⚔ Latency: ${result.latency.average.toFixed(2)}ms`); console.log(` āœ… Success: ${((result.requests.total - result.non2xx) / result.requests.total * 100).toFixed(2)}%`); }); } async runFullTestSuite() { console.log('šŸš€ STARTING FULL LOAD TEST SUITE'); console.log('═'.repeat(60)); try { // 1. Health check test await this.runHealthCheckTest(); // 2. Metrics test await this.runMetricsTest(); // 3. Single event high-load test await this.runPurchaseLoadTest(1, { connections: 5000, duration: 30 }); // 4. Multi-event test await this.runMultiEventLoadTest([1, 2, 3], { connections: 6000, duration: 30 }); console.log('\nšŸŽ‰ FULL TEST SUITE COMPLETED!'); console.log(`šŸ“Š Total tests run: ${this.results.length + 2}`); } catch (error) { console.error('āŒ Test suite failed:', error); process.exit(1); } } getResults() { return this.results; } exportResults(filename = 'load-test-results.json') { const fs = require('fs'); const data = { timestamp: new Date().toISOString(), testSuite: 'Ticket Microservice Load Test', results: this.results }; fs.writeFileSync(filename, JSON.stringify(data, null, 2)); console.log(`šŸ“„ Results exported to: ${filename}`); } } // CLI execution if (require.main === module) { const args = process.argv.slice(2); const tester = new LoadTester(); async function main() { try { if (args.includes('--full')) { await tester.runFullTestSuite(); } else if (args.includes('--event')) { const eventId = parseInt(args[args.indexOf('--event') + 1]) || 1; const connections = parseInt(args[args.indexOf('--connections') + 1]) || 5000; const duration = parseInt(args[args.indexOf('--duration') + 1]) || 30; await tester.runPurchaseLoadTest(eventId, { connections, duration }); } else if (args.includes('--multi')) { const eventIds = args.includes('--events') ? args[args.indexOf('--events') + 1].split(',').map(Number) : [1, 2, 3]; const connections = parseInt(args[args.indexOf('--connections') + 1]) || 6000; const duration = parseInt(args[args.indexOf('--duration') + 1]) || 30; await tester.runMultiEventLoadTest(eventIds, { connections, duration }); } else { console.log('šŸŽÆ Ticket Microservice Load Tester'); console.log('Usage:'); console.log(' node tests/load-test.js --full # Run full test suite'); console.log(' node tests/load-test.js --event 1 --connections 5000 --duration 30'); console.log(' node tests/load-test.js --multi --events 1,2,3 --connections 6000'); console.log(''); console.log('Running default single event test...'); await tester.runPurchaseLoadTest(1); } if (args.includes('--export')) { tester.exportResults(); } } catch (error) { console.error('Test execution failed:', error); process.exit(1); } } main(); } module.exports = LoadTester;