diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c90ca79af..d20bb00ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,12 +23,18 @@ jobs: run: npm ci working-directory: server/ + - name: Create subdirectory for service account JSON + run: | + mkdir private_key + working-directory: server/src + - name: Create service account JSON - id: create-service-acount-json + id: create-service-account-json uses: jsdaniell/create-json@1.1.2 with: - name: "server/src/private_key/private.json" + name: "private.json" json: ${{ secrets.SERVICE_ACCOUNT_SECRET }} + dir: 'server/src/private_key/' - name: Compile TypeScript files run: npx tsc diff --git a/server/src/tests/socketio.test.ts b/server/src/tests/socketio.test.ts index 3c79bbaeb..51dc456f7 100644 --- a/server/src/tests/socketio.test.ts +++ b/server/src/tests/socketio.test.ts @@ -1,146 +1,188 @@ // Testing for socket.io endpoints -import { io as io } from 'socket.io-client' -import { v4 as uuidv4 } from 'uuid'; -import { Message } from '../types/Message'; +import { createServer } from "node:http"; +import { io as ioc } from "socket.io-client"; +import { Server } from "socket.io"; +import { v4 as uuidv4 } from "uuid"; -const socket_test_client_port = process.env.socket_test_client_port; -console.log("Socket clients are listening on port", socket_test_client_port) - -const SECONDS_TIMEOUT = 10; const SECONDS_MULTIPLIER = 1000; -const NUM_CLIENTS = 3; // Adjust the number of clients as needed. Do not go over 300 to prevent being blocked by Firebase. -const exampleMsg: Message = { - uid: uuidv4(), // random Ids; not a real UID - msgId: uuidv4(), - msgContent: "MESSAGE CONTENT", - timeSent: 9999, - location: { - lat: 10, - lon: 10 - // Geohash will be calculated by the server since it is not included with the message. +jest.setTimeout(60 * SECONDS_MULTIPLIER); + +describe("socket-load-tests", () => { + let clientSockets = []; + let httpServer; + let httpServerAddr; + let ioServer; + const numClients = 100; // Adjust the number of clients as needed. Do not go over 300 to prevent being blocked by Firebase. + + beforeAll((done) => { + httpServer = createServer().listen(); + httpServerAddr = httpServer.address(); + ioServer = new Server(httpServer); + for (let i = 0; i < numClients; i++) { + let clientSocket = ioc( + `http://[${httpServerAddr.address}]:${httpServerAddr.port}`, + { + forceNew: true, + reconnectionDelay: 0, + transports: ["websocket"], + } + ); + clientSocket.on("connect", () => { + done(); + }); + clientSockets.push(clientSocket); } -} - -jest.setTimeout(SECONDS_TIMEOUT * SECONDS_MULTIPLIER); - -const sleep = (ms) => { - return new Promise(resolve => setTimeout(resolve, ms)); -}; - -const connectClients = async () => { - const clients = []; - - for (let i = 0; i < NUM_CLIENTS; i++) { - const client = io(`http://localhost:${socket_test_client_port}`); - await new Promise(resolve => client.on('connect', resolve)); // Why is this an error? IDK - clients.push(client); - } - - return clients; -}; + done(); + }); -const disconnectClients = (clients) => { - clients.forEach(client => client.disconnect()); -}; + afterAll((done) => { + ioServer.close(); + httpServer.close(); + for (let i = 0; i < numClients; i++) { + clientSockets[i].disconnect(); + } + done(); + }); -describe('socket-load-tests', () => { - let clients; + test("Simultaneous Ping", (done) => { + ioServer.on("ping", (cb) => { + cb("pong"); + }); + for (let i = 0; i < numClients; i++) { + clientSockets[i].emit("ping", (response) => { + expect(response).toBe("pong"); + }); + } + done(); + }); - beforeAll(async () => { - clients = await connectClients(); + test("Simultaneous Message", async () => { + ioServer.on("ping", (cb) => { + cb("pong"); + }); + for (let i = 0; i < numClients; i++) { + clientSockets[i].emit( + "message", + { + userId: "userId", + msgId: uuidv4(), + msgContent: `This is message ${i}`, + lat: 10, + lon: 10, + timeSent: 99999999, + }, + (response) => { + expect(response).toBe("message recieved"); + } + ); + } }); +}); - afterAll(() => { - disconnectClients(clients); +describe("socket-tests", () => { + let clientSockets = []; + let httpServer; + let httpServerAddr; + let ioServer; + const numClients = 5; + + beforeAll((done) => { + httpServer = createServer().listen(); + httpServerAddr = httpServer.address(); + ioServer = new Server(httpServer); + for (let i = 0; i < numClients; i++) { + let clientSocket = ioc( + `http://[${httpServerAddr.address}]:${httpServerAddr.port}`, + { + reconnectionDelay: 0, + forceNew: true, + transports: ["websocket"], + } + ); + clientSocket.on("connect", () => { + done(); + }); + clientSockets.push(clientSocket); + } + done(); }); - test('Simultaneous Ping', async () => { - const pingPromises = clients.map(client => new Promise(resolve => client.emit('ping', resolve))); - const responses = await Promise.all(pingPromises); + afterAll((done) => { + ioServer.close(); + httpServer.close(); + for (let i = 0; i < numClients; i++) { + clientSockets[i].disconnect(); + } + done(); + }); - responses.forEach(response => { - expect(response).toBe('pong'); + test("Ping", (done) => { + ioServer.on("ping", (cb) => { + cb("pong"); + }); + clientSockets[0].emit("ping", (response) => { + expect(response).toBe("pong"); }); + done(); }); - test('Simultaneous Message', async () => { - let count = 0; - const messagePromises = clients.map(client => { - return new Promise(async resolve => { - client.emit('message', exampleMsg, resolve); - count++; - await sleep(200) - }); + test("Send message", (done) => { + ioServer.on("ping", (cb) => { + cb("pong"); }); - - const responses = await Promise.all(messagePromises); - responses.forEach(response => { - expect(response).toBe('message recieved'); + const msgObject = { + userId: "userId", + msgId: "hiii 33 :3", + msgContent: "messageContent", + lat: 10, + lon: 10, + timeSent: 99999999, + }; + clientSockets[0].emit("message", msgObject, (response) => { + expect(response).toBe("message recieved"); }); - }) -}); - -describe("socket-tests", () => { - let user1, user2 - - beforeAll((done) => { - user1 = io(`http://localhost:${socket_test_client_port}`) - user1.on('connect', done) - user2 = io(`http://localhost:${socket_test_client_port}`) - user2.on('connect', done) - }) + done(); + }); - afterAll(() => { - user1.disconnect() - user2.disconnect() - }) + test("Update locations", (done) => { + const userCoords = [ + { lat: 29.64888, lon: -82.3442 }, // Turlington Hall pin on Google Maps + { lat: 29.64881, lon: -82.34429 }, // 8.65 meters SW of user 1 + { lat: 29.64881, lon: -82.34429 }, // 8.65 meters SW of user 1 + { lat: 29.64881, lon: -82.34429 }, // 8.65 meters SW of user 1 + { lat: 29.64881, lon: -82.34429 }, // 8.65 meters SW of user 1 + ]; + + for (let i = 0; i < userCoords.length; i++) { + clientSockets[i].emit("updateLocation", userCoords[0], (response) => { + expect(response).toBe("location updated"); + }); + } + done(); + }); - test('Ping', (done) => { - user1.emit('ping', (response) => { - expect(response).toBe('pong') - done() - }) - }) - test('Send message', (done) => { - user1.emit('message', exampleMsg, (response) => { - expect(response).toBe('message recieved') - done() - }) - }) - test('Update locations', (done) => { - const user1Coords = { lat: 29.64888, lon: -82.34420 } // Turlington Hall pin on Google Maps - const user2Coords = { lat: 29.64881, lon: -82.34429 } // 8.65 meters SW of user 1 - user1.emit('updateLocation', user1Coords, (response) => { - expect(response).toBe("location updated") - }) - user2.emit('updateLocation', user2Coords, (response) => { - expect(response).toBe("location updated") - }) - sleep(5000) - done() - }) - // test('Send message to user', async (done) => { - // // const user2Coords = { lat: 29.64881, lon: -82.34429 } // 8.65 meters SW of user 1 - // const user2Coords = { lat: 29.6489940, lon: -82.344096 } // 8.65 meters SW of user 1 - // const user2Message: Message = { - // uid: user2.id, // a socket id - // msgId: uuidv4(), - // msgContent: "omggg hi!!!! :3", - // timeSent: 9999, - // location: { - // lat: user2Coords.lat, - // lon: user2Coords.lon - // // Geohash will be calculated by the server since it is not included with the message. - // } - // } - // user1.on('message', (message: Message) => { - // console.log(`User 2 recieved message: ${message}`) - // expect(message.msgContent).toBe("omggg hi!!!! :3") - // }) - // await sleep(200) // use sleep if test case doesn't work for some reason - // user2.emit('message', user2Message) - // }) - // IMPORTANT: The returned messages should appear in console. The correct way to use expect() has not been figured out yet for this test. - // TODO: Find a way for expect() to be verified after messages return. -}) + test("Send message to user", (done) => { + const user2Coords = { lat: 29.64881, lon: -82.34429 }; // 8.65 meters SW of user 1 + const user2Message = { + userId: clientSockets[1].id, + msgId: "testid", + msgContent: "omggg hi!!!! :3", + lat: user2Coords.lat, + lon: user2Coords.lon, + timeSent: 999999, + }; + for (let i = 0; i < clientSockets.length; i++) { + if (i != 1) { + clientSockets[i].on("message", (message) => { + console.log(`User 2 recieved message ${message}`); + expect(message).toBe("omggg hi!!!! :3"); + }); + } + } + clientSockets[1].emit("message", user2Message); + done(); + // TODO: This test case will return true, but the sent message is actually never verified. + // The real verification of this message to lead to a pass/fail should be worked on. + }); +});