From 3f8f456eefb7e5cd613772bbc9c8c3e1b0669cb7 Mon Sep 17 00:00:00 2001 From: Ayobami Date: Mon, 4 Aug 2025 13:16:53 +0100 Subject: [PATCH] feat: general clean up and testing --- .gitignore | 3 +- debug-events.js | 55 +++++------ tests/load-test.js | 242 +++++++++++++++++++++++++-------------------- 3 files changed, 164 insertions(+), 136 deletions(-) diff --git a/.gitignore b/.gitignore index fa0ff74..d6387a2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /package-lock.json /tickets /logs -.env \ No newline at end of file +.env +*.log diff --git a/debug-events.js b/debug-events.js index b259d58..cd57339 100644 --- a/debug-events.js +++ b/debug-events.js @@ -1,34 +1,34 @@ -const redis = require('redis'); -require('dotenv').config(); +const redis = require("redis"); +require("dotenv").config(); async function debugEvents() { - const client = redis.createClient({ - url: process.env.REDIS_URL || 'redis://localhost:6379' + const client = redis.createClient({ + url: process.env.REDIS_URL || "redis://localhost:6379", }); - + try { await client.connect(); - console.log('āœ… Connected to Redis'); - + console.log("āœ… Connected to Redis"); + // Check what event keys exist - const eventKeys = await client.keys('event:*'); - console.log('\nšŸ“‹ Found Redis keys:', eventKeys); - + const eventKeys = await client.keys("event:*"); + console.log("\n Found Redis keys:", eventKeys); + // Check global stats - const globalStats = await client.hGetAll('global:stats'); - console.log('\nšŸŒ Global stats:', globalStats); - + const globalStats = await client.hGetAll("global:stats"); + console.log("\n Global stats:", globalStats); + // Check each event - const metaKeys = eventKeys.filter(key => key.includes(':meta')); - console.log('\nšŸŽ« Event Details:'); - + const metaKeys = eventKeys.filter((key) => key.includes(":meta")); + console.log("\nEvent Details:"); + for (const metaKey of metaKeys) { const eventId = metaKey.match(/event:(\d+):meta/)[1]; const ticketKey = `event:${eventId}:tickets`; - + const meta = await client.hGetAll(metaKey); const ticketCount = await client.lLen(ticketKey); - + console.log(`\n Event ${eventId}:`); console.log(` Name: ${meta.name}`); console.log(` Total Tickets: ${meta.totalTickets}`); @@ -36,20 +36,19 @@ async function debugEvents() { console.log(` Remaining: ${ticketCount}`); console.log(` Created: ${meta.createdAt}`); } - + // Test if we can check existence of event 5 - const event5Exists = await client.exists('event:5:meta'); - console.log(`\nšŸ” Event 5 exists: ${event5Exists ? 'YES' : 'NO'}`); - + const event5Exists = await client.exists("event:5:meta"); + console.log(`\n Event 5 exists: ${event5Exists ? "YES" : "NO"}`); + if (event5Exists) { - const event5Meta = await client.hGetAll('event:5:meta'); - const event5Tickets = await client.lLen('event:5:tickets'); - console.log('šŸ“Š Event 5 details:', event5Meta); - console.log(`šŸŽ« Event 5 remaining tickets: ${event5Tickets}`); + const event5Meta = await client.hGetAll("event:5:meta"); + const event5Tickets = await client.lLen("event:5:tickets"); + console.log("Event 5 details:", event5Meta); + console.log(`Event 5 remaining tickets: ${event5Tickets}`); } - } catch (error) { - console.error('āŒ Error:', error); + console.error("āŒ Error:", error); } finally { await client.disconnect(); } diff --git a/tests/load-test.js b/tests/load-test.js index 17991b3..50111c3 100644 --- a/tests/load-test.js +++ b/tests/load-test.js @@ -1,9 +1,9 @@ -const autocannon = require('autocannon'); -const { performance } = require('perf_hooks'); +const autocannon = require("autocannon"); +const { performance } = require("perf_hooks"); class LoadTester { constructor() { - this.baseUrl = process.env.TEST_URL || 'http://localhost:3049'; + this.baseUrl = process.env.TEST_URL || "http://localhost:3049"; this.results = []; } @@ -12,49 +12,53 @@ class LoadTester { url: `${this.baseUrl}/buy/${eventId}`, connections: 5000, duration: 30, - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, body: JSON.stringify({}), - ...options + ...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`); + 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 + ...result, }; this.results.push(testResult); this.printResults(testResult); - + return testResult; } catch (error) { - console.error('āŒ Load test failed:', 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 => + 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 + connections: Math.floor( + (options.connections || 5000) / eventIds.length + ), + duration: options.duration || 30, }) ); @@ -63,7 +67,7 @@ class LoadTester { this.printSummary(results); return results; } catch (error) { - console.error('āŒ Multi-event load test failed:', error); + console.error("āŒ Multi-event load test failed:", error); throw error; } } @@ -73,20 +77,20 @@ class LoadTester { url: `${this.baseUrl}/health`, connections: 100, duration: 10, - ...options + ...options, }; - console.log('\nšŸ„ Running health check load test...'); - + 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`); - + console.log(`RPS: ${result.requests.average}`); + console.log(`Latency: ${result.latency.average}ms`); + return result; } catch (error) { - console.error('āŒ Health check test failed:', error); + console.error("āŒ Health check test failed:", error); throw error; } } @@ -96,94 +100,111 @@ class LoadTester { url: `${this.baseUrl}/metrics`, connections: 50, duration: 10, - ...options + ...options, }; - console.log('\nšŸ“Š Running metrics endpoint test...'); - + 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`); - + console.log(`RPS: ${result.requests.average}`); + console.log(`Latency: ${result.latency.average}ms`); + return result; } catch (error) { - console.error('āŒ Metrics test failed:', 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("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)); + 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)); - + 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 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(`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)); - + 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)}%`); + 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)); - + 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 + await this.runPurchaseLoadTest(1, { + connections: 5000, + duration: 30, }); - + // 4. Multi-event test await this.runMultiEventLoadTest([1, 2, 3], { connections: 6000, - duration: 30 + duration: 30, }); - - console.log('\nšŸŽ‰ FULL TEST SUITE COMPLETED!'); - console.log(`šŸ“Š Total tests run: ${this.results.length + 2}`); - + + console.log("\n FULL TEST SUITE COMPLETED!"); + console.log(` Total tests run: ${this.results.length + 2}`); } catch (error) { - console.error('āŒ Test suite failed:', error); + console.error("āŒ Test suite failed:", error); process.exit(1); } } @@ -192,14 +213,14 @@ class LoadTester { return this.results; } - exportResults(filename = 'load-test-results.json') { - const fs = require('fs'); + exportResults(filename = "load-test-results.json") { + const fs = require("fs"); const data = { timestamp: new Date().toISOString(), - testSuite: 'Ticket Microservice Load Test', - results: this.results + testSuite: "Ticket Microservice Load Test", + results: this.results, }; - + fs.writeFileSync(filename, JSON.stringify(data, null, 2)); console.log(`šŸ“„ Results exported to: ${filename}`); } @@ -209,46 +230,53 @@ class LoadTester { if (require.main === module) { const args = process.argv.slice(2); const tester = new LoadTester(); - + async function main() { try { - if (args.includes('--full')) { + 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; - + } 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) + } 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; - + 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...'); + 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')) { + + if (args.includes("--export")) { tester.exportResults(); } - } catch (error) { - console.error('Test execution failed:', error); + console.error("Test execution failed:", error); process.exit(1); } } - + main(); }