import { io } from 'socket.io-client'; const API_URL = process.env.API_URL || 'http://localhost:4010/api'; const TENANT_ID = process.env.TENANT_ID || 'siro'; const NUM_DRIVERS = parseInt(process.env.NUM_DRIVERS || '100', 10); const TRIP_RATE_SEC = parseInt(process.env.TRIP_RATE_SEC || '2', 10); const DRIVER_PING_INTERVAL_MS = parseInt(process.env.DRIVER_PING_INTERVAL_MS || '3000', 10); async function api(method, path, body = null, token = null) { const options = { method, headers: { 'Content-Type': 'application/json', 'x-tenant-id': TENANT_ID, 'x-device-id': 'stress-test-device' } }; if (token) options.headers['Authorization'] = `Bearer ${token}`; if (body) options.body = JSON.stringify(body); const res = await fetch(`${API_URL}${path}`, options); const data = await res.json().catch(() => null); if (!res.ok) { throw new Error(`API Error: ${method} ${path} -> ${res.status} : ${JSON.stringify(data)}`); } return data; } let drivers = []; let reqCount = 0; let errors = 0; let tripsCompleted = 0; async function setupDriver(index) { const phone = `+96279${String(index).padStart(6, '0')}`; try { // Authenticate using dev bypass const auth = await api('POST', '/auth/verify-otp', { phone, code: '1234' }); const token = auth.access_token; // Auto-create driver profile if missing if (!auth.user.vehicle_plate) { await api('POST', '/drivers/apply', { service_class: 'economy', vehicle_make: 'Toyota', vehicle_model: 'Corolla', vehicle_plate: `${index}-TEST` }, token).catch(() => {}); } // Go online await api('PATCH', '/drivers/status', { online: true }, token); const WS_URL = API_URL.replace('/api', ''); const socket = io(WS_URL, { extraHeaders: { Authorization: `Bearer ${token}` }, reconnection: true }); const driverObj = { phone, token, socket, available: true }; drivers.push(driverObj); // Initial random location in Amman let lat = 31.95 + (Math.random() * 0.1); let lng = 35.91 + (Math.random() * 0.1); // Start pinging location setInterval(() => { lat += 0.0001; // Move slightly socket.emit('driver:location', { lat, lng, speed: 40, heading: 90 }); reqCount++; }, DRIVER_PING_INTERVAL_MS); // Listen for trip offers socket.on('trip:offer', async (data) => { if (!driverObj.available) return; driverObj.available = false; const tripId = data.tripId || data.id; try { await api('POST', `/trips/${tripId}/accept`, null, token); // Simulate trip flow timeline (shortened for stress test) setTimeout(async () => { await api('PATCH', `/trips/${tripId}/status`, { status: 'driver_arrived' }, token).catch(()=>{}); await api('PATCH', `/trips/${tripId}/status`, { status: 'in_progress' }, token).catch(()=>{}); await api('PATCH', `/trips/${tripId}/status`, { status: 'completed' }, token).catch(()=>{}); driverObj.available = true; // Free to take next trip tripsCompleted++; }, 5000); } catch (err) { driverObj.available = true; } }); } catch (err) { errors++; } } async function spawnRiderAndRequestTrip(index) { const phone = `+96278${String(index).padStart(6, '0')}`; try { const auth = await api('POST', '/auth/verify-otp', { phone, code: '1234' }); const token = auth.access_token; // Pick a location roughly where drivers are spawned const lat = 31.95 + (Math.random() * 0.1); const lng = 35.91 + (Math.random() * 0.1); await api('POST', '/trips', { city: 'amman', service_class: 'economy', origin: { lat, lng }, destination: { lat: lat + 0.05, lng: lng + 0.05 } }, token); reqCount++; } catch (err) { errors++; } } async function start() { console.log(`\x1b[36m==================================================\x1b[0m`); console.log(`🚀 Starting Stress Test`); console.log(`\x1b[33mDrivers:\x1b[0m ${NUM_DRIVERS}`); console.log(`\x1b[33mTrips/Sec:\x1b[0m ${TRIP_RATE_SEC}`); console.log(`\x1b[33mLocation Ping Interval:\x1b[0m ${DRIVER_PING_INTERVAL_MS}ms`); console.log(`\x1b[36m==================================================\x1b[0m\n`); // Throttle driver creation so we don't spam the DB at startup for (let i = 0; i < NUM_DRIVERS; i++) { setupDriver(i); if (i % 10 === 0) await new Promise(r => setTimeout(r, 100)); } console.log(`[System] Finished spawning drivers. Waiting 5s for sockets...`); await new Promise(r => setTimeout(r, 5000)); console.log(`[System] Commencing trip requests...`); let riderIndex = 0; setInterval(() => { for (let i = 0; i < TRIP_RATE_SEC; i++) { spawnRiderAndRequestTrip(riderIndex++); } }, 1000); // Monitor performance locally setInterval(() => { const currentRps = reqCount / 5; console.log(`\x1b[32m[Metrics]\x1b[0m Active Drivers: ${drivers.length} | API/WS Requests/sec: ${currentRps.toFixed(1)} | Trips Completed: ${tripsCompleted} | Errors: ${errors}`); reqCount = 0; }, 5000); } start();