From 7e06b1241f89022093438959284ffe4c8a838ad7 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 19 Jul 2026 01:51:47 +0300 Subject: [PATCH] feat: add automated stress testing script for driver location tracking and trip lifecycle simulation --- backend/e2e-tester/stress_test.js | 155 ++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 backend/e2e-tester/stress_test.js diff --git a/backend/e2e-tester/stress_test.js b/backend/e2e-tester/stress_test.js new file mode 100644 index 0000000..a50de82 --- /dev/null +++ b/backend/e2e-tester/stress_test.js @@ -0,0 +1,155 @@ +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();