feat: general clean up and testing

This commit is contained in:
Ayobami
2025-08-04 13:16:53 +01:00
parent 43ae10d7dd
commit 3f8f456eef
3 changed files with 164 additions and 136 deletions
+1
View File
@@ -4,3 +4,4 @@
/tickets /tickets
/logs /logs
.env .env
*.log
+17 -18
View File
@@ -1,26 +1,26 @@
const redis = require('redis'); const redis = require("redis");
require('dotenv').config(); require("dotenv").config();
async function debugEvents() { async function debugEvents() {
const client = redis.createClient({ const client = redis.createClient({
url: process.env.REDIS_URL || 'redis://localhost:6379' url: process.env.REDIS_URL || "redis://localhost:6379",
}); });
try { try {
await client.connect(); await client.connect();
console.log('✅ Connected to Redis'); console.log("✅ Connected to Redis");
// Check what event keys exist // Check what event keys exist
const eventKeys = await client.keys('event:*'); const eventKeys = await client.keys("event:*");
console.log('\n📋 Found Redis keys:', eventKeys); console.log("\n Found Redis keys:", eventKeys);
// Check global stats // Check global stats
const globalStats = await client.hGetAll('global:stats'); const globalStats = await client.hGetAll("global:stats");
console.log('\n🌍 Global stats:', globalStats); console.log("\n Global stats:", globalStats);
// Check each event // Check each event
const metaKeys = eventKeys.filter(key => key.includes(':meta')); const metaKeys = eventKeys.filter((key) => key.includes(":meta"));
console.log('\n🎫 Event Details:'); console.log("\nEvent Details:");
for (const metaKey of metaKeys) { for (const metaKey of metaKeys) {
const eventId = metaKey.match(/event:(\d+):meta/)[1]; const eventId = metaKey.match(/event:(\d+):meta/)[1];
@@ -38,18 +38,17 @@ async function debugEvents() {
} }
// Test if we can check existence of event 5 // Test if we can check existence of event 5
const event5Exists = await client.exists('event:5:meta'); const event5Exists = await client.exists("event:5:meta");
console.log(`\n🔍 Event 5 exists: ${event5Exists ? 'YES' : 'NO'}`); console.log(`\n Event 5 exists: ${event5Exists ? "YES" : "NO"}`);
if (event5Exists) { if (event5Exists) {
const event5Meta = await client.hGetAll('event:5:meta'); const event5Meta = await client.hGetAll("event:5:meta");
const event5Tickets = await client.lLen('event:5:tickets'); const event5Tickets = await client.lLen("event:5:tickets");
console.log('📊 Event 5 details:', event5Meta); console.log("Event 5 details:", event5Meta);
console.log(`🎫 Event 5 remaining tickets: ${event5Tickets}`); console.log(`Event 5 remaining tickets: ${event5Tickets}`);
} }
} catch (error) { } catch (error) {
console.error('❌ Error:', error); console.error("❌ Error:", error);
} finally { } finally {
await client.disconnect(); await client.disconnect();
} }
+112 -84
View File
@@ -1,9 +1,9 @@
const autocannon = require('autocannon'); const autocannon = require("autocannon");
const { performance } = require('perf_hooks'); const { performance } = require("perf_hooks");
class LoadTester { class LoadTester {
constructor() { constructor() {
this.baseUrl = process.env.TEST_URL || 'http://localhost:3049'; this.baseUrl = process.env.TEST_URL || "http://localhost:3049";
this.results = []; this.results = [];
} }
@@ -12,18 +12,18 @@ class LoadTester {
url: `${this.baseUrl}/buy/${eventId}`, url: `${this.baseUrl}/buy/${eventId}`,
connections: 5000, connections: 5000,
duration: 30, duration: 30,
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
body: JSON.stringify({}), body: JSON.stringify({}),
...options ...options,
}; };
console.log(`\n🚀 Starting load test for Event ${eventId}`); console.log(`\n Starting load test for Event ${eventId}`);
console.log(`📊 Connections: ${defaultOptions.connections}`); console.log(`Connections: ${defaultOptions.connections}`);
console.log(`⏱️ Duration: ${defaultOptions.duration}s`); console.log(`Duration: ${defaultOptions.duration}s`);
console.log(`🎯 Target: ${defaultOptions.url}\n`); console.log(`Target: ${defaultOptions.url}\n`);
const startTime = performance.now(); const startTime = performance.now();
@@ -35,7 +35,7 @@ class LoadTester {
eventId, eventId,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
duration: endTime - startTime, duration: endTime - startTime,
...result ...result,
}; };
this.results.push(testResult); this.results.push(testResult);
@@ -43,18 +43,22 @@ class LoadTester {
return testResult; return testResult;
} catch (error) { } catch (error) {
console.error('❌ Load test failed:', error); console.error("❌ Load test failed:", error);
throw error; throw error;
} }
} }
async runMultiEventLoadTest(eventIds = [1, 2, 3], options = {}) { async runMultiEventLoadTest(eventIds = [1, 2, 3], options = {}) {
console.log(`\n🎯 Running multi-event load test for events: ${eventIds.join(', ')}`); console.log(
`\n Running multi-event load test for events: ${eventIds.join(", ")}`
);
const promises = eventIds.map(eventId => const promises = eventIds.map((eventId) =>
this.runPurchaseLoadTest(eventId, { this.runPurchaseLoadTest(eventId, {
connections: Math.floor((options.connections || 5000) / eventIds.length), connections: Math.floor(
duration: options.duration || 30 (options.connections || 5000) / eventIds.length
),
duration: options.duration || 30,
}) })
); );
@@ -63,7 +67,7 @@ class LoadTester {
this.printSummary(results); this.printSummary(results);
return results; return results;
} catch (error) { } catch (error) {
console.error('❌ Multi-event load test failed:', error); console.error("❌ Multi-event load test failed:", error);
throw error; throw error;
} }
} }
@@ -73,20 +77,20 @@ class LoadTester {
url: `${this.baseUrl}/health`, url: `${this.baseUrl}/health`,
connections: 100, connections: 100,
duration: 10, duration: 10,
...options ...options,
}; };
console.log('\n🏥 Running health check load test...'); console.log("\n Running health check load test...");
try { try {
const result = await autocannon(defaultOptions); const result = await autocannon(defaultOptions);
console.log(`✅ Health check test completed`); console.log(`✅ Health check test completed`);
console.log(`📈 RPS: ${result.requests.average}`); console.log(`RPS: ${result.requests.average}`);
console.log(`Latency: ${result.latency.average}ms`); console.log(`Latency: ${result.latency.average}ms`);
return result; return result;
} catch (error) { } catch (error) {
console.error('❌ Health check test failed:', error); console.error("❌ Health check test failed:", error);
throw error; throw error;
} }
} }
@@ -96,69 +100,87 @@ class LoadTester {
url: `${this.baseUrl}/metrics`, url: `${this.baseUrl}/metrics`,
connections: 50, connections: 50,
duration: 10, duration: 10,
...options ...options,
}; };
console.log('\n📊 Running metrics endpoint test...'); console.log("\nRunning metrics endpoint test...");
try { try {
const result = await autocannon(defaultOptions); const result = await autocannon(defaultOptions);
console.log(`✅ Metrics test completed`); console.log(`✅ Metrics test completed`);
console.log(`📈 RPS: ${result.requests.average}`); console.log(`RPS: ${result.requests.average}`);
console.log(`Latency: ${result.latency.average}ms`); console.log(`Latency: ${result.latency.average}ms`);
return result; return result;
} catch (error) { } catch (error) {
console.error('❌ Metrics test failed:', error); console.error("Metrics test failed:", error);
throw error; throw error;
} }
} }
printResults(result) { printResults(result) {
console.log('📋 LOAD TEST RESULTS'); console.log("LOAD TEST RESULTS");
console.log('═'.repeat(50)); console.log("═".repeat(50));
console.log(`🎯 Event ID: ${result.eventId}`); console.log(`Event ID: ${result.eventId}`);
console.log(`⏱️ Duration: ${(result.duration / 1000).toFixed(2)}s`); console.log(`Duration: ${(result.duration / 1000).toFixed(2)}s`);
console.log(`📊 Total Requests: ${result.requests.total}`); console.log(`Total Requests: ${result.requests.total}`);
console.log(`📈 Requests/sec: ${result.requests.average.toFixed(2)}`); console.log(`Requests/sec: ${result.requests.average.toFixed(2)}`);
console.log(`Avg Latency: ${result.latency.average.toFixed(2)}ms`); console.log(`Avg Latency: ${result.latency.average.toFixed(2)}ms`);
console.log(`🔥 Max Latency: ${result.latency.max}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(
`✅ Success Rate: ${(
((result.requests.total - result.non2xx) / result.requests.total) *
100
).toFixed(2)}%`
);
console.log(`❌ Errors: ${result.non2xx}`); console.log(`❌ Errors: ${result.non2xx}`);
console.log(`🔗 Connections: ${result.connections}`); console.log(`Connections: ${result.connections}`);
console.log(`📦 Throughput: ${(result.throughput.average / 1024 / 1024).toFixed(2)} MB/s`); console.log(
console.log('═'.repeat(50)); `Throughput: ${(result.throughput.average / 1024 / 1024).toFixed(2)} MB/s`
);
console.log("═".repeat(50));
} }
printSummary(results) { printSummary(results) {
console.log('\n📊 MULTI-EVENT TEST SUMMARY'); console.log("\nMULTI-EVENT TEST SUMMARY");
console.log('═'.repeat(60)); console.log("═".repeat(60));
const totalRequests = results.reduce((sum, r) => sum + r.requests.total, 0); const totalRequests = results.reduce((sum, r) => sum + r.requests.total, 0);
const avgRPS = results.reduce((sum, r) => sum + r.requests.average, 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); const totalErrors = results.reduce((sum, r) => sum + r.non2xx, 0);
console.log(`🎯 Events Tested: ${results.length}`); console.log(`Events Tested: ${results.length}`);
console.log(`📊 Total Requests: ${totalRequests}`); console.log(`Total Requests: ${totalRequests}`);
console.log(`📈 Combined RPS: ${avgRPS.toFixed(2)}`); console.log(`Combined RPS: ${avgRPS.toFixed(2)}`);
console.log(`Avg Latency: ${avgLatency.toFixed(2)}ms`); console.log(`Avg Latency: ${avgLatency.toFixed(2)}ms`);
console.log(`✅ Overall Success Rate: ${((totalRequests - totalErrors) / totalRequests * 100).toFixed(2)}%`); console.log(
`✅ Overall Success Rate: ${(
((totalRequests - totalErrors) / totalRequests) *
100
).toFixed(2)}%`
);
console.log(`❌ Total Errors: ${totalErrors}`); console.log(`❌ Total Errors: ${totalErrors}`);
console.log('═'.repeat(60)); console.log("═".repeat(60));
// Individual event breakdown // Individual event breakdown
results.forEach((result, index) => { results.forEach((result, index) => {
console.log(`\n📋 Event ${result.eventId}:`); console.log(`\nEvent ${result.eventId}:`);
console.log(` 📈 RPS: ${result.requests.average.toFixed(2)}`); console.log(` RPS: ${result.requests.average.toFixed(2)}`);
console.log(` Latency: ${result.latency.average.toFixed(2)}ms`); 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(
` ✅ Success: ${(
((result.requests.total - result.non2xx) / result.requests.total) *
100
).toFixed(2)}%`
);
}); });
} }
async runFullTestSuite() { async runFullTestSuite() {
console.log('🚀 STARTING FULL LOAD TEST SUITE'); console.log("STARTING FULL LOAD TEST SUITE");
console.log('═'.repeat(60)); console.log("═".repeat(60));
try { try {
// 1. Health check test // 1. Health check test
@@ -170,20 +192,19 @@ class LoadTester {
// 3. Single event high-load test // 3. Single event high-load test
await this.runPurchaseLoadTest(1, { await this.runPurchaseLoadTest(1, {
connections: 5000, connections: 5000,
duration: 30 duration: 30,
}); });
// 4. Multi-event test // 4. Multi-event test
await this.runMultiEventLoadTest([1, 2, 3], { await this.runMultiEventLoadTest([1, 2, 3], {
connections: 6000, connections: 6000,
duration: 30 duration: 30,
}); });
console.log('\n🎉 FULL TEST SUITE COMPLETED!'); console.log("\n FULL TEST SUITE COMPLETED!");
console.log(`📊 Total tests run: ${this.results.length + 2}`); console.log(` Total tests run: ${this.results.length + 2}`);
} catch (error) { } catch (error) {
console.error('❌ Test suite failed:', error); console.error("❌ Test suite failed:", error);
process.exit(1); process.exit(1);
} }
} }
@@ -192,12 +213,12 @@ class LoadTester {
return this.results; return this.results;
} }
exportResults(filename = 'load-test-results.json') { exportResults(filename = "load-test-results.json") {
const fs = require('fs'); const fs = require("fs");
const data = { const data = {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
testSuite: 'Ticket Microservice Load Test', testSuite: "Ticket Microservice Load Test",
results: this.results results: this.results,
}; };
fs.writeFileSync(filename, JSON.stringify(data, null, 2)); fs.writeFileSync(filename, JSON.stringify(data, null, 2));
@@ -212,39 +233,46 @@ if (require.main === module) {
async function main() { async function main() {
try { try {
if (args.includes('--full')) { if (args.includes("--full")) {
await tester.runFullTestSuite(); await tester.runFullTestSuite();
} else if (args.includes('--event')) { } else if (args.includes("--event")) {
const eventId = parseInt(args[args.indexOf('--event') + 1]) || 1; const eventId = parseInt(args[args.indexOf("--event") + 1]) || 1;
const connections = parseInt(args[args.indexOf('--connections') + 1]) || 5000; const connections =
const duration = parseInt(args[args.indexOf('--duration') + 1]) || 30; parseInt(args[args.indexOf("--connections") + 1]) || 5000;
const duration = parseInt(args[args.indexOf("--duration") + 1]) || 30;
await tester.runPurchaseLoadTest(eventId, { connections, duration }); await tester.runPurchaseLoadTest(eventId, { connections, duration });
} else if (args.includes('--multi')) { } else if (args.includes("--multi")) {
const eventIds = args.includes('--events') const eventIds = args.includes("--events")
? args[args.indexOf('--events') + 1].split(',').map(Number) ? args[args.indexOf("--events") + 1].split(",").map(Number)
: [1, 2, 3]; : [1, 2, 3];
const connections = parseInt(args[args.indexOf('--connections') + 1]) || 6000; const connections =
const duration = parseInt(args[args.indexOf('--duration') + 1]) || 30; parseInt(args[args.indexOf("--connections") + 1]) || 6000;
const duration = parseInt(args[args.indexOf("--duration") + 1]) || 30;
await tester.runMultiEventLoadTest(eventIds, { connections, duration }); await tester.runMultiEventLoadTest(eventIds, { connections, duration });
} else { } else {
console.log('🎯 Ticket Microservice Load Tester'); console.log("Ticket Microservice Load Tester");
console.log('Usage:'); console.log("Usage:");
console.log(' node tests/load-test.js --full # Run full test suite'); console.log(
console.log(' node tests/load-test.js --event 1 --connections 5000 --duration 30'); " node tests/load-test.js --full # Run full test suite"
console.log(' node tests/load-test.js --multi --events 1,2,3 --connections 6000'); );
console.log(''); console.log(
console.log('Running default single event test...'); " 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); await tester.runPurchaseLoadTest(1);
} }
if (args.includes('--export')) { if (args.includes("--export")) {
tester.exportResults(); tester.exportResults();
} }
} catch (error) { } catch (error) {
console.error('Test execution failed:', error); console.error("Test execution failed:", error);
process.exit(1); process.exit(1);
} }
} }