diff --git a/backend/e2e-tester/simulation.js b/backend/e2e-tester/simulation.js index cadf1b3..0956faa 100644 --- a/backend/e2e-tester/simulation.js +++ b/backend/e2e-tester/simulation.js @@ -2,7 +2,6 @@ import readline from 'readline'; import { io } from 'socket.io-client'; const API_URL = process.env.API_URL || 'http://localhost:4010/api'; -// slug المستأجر — «siro» هو المزروع على السيرفر (راجع لوق [Seed] عند الإقلاع). const TENANT_ID = process.env.TENANT_ID || 'siro'; async function api(method, path, body = null, token = null) { @@ -15,127 +14,147 @@ async function api(method, path, body = null, token = null) { } }; - if (token) { - options.headers['Authorization'] = `Bearer ${token}`; - } - - if (body) { - options.body = JSON.stringify(body); - } + 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) { - console.error(`\x1b[31m[API Error] ${method} ${path} -> ${res.status}\x1b[0m`, data); + console.error(`\n\x1b[31m[API Error] ${method} ${path} -> ${res.status}\x1b[0m`); + console.error(JSON.stringify(data, null, 2)); throw new Error(`API Error: ${res.status}`); } return data; } async function run() { - console.log('\n=========================================='); - console.log('🚀 Tripz Backend Automated E2E Simulation'); - console.log('==========================================\n'); + try { + console.log('\n=========================================='); + console.log('🚀 Tripz Backend Automated E2E Simulation'); + console.log('==========================================\n'); - // 1. Authenticate Demo Driver - const driverPhone = '+962790000001'; - console.log(`[Driver] Authenticating Demo Driver (${driverPhone})...`); - await api('POST', '/auth/send-otp', { phone: driverPhone }); - const driverAuth = await api('POST', '/auth/verify-otp', { phone: driverPhone, code: '1234' }); - const driverToken = driverAuth.access_token; - console.log('\x1b[32m[Driver] Authenticated successfully!\x1b[0m\n'); + // 1. Authenticate Demo Driver + const driverPhone = '+962790000001'; + console.log(`[Driver] Authenticating Demo Driver (${driverPhone})...`); + await api('POST', '/auth/send-otp', { phone: driverPhone }); + const driverAuth = await api('POST', '/auth/verify-otp', { phone: driverPhone, code: '1234' }); + const driverToken = driverAuth.access_token; + console.log('\x1b[32m[Driver] Authenticated successfully!\x1b[0m\n'); - // 2. Authenticate Demo Rider - const riderPhone = '+962790000002'; - console.log(`[Rider] Authenticating Demo Rider (${riderPhone})...`); - await api('POST', '/auth/send-otp', { phone: riderPhone }); - const riderAuth = await api('POST', '/auth/verify-otp', { phone: riderPhone, code: '1234' }); - const riderToken = riderAuth.access_token; - console.log('\x1b[32m[Rider] Authenticated successfully!\x1b[0m\n'); + // 2. Authenticate Demo Rider + const riderPhone = '+962790000002'; + console.log(`[Rider] Authenticating Demo Rider (${riderPhone})...`); + await api('POST', '/auth/send-otp', { phone: riderPhone }); + const riderAuth = await api('POST', '/auth/verify-otp', { phone: riderPhone, code: '1234' }); + const riderToken = riderAuth.access_token; + console.log('\x1b[32m[Rider] Authenticated successfully!\x1b[0m\n'); - // 3. Setup Driver Profile & Go Online - console.log(`[Driver] Creating Driver profile...`); - await api('POST', '/drivers/apply', { service_class: 'economy', vehicle_make: 'Toyota', vehicle_model: 'Prius', vehicle_plate: '12-3456' }, driverToken); - - console.log(`[Driver] Going Online...`); - await api('PATCH', '/drivers/status', { online: true }, driverToken); - console.log('\x1b[32m[Driver] Profile created and is now ONLINE.\x1b[0m\n'); + // 3. Setup Driver Profile & Go Online + console.log(`[Driver] Creating Driver profile...`); + await api('POST', '/drivers/apply', { service_class: 'economy', vehicle_make: 'Toyota', vehicle_model: 'Prius', vehicle_plate: '12-3456' }, driverToken); + + console.log(`[Driver] Going Online...`); + await api('PATCH', '/drivers/status', { online: true }, driverToken); + console.log('\x1b[32m[Driver] Profile created and is now ONLINE.\x1b[0m\n'); - // 4. Connect WebSockets - console.log(`[Sockets] Connecting Rider and Driver via WebSockets...`); - const WS_URL = API_URL.replace('/api', ''); - const driverSocket = io(WS_URL, { extraHeaders: { Authorization: `Bearer ${driverToken}` } }); - const riderSocket = io(WS_URL, { extraHeaders: { Authorization: `Bearer ${riderToken}` } }); + // 4. Connect WebSockets + console.log(`[Sockets] Connecting Rider and Driver via WebSockets...`); + const WS_URL = API_URL.replace('/api', ''); + const driverSocket = io(WS_URL, { extraHeaders: { Authorization: `Bearer ${driverToken}` } }); + const riderSocket = io(WS_URL, { extraHeaders: { Authorization: `Bearer ${riderToken}` } }); - let tripId = null; + let tripId = null; - driverSocket.on('connect', () => console.log('\x1b[36m[Driver WS] Connected to Gateway\x1b[0m')); - riderSocket.on('connect', () => console.log('\x1b[36m[Rider WS] Connected to Gateway\x1b[0m')); + // Sockets Error Logging + driverSocket.on('connect_error', (err) => console.error(`\x1b[31m[Driver WS] Error: ${err.message}\x1b[0m`)); + riderSocket.on('connect_error', (err) => console.error(`\x1b[31m[Rider WS] Error: ${err.message}\x1b[0m`)); - driverSocket.on('trip:dispatch', async (data) => { - console.log(`\n\x1b[35m[Driver WS] 🔔 RECEIVED DISPATCH for Trip ${data.id}!\x1b[0m`); - tripId = data.id; + driverSocket.on('connect', () => console.log('\x1b[36m[Driver WS] Connected to Gateway\x1b[0m')); + riderSocket.on('connect', () => console.log('\x1b[36m[Rider WS] Connected to Gateway\x1b[0m')); - console.log(`[Driver] Accepting Trip...`); - await api('POST', `/trips/${tripId}/accept`, null, driverToken); - console.log(`\x1b[32m[Driver] Trip Accepted successfully!\x1b[0m`); - }); + driverSocket.on('trip:dispatch', async (data) => { + console.log(`\n\x1b[35m[Driver WS] 🔔 RECEIVED DISPATCH for Trip ${data.tripId || data.id}!\x1b[0m`); + console.log(`Dispatch Payload:`, JSON.stringify(data, null, 2)); + tripId = data.tripId || data.id; - riderSocket.on('trip:updated', (data) => { - console.log(`\x1b[33m[Rider WS] Trip status updated: ${data.status}\x1b[0m`); - }); + try { + console.log(`[Driver] Accepting Trip...`); + await api('POST', `/trips/${tripId}/accept`, null, driverToken); + console.log(`\x1b[32m[Driver] Trip Accepted successfully!\x1b[0m`); + } catch (err) { + console.error(`\x1b[31m[Driver] Failed to accept trip!\x1b[0m`); + } + }); - await new Promise(r => setTimeout(r, 2000)); + riderSocket.on('trip:updated', (data) => { + console.log(`\x1b[33m[Rider WS] Trip status updated: ${data.status}\x1b[0m`); + }); - // 5. Driver Location Pings - let driverLat = 32.0645; // Zarqa Coordinates - let driverLng = 36.0827; + await new Promise(r => setTimeout(r, 2000)); - const locationInterval = setInterval(() => { - // Driver moves slightly - driverLat += 0.0001; + // 5. Driver Location Pings + let driverLat = 32.0645; + let driverLng = 36.0827; + + const locationInterval = setInterval(() => { + driverLat += 0.0001; + driverSocket.emit('driver:location', { lat: driverLat, lng: driverLng, speed: 40, heading: 90 }); + console.log(`[Driver WS] 📍 Location ping: ${driverLat.toFixed(5)}, ${driverLng.toFixed(5)}`); + }, 3000); + + // Send first ping IMMEDIATELY driverSocket.emit('driver:location', { lat: driverLat, lng: driverLng, speed: 40, heading: 90 }); - console.log(`[Driver WS] 📍 Location ping: ${driverLat.toFixed(5)}, ${driverLng.toFixed(5)}`); - }, 3000); + console.log(`[Driver WS] 📍 Initial Location ping: ${driverLat.toFixed(5)}, ${driverLng.toFixed(5)}`); + + // Wait for the server to process the location update into Redis + await new Promise(r => setTimeout(r, 1500)); - // 6. Rider Requests Trip - console.log(`\n[Rider] Requesting Trip from Zarqa...`); - const tripRequest = await api('POST', '/trips', { - city: 'amman', - service_class: 'economy', - origin: { lat: 32.0650, lng: 36.0830 }, // Zarqa origin - destination: { lat: 32.0700, lng: 36.0900 } - }, riderToken); + // 6. Rider Requests Trip + console.log(`\n[Rider] Requesting Trip from Zarqa...`); + const tripRequest = await api('POST', '/trips', { + city: 'amman', + service_class: 'economy', + origin: { lat: 32.0650, lng: 36.0830 }, + destination: { lat: 32.0700, lng: 36.0900 } + }, riderToken); - console.log(`\x1b[32m[Rider] Trip created: ${tripRequest.trip.id}\x1b[0m`); + console.log(`\x1b[32m[Rider] Trip created: ${tripRequest.trip?.id || tripRequest.id}\x1b[0m`); + console.log(`[System] Offered to \x1b[33m${tripRequest.offeredDrivers}\x1b[0m drivers nearby.`); - // 7. Simulate Trip Flow (Wait 10 seconds per state) - await new Promise(r => setTimeout(r, 10000)); - - if (tripId) { - console.log(`\n[Driver] Arriving at pickup...`); - await api('PATCH', `/trips/${tripId}/status`, { status: 'driver_arrived' }, driverToken); + // 7. Simulate Trip Flow + await new Promise(r => setTimeout(r, 10000)); + + if (tripId) { + try { + console.log(`\n[Driver] Arriving at pickup...`); + await api('PATCH', `/trips/${tripId}/status`, { status: 'driver_arrived' }, driverToken); + await new Promise(r => setTimeout(r, 5000)); - await new Promise(r => setTimeout(r, 5000)); + console.log(`\n[Driver] Starting Trip...`); + await api('PATCH', `/trips/${tripId}/status`, { status: 'in_progress' }, driverToken); + await new Promise(r => setTimeout(r, 5000)); - console.log(`\n[Driver] Starting Trip...`); - await api('PATCH', `/trips/${tripId}/status`, { status: 'in_progress' }, driverToken); + console.log(`\n[Driver] Completing Trip...`); + await api('PATCH', `/trips/${tripId}/status`, { status: 'completed' }, driverToken); - await new Promise(r => setTimeout(r, 5000)); + console.log(`\n\x1b[32m🎉 E2E Simulation Completed Successfully!\x1b[0m\n`); + } catch(e) { + console.error(`\x1b[31m[Simulation Error] ${e.message}\x1b[0m`); + } + } else { + console.log(`\n\x1b[31mFailed to receive dispatch!\x1b[0m`); + console.log(`[Hint] Check if offeredDrivers was 0. If it was 0, the backend didn't find the driver in Redis. If it was > 0, the backend emitted the socket event but the driver didn't receive it.\n`); + } - console.log(`\n[Driver] Completing Trip...`); - await api('PATCH', `/trips/${tripId}/status`, { status: 'completed' }, driverToken); - - console.log(`\n\x1b[32m🎉 E2E Simulation Completed Successfully!\x1b[0m\n`); - } else { - console.log(`\n\x1b[31mFailed to receive dispatch!\x1b[0m\n`); + clearInterval(locationInterval); + driverSocket.disconnect(); + riderSocket.disconnect(); + process.exit(0); + } catch (error) { + console.error(`\n\x1b[31m[Fatal Error] Simulation Crashed: ${error.message}\x1b[0m\n`); + process.exit(1); } - - clearInterval(locationInterval); - driverSocket.disconnect(); - riderSocket.disconnect(); - process.exit(0); } -run().catch(console.error); +run();