92 lines
3.7 KiB
TypeScript
92 lines
3.7 KiB
TypeScript
import axios from 'axios';
|
|
|
|
const API_URL = process.env.TEST_API_URL || 'http://localhost:3200/api/geocoding/search';
|
|
const API_KEY = process.env.TEST_API_KEY || process.env.MAP_API_KEY || '';
|
|
const TOTAL_REQUESTS = 5000;
|
|
const CONCURRENCY = 50; // Number of parallel requests
|
|
|
|
const keywords = ["مستشفى", "شارع", "مدرسة", "جامعة", "مطعم", "فندق", "مسجد", "مقهى", "صيدلية", "مول", "دوار", "عيادة", "بنك", "مخبز"];
|
|
const names = ["السلام", "التخصصي", "المدينة", "النور", "اليرموك", "العربي", "الحديث", "الوطني", "المركزي", "الواحة", "الاموي", "المواساة"];
|
|
|
|
function generateRandomQuery(): string {
|
|
const keyword = keywords[Math.floor(Math.random() * keywords.length)];
|
|
const name = names[Math.floor(Math.random() * names.length)];
|
|
const randomSuffix = Math.random() > 0.5 ? ` ${Math.floor(Math.random() * 100)}` : '';
|
|
return `${keyword} ${name}${randomSuffix}`;
|
|
}
|
|
|
|
async function runLoadTest() {
|
|
console.log(`🚀 Starting Load Test: ${TOTAL_REQUESTS} requests with concurrency of ${CONCURRENCY}...`);
|
|
console.log(`📡 Target API: ${API_URL}\n`);
|
|
|
|
let completed = 0;
|
|
let successful = 0;
|
|
let failed = 0;
|
|
const latencies: number[] = [];
|
|
|
|
const startTime = Date.now();
|
|
|
|
// Worker function to process a chunk of requests
|
|
async function worker(requestsCount: number) {
|
|
for (let i = 0; i < requestsCount; i++) {
|
|
const query = generateRandomQuery();
|
|
const reqStart = Date.now();
|
|
try {
|
|
await axios.get(API_URL, {
|
|
headers: { 'x-api-key': API_KEY },
|
|
params: { q: query, country: 'syria' } // targeting syria since data exists there
|
|
});
|
|
successful++;
|
|
} catch (error: any) {
|
|
if (failed === 0) {
|
|
console.error('First failure reason:', error.response?.data || error.message);
|
|
}
|
|
failed++;
|
|
} finally {
|
|
latencies.push(Date.now() - reqStart);
|
|
completed++;
|
|
|
|
if (completed % 500 === 0) {
|
|
console.log(`⏳ Progress: ${completed}/${TOTAL_REQUESTS} requests completed...`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Split the total requests among the concurrent workers
|
|
const requestsPerWorker = Math.floor(TOTAL_REQUESTS / CONCURRENCY);
|
|
const workers: Promise<void>[] = [];
|
|
|
|
for (let i = 0; i < CONCURRENCY; i++) {
|
|
// The last worker takes any remainder
|
|
const count = (i === CONCURRENCY - 1) ? requestsPerWorker + (TOTAL_REQUESTS % CONCURRENCY) : requestsPerWorker;
|
|
workers.push(worker(count));
|
|
}
|
|
|
|
await Promise.all(workers);
|
|
|
|
const totalTimeSec = (Date.now() - startTime) / 1000;
|
|
const rps = TOTAL_REQUESTS / totalTimeSec;
|
|
|
|
latencies.sort((a, b) => a - b);
|
|
const p50 = latencies[Math.floor(latencies.length * 0.50)] || 0;
|
|
const p95 = latencies[Math.floor(latencies.length * 0.95)] || 0;
|
|
const p99 = latencies[Math.floor(latencies.length * 0.99)] || 0;
|
|
|
|
console.log('\n=======================================');
|
|
console.log('📊 LOAD TEST RESULTS');
|
|
console.log('=======================================');
|
|
console.log(`Total Requests : ${TOTAL_REQUESTS}`);
|
|
console.log(`Successful : ${successful} (${((successful/TOTAL_REQUESTS)*100).toFixed(1)}%)`);
|
|
console.log(`Failed : ${failed} (${((failed/TOTAL_REQUESTS)*100).toFixed(1)}%)`);
|
|
console.log(`Total Time : ${totalTimeSec.toFixed(2)} seconds`);
|
|
console.log(`Throughput (RPS): ${rps.toFixed(2)} req/sec`);
|
|
console.log('---------------------------------------');
|
|
console.log(`p50 Latency : ${p50} ms`);
|
|
console.log(`p95 Latency : ${p95} ms`);
|
|
console.log(`p99 Latency : ${p99} ms`);
|
|
console.log('=======================================\n');
|
|
}
|
|
|
|
runLoadTest().catch(console.error);
|