2024-12-06 11:24:57 -05:00
|
|
|
import { attemptLoginOrRegister, createRoomContainer } from './utility/apiServices.js';
|
|
|
|
|
import { Student } from './class/student.js';
|
|
|
|
|
import { Teacher } from './class/teacher.js';
|
|
|
|
|
import { Watcher } from './class/watcher.js';
|
2024-12-08 15:00:55 -05:00
|
|
|
import { TestMetrics } from './class/metrics.js';
|
2024-12-06 11:24:57 -05:00
|
|
|
import dotenv from 'dotenv';
|
|
|
|
|
import generateMetricsReport from './utility/metrics_generator.js';
|
|
|
|
|
|
|
|
|
|
dotenv.config();
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
const config = {
|
2024-12-07 15:41:21 -05:00
|
|
|
baseUrl: process.env.BASE_URL || 'http://localhost',
|
2024-12-06 19:12:29 -05:00
|
|
|
auth: {
|
|
|
|
|
username: process.env.USER_EMAIL || 'admin@admin.com',
|
|
|
|
|
password: process.env.USER_PASSWORD || 'admin'
|
|
|
|
|
},
|
|
|
|
|
rooms: {
|
2024-12-07 19:33:38 -05:00
|
|
|
count: parseInt(process.env.NUMBER_ROOMS || '15'),
|
2024-12-06 19:12:29 -05:00
|
|
|
usersPerRoom: parseInt(process.env.USERS_PER_ROOM || '60'),
|
2024-12-07 19:33:38 -05:00
|
|
|
batchSize: parseInt(process.env.BATCH_SIZE || 5),
|
|
|
|
|
batchDelay: parseInt(process.env.BATCH_DELAY || 250)
|
2024-12-06 19:12:29 -05:00
|
|
|
},
|
|
|
|
|
simulation: {
|
2024-12-07 19:33:38 -05:00
|
|
|
maxMessages: parseInt(process.env.MAX_MESSAGES_ROUND || '20'),
|
2024-12-06 19:12:29 -05:00
|
|
|
messageInterval: parseInt(process.env.CONVERSATION_INTERVAL || '1000'),
|
2024-12-07 19:33:38 -05:00
|
|
|
responseTimeout: parseInt(process.env.MESSAGE_RESPONSE_TIMEOUT || 5000)
|
2024-12-06 19:12:29 -05:00
|
|
|
}
|
2024-12-06 11:24:57 -05:00
|
|
|
};
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
const rooms = new Map();
|
2024-12-06 19:31:48 -05:00
|
|
|
const metrics = new TestMetrics();
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-07 19:33:38 -05:00
|
|
|
// Changes to setupRoom function
|
2024-12-06 19:12:29 -05:00
|
|
|
async function setupRoom(token, index) {
|
|
|
|
|
try {
|
|
|
|
|
const room = await createRoomContainer(config.baseUrl, token);
|
|
|
|
|
if (!room?.id) throw new Error('Room creation failed');
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.roomsCreated++;
|
2024-12-06 11:24:57 -05:00
|
|
|
|
|
|
|
|
const teacher = new Teacher(`teacher_${index}`, room.id);
|
2024-12-07 19:33:38 -05:00
|
|
|
// Only create watcher for first room (index 0)
|
|
|
|
|
const watcher = index === 0 ? new Watcher(`watcher_${index}`, room.id) : null;
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
await Promise.all([
|
|
|
|
|
teacher.connectToRoom(config.baseUrl)
|
2024-12-06 19:31:48 -05:00
|
|
|
.then(() => metrics.usersConnected++)
|
|
|
|
|
.catch(err => {
|
|
|
|
|
metrics.userConnectionsFailed++;
|
|
|
|
|
metrics.logError('teacherConnection', err);
|
|
|
|
|
console.warn(`Teacher ${index} connection failed:`, err.message);
|
|
|
|
|
}),
|
2024-12-07 19:33:38 -05:00
|
|
|
// Only connect watcher if it exists
|
|
|
|
|
...(watcher ? [
|
|
|
|
|
watcher.connectToRoom(config.baseUrl)
|
|
|
|
|
.then(() => metrics.usersConnected++)
|
|
|
|
|
.catch(err => {
|
|
|
|
|
metrics.userConnectionsFailed++;
|
|
|
|
|
metrics.logError('watcherConnection', err);
|
|
|
|
|
console.warn(`Watcher ${index} connection failed:`, err.message);
|
|
|
|
|
})
|
|
|
|
|
] : [])
|
2024-12-06 19:12:29 -05:00
|
|
|
]);
|
|
|
|
|
|
2024-12-07 19:33:38 -05:00
|
|
|
// Adjust number of students based on whether room has a watcher
|
|
|
|
|
const studentCount = watcher ?
|
|
|
|
|
config.rooms.usersPerRoom - 2 : // Room with watcher: subtract teacher and watcher
|
|
|
|
|
config.rooms.usersPerRoom - 1; // Rooms without watcher: subtract only teacher
|
|
|
|
|
|
|
|
|
|
const students = Array.from({ length: studentCount },
|
2024-12-06 19:12:29 -05:00
|
|
|
(_, i) => new Student(`student_${index}_${i}`, room.id));
|
|
|
|
|
|
|
|
|
|
rooms.set(room.id, { teacher, watcher, students });
|
2024-12-06 11:24:57 -05:00
|
|
|
return room.id;
|
|
|
|
|
} catch (err) {
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.roomsFailed++;
|
|
|
|
|
metrics.logError('roomSetup', err);
|
2024-12-06 19:12:29 -05:00
|
|
|
console.warn(`Room ${index} setup failed:`, err.message);
|
2024-12-06 11:24:57 -05:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
async function connectParticipants(roomId) {
|
|
|
|
|
const { students } = rooms.get(roomId);
|
|
|
|
|
const participants = [...students];
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < participants.length; i += config.rooms.batchSize) {
|
|
|
|
|
const batch = participants.slice(i, i + config.rooms.batchSize);
|
|
|
|
|
await Promise.all(batch.map(p =>
|
|
|
|
|
Promise.race([
|
2024-12-06 19:31:48 -05:00
|
|
|
p.connectToRoom(config.baseUrl).then(() => {
|
|
|
|
|
metrics.usersConnected++;
|
|
|
|
|
}),
|
2024-12-06 19:12:29 -05:00
|
|
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
|
2024-12-06 19:31:48 -05:00
|
|
|
]).catch(err => {
|
|
|
|
|
metrics.userConnectionsFailed++;
|
|
|
|
|
metrics.logError('studentConnection', err);
|
|
|
|
|
console.warn(`Connection failed for ${p.username}:`, err.message);
|
|
|
|
|
})
|
2024-12-06 19:12:29 -05:00
|
|
|
));
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, config.rooms.batchDelay));
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
async function simulate() {
|
|
|
|
|
const simulations = Array.from(rooms.entries()).map(async ([roomId, { teacher, students }]) => {
|
|
|
|
|
const connectedStudents = students.filter(student => student.socket?.connected);
|
|
|
|
|
const expectedResponses = connectedStudents.length;
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < config.simulation.maxMessages; i++) {
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.messagesAttempted++;
|
2024-12-06 19:12:29 -05:00
|
|
|
const initialMessages = teacher.nbrMessageReceived;
|
|
|
|
|
|
|
|
|
|
try {
|
2024-12-06 19:31:48 -05:00
|
|
|
teacher.broadcastMessage(`Message ${i + 1} from ${teacher.username}`);
|
|
|
|
|
metrics.messagesSent++;
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
await Promise.race([
|
|
|
|
|
new Promise(resolve => {
|
|
|
|
|
const checkResponses = setInterval(() => {
|
|
|
|
|
const receivedResponses = teacher.nbrMessageReceived - initialMessages;
|
|
|
|
|
if (receivedResponses >= expectedResponses) {
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.messagesReceived += receivedResponses;
|
2024-12-06 19:12:29 -05:00
|
|
|
clearInterval(checkResponses);
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
}, 100);
|
2024-12-06 19:31:48 -05:00
|
|
|
}),
|
2024-12-07 19:33:38 -05:00
|
|
|
new Promise((_, reject) =>
|
2024-12-06 19:31:48 -05:00
|
|
|
setTimeout(() => reject(new Error('Response timeout')), config.simulation.responseTimeout)
|
|
|
|
|
)
|
2024-12-06 19:12:29 -05:00
|
|
|
]);
|
|
|
|
|
} catch (error) {
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.logError('messaging', error);
|
2024-12-06 19:12:29 -05:00
|
|
|
console.error(`Error in room ${roomId} message ${i + 1}:`, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, config.simulation.messageInterval));
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
await Promise.all(simulations);
|
|
|
|
|
console.log('All room simulations completed');
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
async function generateReport() {
|
2024-12-07 19:33:38 -05:00
|
|
|
const watcherRoom = Array.from(rooms.entries()).find(([_, room]) => room.watcher);
|
|
|
|
|
if (!watcherRoom) {
|
|
|
|
|
throw new Error('No watcher found in any room');
|
|
|
|
|
}
|
|
|
|
|
const data = {
|
|
|
|
|
[watcherRoom[0]]: watcherRoom[1].watcher.roomRessourcesData
|
|
|
|
|
};
|
|
|
|
|
return generateMetricsReport(data, metrics);
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
function cleanup() {
|
|
|
|
|
for (const { teacher, watcher, students } of rooms.values()) {
|
|
|
|
|
[teacher, watcher, ...students].forEach(p => p?.disconnect());
|
|
|
|
|
}
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
async function main() {
|
|
|
|
|
try {
|
|
|
|
|
const token = await attemptLoginOrRegister(config.baseUrl, config.auth.username, config.auth.password);
|
|
|
|
|
if (!token) throw new Error('Authentication failed');
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('Creating rooms...');
|
|
|
|
|
const roomIds = await Promise.all(
|
|
|
|
|
Array.from({ length: config.rooms.count }, (_, i) => setupRoom(token, i))
|
|
|
|
|
);
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('Connecting participants...');
|
|
|
|
|
await Promise.all(roomIds.filter(Boolean).map(connectParticipants));
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('Retrieving baseline metrics...');
|
2024-12-06 19:31:48 -05:00
|
|
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('Starting simulation across all rooms...');
|
|
|
|
|
await simulate();
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('Simulation complete. Waiting for system stabilization...');
|
2024-12-06 19:31:48 -05:00
|
|
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:31:48 -05:00
|
|
|
console.log('Generating final report...');
|
2024-12-06 19:12:29 -05:00
|
|
|
const folderName = await generateReport();
|
|
|
|
|
console.log(`Metrics report generated in ${folderName.outputDir}`);
|
2024-12-06 11:24:57 -05:00
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
console.log('All done!');
|
2024-12-06 11:24:57 -05:00
|
|
|
} catch (error) {
|
2024-12-06 19:31:48 -05:00
|
|
|
metrics.logError('main', error);
|
2024-12-06 11:24:57 -05:00
|
|
|
console.error('Error:', error.message);
|
2024-12-07 19:33:38 -05:00
|
|
|
} finally {
|
|
|
|
|
cleanup();
|
2024-12-06 11:24:57 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 19:12:29 -05:00
|
|
|
['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection'].forEach(event => {
|
|
|
|
|
process.on(event, cleanup);
|
2024-12-06 11:24:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
main();
|