Files
Siro/stress_test/load_test.js
T

182 lines
6.7 KiB
JavaScript

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();
// Extract JSON portion in case of PHP warnings
const jsonString = output.substring(output.indexOf('{'));
return JSON.parse(jsonString).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();