feat: general clean up and testing
This commit is contained in:
+2
-1
@@ -3,4 +3,5 @@
|
|||||||
/package-lock.json
|
/package-lock.json
|
||||||
/tickets
|
/tickets
|
||||||
/logs
|
/logs
|
||||||
.env
|
.env
|
||||||
|
*.log
|
||||||
|
|||||||
+27
-28
@@ -1,34 +1,34 @@
|
|||||||
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];
|
||||||
const ticketKey = `event:${eventId}:tickets`;
|
const ticketKey = `event:${eventId}:tickets`;
|
||||||
|
|
||||||
const meta = await client.hGetAll(metaKey);
|
const meta = await client.hGetAll(metaKey);
|
||||||
const ticketCount = await client.lLen(ticketKey);
|
const ticketCount = await client.lLen(ticketKey);
|
||||||
|
|
||||||
console.log(`\n Event ${eventId}:`);
|
console.log(`\n Event ${eventId}:`);
|
||||||
console.log(` Name: ${meta.name}`);
|
console.log(` Name: ${meta.name}`);
|
||||||
console.log(` Total Tickets: ${meta.totalTickets}`);
|
console.log(` Total Tickets: ${meta.totalTickets}`);
|
||||||
@@ -36,20 +36,19 @@ async function debugEvents() {
|
|||||||
console.log(` Remaining: ${ticketCount}`);
|
console.log(` Remaining: ${ticketCount}`);
|
||||||
console.log(` Created: ${meta.createdAt}`);
|
console.log(` Created: ${meta.createdAt}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
|
|||||||
+135
-107
@@ -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,49 +12,53 @@ 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();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await autocannon(defaultOptions);
|
const result = await autocannon(defaultOptions);
|
||||||
const endTime = performance.now();
|
const endTime = performance.now();
|
||||||
|
|
||||||
const testResult = {
|
const testResult = {
|
||||||
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);
|
||||||
this.printResults(testResult);
|
this.printResults(testResult);
|
||||||
|
|
||||||
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,94 +100,111 @@ 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
|
||||||
await this.runHealthCheckTest();
|
await this.runHealthCheckTest();
|
||||||
|
|
||||||
// 2. Metrics test
|
// 2. Metrics test
|
||||||
await this.runMetricsTest();
|
await this.runMetricsTest();
|
||||||
|
|
||||||
// 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,14 +213,14 @@ 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));
|
||||||
console.log(`📄 Results exported to: ${filename}`);
|
console.log(`📄 Results exported to: ${filename}`);
|
||||||
}
|
}
|
||||||
@@ -209,46 +230,53 @@ class LoadTester {
|
|||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const tester = new LoadTester();
|
const tester = new LoadTester();
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user