// Import necessary modules for testing import { expect } from "chai"; import ioClient from "socket.io-client"; import { deleteNodeByNuid, getNodeByNuid, } from "../src/modules/mongo-wrappers/mongoNodesWrappers.mjs"; import { deleteSystemByName, getSystemByName, } from "../src/modules/mongo-wrappers/mongoSystemsWrappers.mjs"; import { nodeDisconnectWrapper, checkIfNodeHasOpenDiscordClient, getNodeCurrentListeningSystem, checkIfNodeIsConnectedToVC, getNodeDiscordUsername, getNodeDiscordID, requestBotLeaveServer, requestNodeJoinSystem, requestNodeUpdate, } from "../src/modules/socketServerWrappers.mjs"; import { nodeIo } from "../src/modules/socketServer.mjs"; import dotenv from "dotenv"; dotenv.config(); process.env.SERVER_PORT = 6000; // Define necessary variables for testing, such as mocked database connections or socket instances const localNodeConfig = { serverIp: "localhost", serverPort: process.env.SERVER_PORT, node: { nuid: "4f29a6340901a12affc87047c0ac16b01b92496c460c880a2459abe8c7928374", name: "testyv7", location: "china", capabilities: ["radio"], }, nearbySystems: { "Testing P25 System Name": { frequencies: [155344000, 155444000, 155555000, 155588550], mode: "p25", trunkFile: "trunk.tsv", whitelistFile: "whitelist.tsv", }, }, }; const updatedLocalNodeConfig = { node: { nuid: localNodeConfig.node.nuid, name: "updatedName", location: "updatedLocation", capabilities: ["radio", "weather"], // Updated capabilities }, nearbySystems: { "Testing P25 System Name": { frequencies: [155444000, 155555000, 155500000], mode: "p25", trunkFile: "trunk2.tsv", whitelistFile: "whitelist2.tsv", }, }, }; describe("Socket Server - Core Tests", () => { // Start the Socket.IO server before running tests let clientSocket; // The socket client let serverClientSocket; // The open client socket on the server before((done) => { // Startup the node server nodeIo.listen(process.env.SERVER_PORT || 3000, () => { console.log( `server running at http://localhost:${process.env.SERVER_PORT}`, ); }); // Connect a client socket to the server clientSocket = ioClient.connect( `http://localhost:${process.env.SERVER_PORT}`, ); nodeIo.on("connection", (socket) => { serverClientSocket = socket; done(); }); }); // Close the Socket.IO server after running tests after(async () => { // Disconnect client socket clientSocket.disconnect(); // Close the server nodeIo.close(); // Remove the test data deleteNodeByNuid(localNodeConfig.node.nuid); // Delete the user deleteSystemByName(Object.keys(localNodeConfig.nearbySystems)[0]); }); // Test Node Login functionality describe("Node Login", () => { it("Should add a new node if it does not exist", async () => { // Simulate a node login request // Use the getNodeByNuid mock function to simulate checking if node exists const existingNode = await getNodeByNuid(localNodeConfig.node.nuid); // Assert that existingNode is null before node login expect(existingNode).to.be.null; // Wait for the update const node_login = new Promise((res) => { clientSocket.on("node-login-successful", async () => { res(); }); }); // Emit the login command clientSocket.emit("node-login", localNodeConfig.node); // Wait for the successful login event await node_login; // Now we need to check if the node is added to the database // We can use getNodeByNuid again to verify if the node was added correctly const addedNode = await getNodeByNuid(localNodeConfig.node.nuid); console.log("Added Node:", addedNode); // Assert that the node is added correctly expect(addedNode).to.have.property("_id"); // Check if _id property exists expect(addedNode).to.have.property("nuid", localNodeConfig.node.nuid); expect(addedNode).to.have.property("name", localNodeConfig.node.name); expect(addedNode).to.have.property( "location", localNodeConfig.node.location, ); expect(addedNode).to.have.deep.property( "capabilities", localNodeConfig.node.capabilities, ); }); it("Should update a node if it exists", async () => { // Simulate a node login request // Use the getNodeByNuid mock function to simulate checking if node exists const existingNode = await getNodeByNuid(localNodeConfig.node.nuid); // Assert that existingNode is matches the existing data before logging in expect(existingNode).to.have.property("_id"); // Check if _id property exists expect(existingNode).to.have.property("nuid", localNodeConfig.node.nuid); expect(existingNode).to.have.property("name", localNodeConfig.node.name); expect(existingNode).to.have.property( "location", localNodeConfig.node.location, ); expect(existingNode).to.have.deep.property( "capabilities", localNodeConfig.node.capabilities, ); // Wait for the update const node_login = new Promise((res) => { clientSocket.on("node-login-successful", async () => { res(); }); }); // Emit the login command clientSocket.emit("node-login", updatedLocalNodeConfig.node); // Wait for the successful login event await node_login; // Now we need to check if the node is added to the database // We can use getNodeByNuid again to verify if the node was added correctly const updatedNode = await getNodeByNuid(localNodeConfig.node.nuid); console.log("Updated Node:", updatedNode); // Assert that the node is added correctly expect(updatedNode).to.have.property("_id"); // Check if _id property exists expect(updatedNode).to.have.property( "nuid", updatedLocalNodeConfig.node.nuid, ); expect(updatedNode).to.have.property( "name", updatedLocalNodeConfig.node.name, ); expect(updatedNode).to.have.property( "location", updatedLocalNodeConfig.node.location, ); expect(updatedNode).to.have.deep.property( "capabilities", updatedLocalNodeConfig.node.capabilities, ); }); }); // Test Node Update functionality describe("Node Update", () => { it("Should add a node's nearby systems", async () => { // Simulate an update request sent from the client to the server // Get the existing node in the database const existingNode = await getNodeByNuid(localNodeConfig.node.nuid); // Assert that existingNode matches the updatedLocalNodeConfig expect(existingNode).to.have.property("_id"); // Check if _id property exists expect(existingNode).to.have.property( "nuid", updatedLocalNodeConfig.node.nuid, ); expect(existingNode).to.have.property( "name", updatedLocalNodeConfig.node.name, ); expect(existingNode).to.have.property( "location", updatedLocalNodeConfig.node.location, ); expect(existingNode).to.have.deep.property( "capabilities", updatedLocalNodeConfig.node.capabilities, ); // Get the system from the DB const existsingSystem = await getSystemByName("Testing P25 System Name"); // Assert that there is no existing system in the DB expect(existsingSystem).to.be.null; // Wait for the update const node_system_update = new Promise((res) => { clientSocket.on("node-update-successful", async () => { res(); }); }); // Emit the update command clientSocket.emit("node-update", updatedLocalNodeConfig); // Wait for the successful update event await node_system_update; // Now we need to check if the system is added to the database // We can use getNodeByNuid again to verify if the node was added correctly const updatedNode = await getNodeByNuid(localNodeConfig.node.nuid); console.log("Updated Node:", updatedNode); // Assert that the node is added correctly expect(updatedNode).to.have.property("_id"); // Check if _id property exists expect(updatedNode).to.have.property( "nuid", updatedLocalNodeConfig.node.nuid, ); expect(updatedNode).to.have.property( "name", updatedLocalNodeConfig.node.name, ); expect(updatedNode).to.have.property( "location", updatedLocalNodeConfig.node.location, ); expect(updatedNode).to.have.deep.property( "capabilities", updatedLocalNodeConfig.node.capabilities, ); // Get the updated system const addedSystem = await getSystemByName("Testing P25 System Name"); console.log("Added system:", addedSystem); expect(addedSystem).to.have.property("_id"); // Check if _id property exists expect(addedSystem).to.have.property("nodes"); // Check if nodes property exists expect(addedSystem.nodes).to.include(updatedLocalNodeConfig.node.nuid); // Check if this node ID is in the nodes array expect(addedSystem).to.have.deep.property( "frequencies", updatedLocalNodeConfig.nearbySystems["Testing P25 System Name"] .frequencies, ); expect(addedSystem).to.have.property( "mode", updatedLocalNodeConfig.nearbySystems["Testing P25 System Name"].mode, ); }); it("Should update a node and its nearby systems", async () => { // Get the existing node in the database const existingNode = await getNodeByNuid(localNodeConfig.node.nuid); // Assert that existingNode matches the updatedLocalNodeConfig expect(existingNode).to.have.property("_id"); // Check if _id property exists expect(existingNode).to.have.property( "nuid", updatedLocalNodeConfig.node.nuid, ); expect(existingNode).to.have.property( "name", updatedLocalNodeConfig.node.name, ); expect(existingNode).to.have.property( "location", updatedLocalNodeConfig.node.location, ); expect(existingNode).to.have.deep.property( "capabilities", updatedLocalNodeConfig.node.capabilities, ); // Get the updated system const existingSystem = await getSystemByName("Testing P25 System Name"); expect(existingSystem).to.have.property("_id"); // Check if _id property exists expect(existingSystem).to.have.property("nodes"); // Check if nodes property exists expect(existingSystem.nodes).to.include(updatedLocalNodeConfig.node.nuid); // Check if this node ID is in the nodes array expect(existingSystem).to.have.deep.property( "frequencies", updatedLocalNodeConfig.nearbySystems["Testing P25 System Name"] .frequencies, ); expect(existingSystem).to.have.property( "mode", updatedLocalNodeConfig.nearbySystems["Testing P25 System Name"].mode, ); // Wait for the update const node_update = new Promise((res) => { clientSocket.on("node-update-successful", async () => { res(); }); }); // Emit the update command clientSocket.emit("node-update", localNodeConfig); // Wait for the successful update event await node_update; const updatedNode = await getNodeByNuid(localNodeConfig.node.nuid); console.log("Updated Node:", updatedNode); // Assert that the node is added correctly expect(updatedNode).to.have.property("_id"); // Check if _id property exists expect(updatedNode).to.have.property("nuid", localNodeConfig.node.nuid); expect(updatedNode).to.have.property("name", localNodeConfig.node.name); expect(updatedNode).to.have.property( "location", localNodeConfig.node.location, ); expect(updatedNode).to.have.deep.property( "capabilities", localNodeConfig.node.capabilities, ); // Get the updated system const updatedSystem = await getSystemByName("Testing P25 System Name"); console.log("Updated system:", updatedSystem); expect(updatedSystem).to.have.property("_id"); // Check if _id property exists expect(updatedSystem).to.have.property("nodes"); // Check if nodes property exists expect(updatedSystem.nodes).include(localNodeConfig.node.nuid); // Check if this node ID is in the nodes array expect(updatedSystem).to.have.deep.property( "frequencies", localNodeConfig.nearbySystems["Testing P25 System Name"].frequencies, ); expect(updatedSystem).to.have.property( "mode", localNodeConfig.nearbySystems["Testing P25 System Name"].mode, ); }); }); // Test getNodeCurrentListeningSystem describe("Get Node Current Listening System", () => { it("Should return the current listening system for the node", async () => { // Simulate that the client socket is listening to a system const listeningSystem = "Testing P25 System Name"; // Emit the event to the server and wait for the response const nodeReply = new Promise((resolve) => { clientSocket.once("node-check-current-system", (callback) => { // Simulate receiving the current listening system from the client callback(listeningSystem); }); // Call the function to get the current listening system const currentSystem = getNodeCurrentListeningSystem(serverClientSocket); resolve(currentSystem); }); // Wait for the promise to resolve const currentSystem = await nodeReply; // Assert that the current system matches the expected listening system expect(currentSystem).to.equal(listeningSystem); }); }); // Test checkIfNodeIsConnectedToVC describe("Check if Node is Connected to VC", () => { it("Should correctly determine if the node is connected to a voice channel", async () => { // Simulate that the client socket is listening to a system const isConnectedToVC = true; const guildId = "mockGuildId"; // Emit the event to the server and wait for the response const nodeReply = new Promise((resolve) => { clientSocket.once( "node-check-connected-status", (passedGuildId, callback) => { // Check if the passed guild ID matches the expected guild ID expect(passedGuildId).to.equal(guildId); // Simulate receiving the connection status from the client callback(isConnectedToVC); }, ); // Call the function to check if the node is connected to a voice channel const response = checkIfNodeIsConnectedToVC( nodeIo, guildId, localNodeConfig.node.nuid, ); resolve(response); }); // Wait for the promise to resolve const response = await nodeReply; // Assert that the response matches the expected connection status expect(response).to.equal(isConnectedToVC); }); }); // Test checkIfNodeHasOpenDiscordClient describe("Check if Node has an open discord client", () => { it("Should correctly determine if the node has an open Discord client", async () => { const isDiscordOpen = true; // Emit the event to the server and wait for the response const nodeReply = new Promise((resolve) => { clientSocket.once("node-check-discord-open-client", (callback) => { // Simulate receiving the client status from the client callback(isDiscordOpen); }); // Call the function to check if the node has an open Discord client const response = checkIfNodeHasOpenDiscordClient(serverClientSocket); resolve(response); }); // Wait for the promise to resolve const response = await nodeReply; // Assert that the response matches the expected client status expect(response).to.equal(isDiscordOpen); }); }); // Test getNodeDiscordUsername describe("Get the discord username from the client", () => { it("Should request the username from a specific client", async () => { const discordUsername = "Test Discord Username"; const guildId = "mockGuildId"; // Emit the event to the server and wait for the response const nodeReply = new Promise((resolve) => { clientSocket.once( "node-get-discord-username", (passedGuildId, callback) => { // Check if the passed guild ID matches the expected guild ID expect(passedGuildId).to.equal(guildId); // Simulate receiving the Discord username from the client callback(discordUsername); }, ); // Call the function to get the Discord username const response = getNodeDiscordUsername(serverClientSocket, guildId); resolve(response); }); // Wait for the promise to resolve const response = await nodeReply; // Assert that the response matches the expected Discord username expect(response).to.equal(discordUsername); }); }); // Test getNodeDiscordID describe("Get the discord ID from the client", () => { it("Should get the ID from the client", async () => { // Mocked Discord ID const discordId = "mockDiscordID"; // Emit the event to the server and wait for the response const nodeReply = new Promise((resolve) => { // Listen for the 'node-get-discord-id' event from the server clientSocket.once("node-get-discord-id", (callback) => { // Simulate receiving the Discord ID from the client callback(discordId); }); // Call the function to get the Discord ID const response = getNodeDiscordID(serverClientSocket); resolve(response); }); // Wait for the promise to resolve const response = await nodeReply; // Assert that the response matches the expected Discord ID expect(response).to.equal(discordId); }); }); // Test requestNodeJoinSystem describe("Request Node Join System", () => { it("Should send a request to the node to join a system", async () => { const systemName = "mockSystemName"; const channelId = "mockChannelId"; const token = "mockToken"; // Emit the event to the server and wait for the response await new Promise(async (resolve) => { clientSocket.once("node-join", (joinData) => { // Check if the passed system ID matches the expected system ID expect(joinData.clientID).to.equal(token); expect(joinData.channelID).to.equal(channelId); expect(joinData.system).to.equal(systemName); // Simulate receiving a success callback from the client resolve(); }); // Call the function to request joining a system requestNodeJoinSystem(serverClientSocket, systemName, channelId, token); }); }); }); // Test requestNodeLeaveSystem describe("Request Node Leave System", () => { it("Should send a request to the node to leave a given server", async () => { const guildId = "mockGuildId"; // Emit the event to the server and wait for the response await new Promise(async (resolve) => { clientSocket.once("node-leave", (passedGuildId) => { // Check if the passed system ID matches the expected system ID expect(passedGuildId).to.equal(guildId); // Simulate receiving a success callback from the client resolve(); }); // Call the function to request joining a system requestBotLeaveServer(serverClientSocket, guildId); }); }); }); // Test requestNodeUpdate describe("Request Node Update", () => { it("Should send the node a request to check for an update", async () => { // Emit the event to the server and wait for the response await new Promise((resolve) => { clientSocket.once("node-request-update", (callback) => { // Simulate an out of date request expect(callback); callback(true); }); // Call the function to request updating node information const response = requestNodeUpdate(serverClientSocket); resolve(response); }); }); }); // Test nodeDisconnectWrapper describe("Node Disconnect Wrapper", () => { it("Should disconnect the node and trigger cleanup actions", async () => { // Mock the socket ID const socketId = "mockSocketId"; // Call the nodeDisconnectWrapper function const result = await nodeDisconnectWrapper(socketId); // Assert that the result is as expected (if any) expect(result).to.be.undefined; }); }); });