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("\nRunning 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("\nMULTI-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(`\nEvent ${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;