diff --git a/siro_driver/android/app/src/main/AndroidManifest.xml b/siro_driver/android/app/src/main/AndroidManifest.xml index 90907b90..417c3005 100644 --- a/siro_driver/android/app/src/main/AndroidManifest.xml +++ b/siro_driver/android/app/src/main/AndroidManifest.xml @@ -68,10 +68,7 @@ - - + - - - - - - + diff --git a/siro_driver/pubspec.yaml b/siro_driver/pubspec.yaml index ba95cd32..5a6c3fd6 100644 --- a/siro_driver/pubspec.yaml +++ b/siro_driver/pubspec.yaml @@ -2,7 +2,7 @@ name: siro_driver description: "A new Flutter project." publish_to: "none" # Remove this line if you wish to publish to pub.dev -version: 1.0.0+4 +version: 1.0.0+5 environment: sdk: ">=3.0.5 <4.0.0" diff --git a/stress_test/generate_mock_data.php b/stress_test/generate_mock_data.php new file mode 100644 index 00000000..d9677e57 --- /dev/null +++ b/stress_test/generate_mock_data.php @@ -0,0 +1,37 @@ + (string)$driverId, + 'role' => 'driver', + 'iat' => time(), + 'exp' => time() + 86400 +]; + +$jwt = JWT::encode($payload, $secret, 'HS256'); + +echo json_encode([ + 'driver_id' => $driverId, + 'jwt' => $jwt +]); diff --git a/stress_test/load_test.js b/stress_test/load_test.js new file mode 100644 index 00000000..bd02d862 --- /dev/null +++ b/stress_test/load_test.js @@ -0,0 +1,179 @@ +const { io } = require('socket.io-client'); +const axios = require('axios'); +const { execSync } = require('child_process'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); + +const argv = yargs(hideBin(process.argv)) + .option('trips', { + alias: 't', + type: 'number', + description: 'Number of concurrent trips to simulate', + default: 1 + }) + .option('base-url', { + alias: 'b', + type: 'string', + description: 'Base URL for backend APIs', + default: 'http://localhost/backend' + }) + .option('socket-url', { + alias: 's', + type: 'string', + description: 'Location server socket URL', + default: 'ws://localhost:2020' + }) + .argv; + +// Helper to get JWT for mock driver +function getMockDriverJwt(driverId) { + try { + const output = execSync(`php generate_mock_data.php ${driverId}`).toString(); + return JSON.parse(output).jwt; + } catch (err) { + console.error(`Failed to generate JWT for driver ${driverId}`, err.message); + return 'mock_jwt'; + } +} + +async function simulateTrip(tripIndex) { + const driverId = 900000 + tripIndex; + const passengerId = 800000 + tripIndex; + const driverJwt = getMockDriverJwt(driverId); + + console.log(`[Trip ${tripIndex}] Starting simulation... Driver: ${driverId}, Passenger: ${passengerId}`); + + // 1. Connect Driver to Socket + const socket = io(argv.socketUrl, { + query: { + driver_id: driverId, + platform: 'android', + jwt: driverJwt + }, + transports: ['websocket'] + }); + + return new Promise((resolve, reject) => { + let rideId = null; + + socket.on('connect', async () => { + console.log(`[Trip ${tripIndex}] Driver ${driverId} connected to socket.`); + + // 2. Passenger Requests Ride + try { + const addRideFormData = new URLSearchParams({ + passenger_id: passengerId, + start_location: '31.95,35.91', // Amman + end_location: '31.96,35.92', + price: '5', + price_token: 'dummy', + distance: '2', + carType: 'Economy', + passenger_name: 'Test Passenger', + passenger_phone: '0790000000', + start_name: 'Start', + end_name: 'End' + }); + + const reqStart = Date.now(); + const res = await axios.post(`${argv.baseUrl}/ride/rides/add_ride.php`, addRideFormData.toString(), { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }); + + // Note: the backend add_ride.php might return the newly created ride ID + console.log(`[Trip ${tripIndex}] Passenger requested ride. Response time: ${Date.now() - reqStart}ms`); + } catch (err) { + console.error(`[Trip ${tripIndex}] Error requesting ride:`, err.message); + socket.disconnect(); + return reject(err); + } + }); + + socket.on('market_new_ride', async (payload) => { + rideId = payload.id; + console.log(`[Trip ${tripIndex}] Driver ${driverId} received ride offer #${rideId}. Accepting...`); + + // 3. Driver Accepts Ride + try { + const acceptFormData = new URLSearchParams({ + id: rideId, + driver_id: driverId, + status: 'accepted' + }); + + const acceptStart = Date.now(); + await axios.post(`${argv.baseUrl}/ride/rides/acceptRide.php`, acceptFormData.toString(), { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }); + console.log(`[Trip ${tripIndex}] Driver ${driverId} accepted ride #${rideId}. Response time: ${Date.now() - acceptStart}ms`); + + // 4. Simulate Driving (Updates Location) + let steps = 0; + const driveInterval = setInterval(async () => { + steps++; + socket.emit('update_location', { + lat: 31.95 + (steps * 0.001), + lng: 35.91 + (steps * 0.001), + heading: 90, + speed: 40, + status: 'on' + }); + + if (steps === 3) { + // Driver Arrived / Starts Ride + const startFormData = new URLSearchParams({ + id: rideId, + driver_id: driverId, + status: 'start' + }); + await axios.post(`${argv.baseUrl}/ride/rides/start_ride.php`, startFormData.toString()); + console.log(`[Trip ${tripIndex}] Driver ${driverId} started ride #${rideId}`); + } + + if (steps === 6) { + clearInterval(driveInterval); + // 5. Finish Ride + const finishFormData = new URLSearchParams({ + id: rideId, + driver_id: driverId, + status: 'finish' + }); + await axios.post(`${argv.baseUrl}/ride/rides/finish_ride_updates.php`, finishFormData.toString()); + console.log(`[Trip ${tripIndex}] Driver ${driverId} finished ride #${rideId}.`); + + socket.disconnect(); + resolve(`Trip ${tripIndex} completed successfully.`); + } + }, 1000); + + } catch (err) { + console.error(`[Trip ${tripIndex}] Error during ride execution:`, err.message); + socket.disconnect(); + reject(err); + } + }); + + socket.on('disconnect', () => { + console.log(`[Trip ${tripIndex}] Driver ${driverId} disconnected.`); + }); + }); +} + +async function runTest() { + console.log(`🚀 Starting Load Test with ${argv.trips} concurrent trips`); + const promises = []; + for (let i = 1; i <= argv.trips; i++) { + promises.push(simulateTrip(i)); + // Add a slight stagger so we don't overwhelm immediately + await new Promise(r => setTimeout(r, 100)); + } + + try { + await Promise.all(promises); + console.log("✅ All simulated trips finished successfully."); + } catch (e) { + console.error("❌ Some trips failed.", e); + } +} + +runTest(); diff --git a/stress_test/package.json b/stress_test/package.json new file mode 100644 index 00000000..39a6842e --- /dev/null +++ b/stress_test/package.json @@ -0,0 +1,17 @@ +{ + "name": "siro-stress-test", + "version": "1.0.0", + "description": "Stress testing suite for Siro ride-hailing services", + "main": "load_test.js", + "scripts": { + "test": "node load_test.js", + "test:multi": "node load_test.js --trips=100" + }, + "dependencies": { + "axios": "^1.6.8", + "socket.io-client": "^4.7.5", + "uuid": "^9.0.1", + "yargs": "^17.7.2", + "dotenv": "^16.4.5" + } +}