mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
base
no library for socket.io
This commit is contained in:
parent
0b2552bdff
commit
b608793ac3
8 changed files with 738 additions and 39 deletions
|
|
@ -1,5 +1,8 @@
|
||||||
js_import njs/main.js;
|
js_import njs/main.js;
|
||||||
|
|
||||||
|
js_set $room_cache_check main.checkCache;
|
||||||
|
js_set $room_cache_set main.setCache;
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default upgrade;
|
default upgrade;
|
||||||
'' close;
|
'' close;
|
||||||
|
|
@ -26,26 +29,28 @@ server {
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
|
|
||||||
# DNS resolver for Docker's internal DNS
|
|
||||||
resolver 127.0.0.11 valid=30s;
|
|
||||||
|
|
||||||
# Game WebSocket routing
|
# Game WebSocket routing
|
||||||
location ~ /api/room/([^/]+)/socket {
|
location ~/api/room/([^/]+)/socket {
|
||||||
set $room_id $1;
|
set $room_id $1;
|
||||||
|
js_content main.routeWebSocket;
|
||||||
|
}
|
||||||
|
|
||||||
proxy_pass http://room_$room_id:4500;
|
# WebSocket proxy location
|
||||||
|
location @websocket_proxy {
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection $connection_upgrade;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
proxy_connect_timeout 7m;
|
proxy_connect_timeout 7m;
|
||||||
proxy_send_timeout 7m;
|
proxy_send_timeout 7m;
|
||||||
proxy_read_timeout 7m;
|
proxy_read_timeout 7m;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
|
||||||
|
proxy_pass $proxy_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|
@ -54,4 +59,4 @@ server {
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
|
const roomCache = new Map();
|
||||||
|
|
||||||
async function fetchRoomInfo(r) {
|
async function fetchRoomInfo(r) {
|
||||||
try {
|
try {
|
||||||
// Make request to API to get room info
|
let res = await r.subrequest(`/api/room/${r.variables.room_id}`, { method: 'GET' });
|
||||||
let res = await r.subrequest('/api/room/' + r.variables.room_id, {
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
r.error(`Failed to fetch room info: ${res.status}`);
|
r.error(`Failed to fetch room info: ${res.status}`);
|
||||||
|
|
@ -11,7 +10,7 @@ async function fetchRoomInfo(r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let room = JSON.parse(res.responseText);
|
let room = JSON.parse(res.responseText);
|
||||||
r.error(`Debug: Room info: ${JSON.stringify(room)}`); // Debug log
|
r.error(`Debug: Room info fetched: ${JSON.stringify(room)}`);
|
||||||
return room;
|
return room;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
r.error(`Error fetching room info: ${error}`);
|
r.error(`Error fetching room info: ${error}`);
|
||||||
|
|
@ -19,36 +18,48 @@ async function fetchRoomInfo(r) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function routeWebSocket(r) {
|
function checkCache(r) {
|
||||||
try {
|
let room = roomCache.get(r.variables.room_id);
|
||||||
const roomInfo = await fetchRoomInfo(r);
|
if (room) {
|
||||||
|
r.error(`Cache hit for room_id: ${r.variables.room_id}`);
|
||||||
|
r.return(200, JSON.stringify(room));
|
||||||
|
} else {
|
||||||
|
r.error(`Cache miss for room_id: ${r.variables.room_id}`);
|
||||||
|
r.return(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!roomInfo || !roomInfo.host) {
|
function setCache(r) {
|
||||||
r.error(`Debug: Invalid room info: ${JSON.stringify(roomInfo)}`);
|
let room = JSON.parse(r.responseBody);
|
||||||
|
roomCache.set(r.variables.room_id, room);
|
||||||
|
r.error(`Cached room info: ${JSON.stringify(room)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function routeWebSocket(r) {
|
||||||
|
let room = roomCache.get(r.variables.room_id);
|
||||||
|
|
||||||
|
if (!room) {
|
||||||
|
r.error(`Cache miss. Fetching room info for: ${r.variables.room_id}`);
|
||||||
|
room = await fetchRoomInfo(r);
|
||||||
|
|
||||||
|
if (!room || !room.host) {
|
||||||
|
r.error(`Invalid room info for room_id: ${r.variables.room_id}`);
|
||||||
r.return(404, 'Room not found or invalid');
|
r.return(404, 'Room not found or invalid');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the host includes protocol if not already present
|
roomCache.set(r.variables.room_id, room); // Cache the result
|
||||||
let proxyUrl = roomInfo.host;
|
} else {
|
||||||
if (!proxyUrl.startsWith('http://') && !proxyUrl.startsWith('https://')) {
|
r.error(`Cache hit for room_id: ${r.variables.room_id}`);
|
||||||
proxyUrl = 'http://' + proxyUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.error(`Debug: Original URL: ${r.uri}`);
|
|
||||||
r.error(`Debug: Setting proxy target to: ${proxyUrl}`);
|
|
||||||
r.error(`Debug: Headers: ${JSON.stringify(r.headersIn)}`);
|
|
||||||
|
|
||||||
// Set the proxy target variable
|
|
||||||
r.variables.proxy_target = proxyUrl;
|
|
||||||
|
|
||||||
// Redirect to the websocket proxy
|
|
||||||
r.internalRedirect('@websocket_proxy');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
r.error(`WebSocket routing error: ${error}`);
|
|
||||||
r.return(500, 'Internal routing error');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let proxyUrl = room.host.startsWith('http://') || room.host.startsWith('https://')
|
||||||
|
? room.host
|
||||||
|
: `http://${room.host}`;
|
||||||
|
|
||||||
|
r.error(`Routing WebSocket to: ${proxyUrl}`);
|
||||||
|
r.variables.proxy_target = proxyUrl;
|
||||||
|
r.internalRedirect('@websocket_proxy');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { routeWebSocket };
|
export default { routeWebSocket, checkCache, setCache };
|
||||||
|
|
|
||||||
32
test/stressTest/class/student.js
Normal file
32
test/stressTest/class/student.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { io } from "socket.io-client";
|
||||||
|
|
||||||
|
export class Student {
|
||||||
|
constructor(username, roomName) {
|
||||||
|
this.username = username;
|
||||||
|
this.roomName = roomName;
|
||||||
|
this.socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectToRoom(baseUrl) {
|
||||||
|
this.socket = io(baseUrl, {
|
||||||
|
path: `/api/room/${this.roomName}/socket`,
|
||||||
|
transports: ['websocket'],autoConnect: true,
|
||||||
|
reconnection: true,
|
||||||
|
reconnectionAttempts: 10,
|
||||||
|
reconnectionDelay: 10000,
|
||||||
|
timeout: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
this.joinRoom(this.roomName,this.username);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
joinRoom(roomName, username) {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.emit('join-room', { roomName, username });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
test/stressTest/class/teacher.js
Normal file
35
test/stressTest/class/teacher.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { io } from "socket.io-client";
|
||||||
|
|
||||||
|
export class Teacher {
|
||||||
|
constructor(username, roomName) {
|
||||||
|
this.username = username;
|
||||||
|
this.roomName = roomName;
|
||||||
|
this.socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectToRoom(baseUrl) {
|
||||||
|
this.socket = io(baseUrl, {
|
||||||
|
path: `/api/room/${this.roomName}/socket`,
|
||||||
|
transports: ['websocket'],autoConnect: true,
|
||||||
|
reconnection: true,
|
||||||
|
reconnectionAttempts: 10,
|
||||||
|
reconnectionDelay: 10000,
|
||||||
|
timeout: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
this.createRoom(this.roomName);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('create-success', () => {
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoom() {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.emit('create-room', this.roomName || undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
test/stressTest/main.js
Normal file
128
test/stressTest/main.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import { attemptLoginOrRegister, createRoomContainer } from './utility/apiServices.js';
|
||||||
|
import { Student } from './class/student.js';
|
||||||
|
import { Teacher } from './class/teacher.js';
|
||||||
|
|
||||||
|
const BASE_URL = 'http://localhost';
|
||||||
|
const user = {
|
||||||
|
username: 'admin@example.com',
|
||||||
|
password: 'adminPassword',
|
||||||
|
};
|
||||||
|
|
||||||
|
const numberRooms = 30;
|
||||||
|
const studentPerRoom = 59; // Max is 60; 1 slot is reserved for the teacher
|
||||||
|
|
||||||
|
const roomAssociations = {};
|
||||||
|
const allSockets = []; // Track all active WebSocket connections
|
||||||
|
|
||||||
|
async function createRoomsAndTeachers(token) {
|
||||||
|
const roomCreationPromises = [];
|
||||||
|
const teachers = [];
|
||||||
|
|
||||||
|
for (let index = 0; index < numberRooms; index++) {
|
||||||
|
roomCreationPromises.push(
|
||||||
|
createRoomContainer(BASE_URL, token).then((room) => {
|
||||||
|
if (room?.id) {
|
||||||
|
const teacher = new Teacher(`teacher_${index}`, room.id);
|
||||||
|
teachers.push(teacher);
|
||||||
|
|
||||||
|
roomAssociations[room.id] = {
|
||||||
|
teacher,
|
||||||
|
students: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track teacher WebSocket for cleanup
|
||||||
|
if (teacher.socket) {
|
||||||
|
allSockets.push(teacher.socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(roomCreationPromises);
|
||||||
|
console.log(`Created ${Object.keys(roomAssociations).length} rooms with associated teachers.`);
|
||||||
|
return teachers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectTeachersToRooms(teachers) {
|
||||||
|
const teacherConnectionPromises = teachers.map(async (teacher) => {
|
||||||
|
await teacher.connectToRoom(BASE_URL);
|
||||||
|
if (teacher.socket) {
|
||||||
|
allSockets.push(teacher.socket); // Track WebSocket
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.allSettled(teacherConnectionPromises);
|
||||||
|
console.log('All teachers connected to their rooms.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addAndConnectStudents() {
|
||||||
|
const studentCreationPromises = [];
|
||||||
|
|
||||||
|
Object.entries(roomAssociations).forEach(([roomId, association], roomIndex) => {
|
||||||
|
for (let i = 0; i < studentPerRoom; i++) {
|
||||||
|
const student = new Student(`student_${roomIndex}_${i}`, roomId);
|
||||||
|
association.students.push(student);
|
||||||
|
|
||||||
|
studentCreationPromises.push(
|
||||||
|
student.connectToRoom(BASE_URL).then(() => {
|
||||||
|
if (student.socket) {
|
||||||
|
allSockets.push(student.socket); // Track WebSocket
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.allSettled(studentCreationPromises);
|
||||||
|
console.log('All students connected to their respective rooms.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAllSockets() {
|
||||||
|
console.log('Closing all WebSocket connections...');
|
||||||
|
allSockets.forEach((socket) => {
|
||||||
|
try {
|
||||||
|
if (socket.readyState === socket.OPEN) {
|
||||||
|
socket.close(); // Gracefully close the WebSocket
|
||||||
|
console.log('Closed WebSocket connection.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error closing WebSocket:', error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log('All WebSocket connections closed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password);
|
||||||
|
if (!token) {
|
||||||
|
console.error('Failed to log in. Exiting...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const teachers = await createRoomsAndTeachers(token);
|
||||||
|
await connectTeachersToRooms(teachers);
|
||||||
|
await addAndConnectStudents();
|
||||||
|
|
||||||
|
console.log('All tasks completed.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('An error occurred:', error.message);
|
||||||
|
} finally {
|
||||||
|
closeAllSockets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle script termination (Ctrl+C)
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
console.log('Script interrupted (Ctrl+C).');
|
||||||
|
closeAllSockets();
|
||||||
|
process.exit(0); // Exit cleanly
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle script exit
|
||||||
|
process.on('exit', () => {
|
||||||
|
closeAllSockets();
|
||||||
|
});
|
||||||
|
|
||||||
|
main();
|
||||||
344
test/stressTest/package-lock.json
generated
Normal file
344
test/stressTest/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,344 @@
|
||||||
|
{
|
||||||
|
"name": "stresstest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "stresstest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"socket.io-client": "^4.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cors": {
|
||||||
|
"version": "2.8.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
|
||||||
|
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||||
|
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.19.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||||
|
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64id": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^4.5.0 || >= 5.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io": {
|
||||||
|
"version": "6.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||||
|
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cookie": "^0.4.1",
|
||||||
|
"@types/cors": "^2.8.12",
|
||||||
|
"@types/node": ">=10.0.0",
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "2.0.0",
|
||||||
|
"cookie": "~0.7.2",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
|
||||||
|
"integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1",
|
||||||
|
"xmlhttprequest-ssl": "~2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
|
"node_modules/socket.io": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "~2.0.0",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io": "~6.6.0",
|
||||||
|
"socket.io-adapter": "~2.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-adapter": {
|
||||||
|
"version": "2.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||||
|
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "~4.3.4",
|
||||||
|
"ws": "~8.17.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.6.1",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.19.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||||
|
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||||
|
},
|
||||||
|
"node_modules/vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
test/stressTest/package.json
Normal file
17
test/stressTest/package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "stresstest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "main.js",
|
||||||
|
"type": "module",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"socket.io-client": "^4.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
127
test/stressTest/utility/apiServices.js
Normal file
127
test/stressTest/utility/apiServices.js
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in a user.
|
||||||
|
* @param {string} baseUrl - The base URL of the API.
|
||||||
|
* @param {string} email - The user's email.
|
||||||
|
* @param {string} password - The user's password.
|
||||||
|
* @returns {Promise<string>} - The authentication token if successful.
|
||||||
|
*/
|
||||||
|
async function login(baseUrl, email, password) {
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new Error("Email and password are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${baseUrl}/api/user/login`;
|
||||||
|
const payload = { email, password };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post(url, payload, {
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200 || !res.data.token) {
|
||||||
|
throw new Error(`Login failed. Status: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Login successful for ${email}`);
|
||||||
|
return res.data.token;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Login error for ${email}:`, error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new user.
|
||||||
|
* @param {string} baseUrl - The base URL of the API.
|
||||||
|
* @param {string} email - The user's email.
|
||||||
|
* @param {string} password - The user's password.
|
||||||
|
* @returns {Promise<string>} - A success message if registration is successful.
|
||||||
|
*/
|
||||||
|
async function register(baseUrl, email, password) {
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new Error("Email and password are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${baseUrl}/api/user/register`;
|
||||||
|
const payload = { email, password };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post(url, payload, {
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error(`Registration failed. Status: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Registration successful for ${email}`);
|
||||||
|
return res.data.message || "Registration completed successfully.";
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Registration error for ${email}:`, error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to log in a user, or registers and logs in if the login fails.
|
||||||
|
* @param {string} baseUrl - The base URL of the API.
|
||||||
|
* @param {string} username - The user's email/username.
|
||||||
|
* @param {string} password - The user's password.
|
||||||
|
* @returns {Promise<string|null>} - The authentication token if successful, otherwise null.
|
||||||
|
*/
|
||||||
|
export async function attemptLoginOrRegister(baseUrl, username, password) {
|
||||||
|
try {
|
||||||
|
const token = await login(baseUrl, username, password);
|
||||||
|
console.log(`User successfully logged in: ${username}`);
|
||||||
|
return token;
|
||||||
|
} catch (loginError) {
|
||||||
|
console.log(`Login failed for ${username}. Attempting registration...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const registerResponse = await register(baseUrl, username, password);
|
||||||
|
console.log(`User successfully registered: ${username}`);
|
||||||
|
|
||||||
|
const token = await login(baseUrl, username, password);
|
||||||
|
console.log(`User successfully logged in after registration: ${username}`);
|
||||||
|
return token;
|
||||||
|
} catch (registerError) {
|
||||||
|
console.error(`Registration failed for ${username}:`, registerError.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new room.
|
||||||
|
* @param {string} baseUrl - The base URL of the API.
|
||||||
|
* @param {string} token - The authorization token.
|
||||||
|
* @returns {Promise<object>} - The created room object if successful.
|
||||||
|
*/
|
||||||
|
export async function createRoomContainer(baseUrl, token) {
|
||||||
|
if (!token) {
|
||||||
|
throw new Error("Authorization token is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${baseUrl}/api/room`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post(url, {}, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error(`Room creation failed. Status: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Room successfully created:", res.data);
|
||||||
|
return res.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Room creation error:", error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue