diff --git a/src/bot.js b/src/bot.js index c3f145e..f9054d7 100644 --- a/src/bot.js +++ b/src/bot.js @@ -1,4 +1,4 @@ -import { Client, Events } from '@fluxerjs/core'; +import { Client, Events, Message } from '@fluxerjs/core'; import { messageHelper } from "./helpers/messageHelper.js"; import {enums} from "./enums.js"; import {commands} from "./commands.js"; @@ -15,15 +15,25 @@ if (!token) { process.exit(1); } -const client = new Client({ intents: 0 }); +export const client = new Client({ intents: 0 }); client.on(Events.MessageCreate, async (message) => { - try { - // Ignore bots and messages without content - if (message.author.bot || !message.content) return; + await handleMessageCreate(message); +}); +/** + * Calls functions based off the contents of a message object. + * + * @async + * @param {Message} message - The message object + * + **/ +export const handleMessageCreate = async function(message) { + try { // Parse command and arguments const content = message.content.trim(); + // Ignore bots and messages without content + if (message.author.bot || content.length === 0) return; // If message doesn't start with the bot prefix, it could still be a message with a proxy tag. If it's not, return. if (!content.startsWith(messageHelper.prefix)) { @@ -53,7 +63,7 @@ client.on(Events.MessageCreate, async (message) => { console.error(error); // return await message.reply(error.message); } -}); +} client.on(Events.Ready, () => { console.log(`Logged in as ${client.user?.username}`); @@ -72,10 +82,12 @@ function printGuilds() { const debouncePrintGuilds = utils.debounce(printGuilds, 2000); const debounceLogin = utils.debounce(client.login, 60000); -try { - await debounceLogin(token); - // await db.check_connection(); -} catch (err) { - console.error('Login failed:', err); - process.exit(1); -} \ No newline at end of file +(async () => { + try { + await debounceLogin(token); + // await db.check_connection(); + } catch (err) { + console.error('Login failed:', err); + process.exit(1); + } +}) \ No newline at end of file diff --git a/tests/bot.test.js b/tests/bot.test.js index 7455ae7..a824fb5 100644 --- a/tests/bot.test.js +++ b/tests/bot.test.js @@ -1,10 +1,64 @@ -const { Client, Events } = require('@fluxerjs/core'); -const { messageHelper } = require("../src/helpers/messageHelper.js"); +const env = require('dotenv'); const {enums} = require("../src/enums.js"); + +jest.mock('@fluxerjs/core', () => { + return { + Events: { + MessageCreate: jest.fn(), + Ready: jest.fn(), + GuildCreate: jest.fn(), + }, + Client: jest.fn().mockImplementation(() => { + return { + on: jest.fn(), + intents: 0 + } + }), + Message: jest.fn() + }; +}); + +jest.mock("../src/helpers/messageHelper.js", () => { + return { + messageHelper: { + parseCommandArgs: jest.fn(), + prefix: "pf;" + } + } +}); + +jest.mock("../src/helpers/webhookHelper.js", () => { + return { + webhookHelper: { + sendMessageAsMember: jest.fn() + } + } +}) +jest.mock("../src/helpers/utils.js", () => { + return { + utils: { + debounce: jest.fn() + } + } +}) + +jest.mock("../src/commands.js", () => { + return { + commands: { + get: jest.fn() + } + } +}) + + +const {Client, Events} = require('@fluxerjs/core'); +const {messageHelper} = require("../src/helpers/messageHelper.js"); + const {commands} = require("../src/commands.js"); const {webhookHelper} = require("../src/helpers/webhookHelper.js"); -const env = require('dotenv'); + const {utils} = require("../src/helpers/utils.js"); +const {handleMessageCreate, client} = require("../src/bot.js"); env.config(); @@ -15,6 +69,189 @@ describe('bot', () => { jest.clearAllMocks(); }) + describe('handleMessageCreate', () => { + + + test('on message creation, if message is from bot, return', () => { + // Arrange + const message = { + author: { + bot: true + } + } + // Act + return handleMessageCreate(message).then((res) => { + expect(res).toBe(undefined); + }); + }) + + test('on message creation, if message is empty, return', () => { + // Arrange + const message = { + content: " ", + author: { + bot: false + } + } + // Act + return handleMessageCreate(message).then((res) => { + // Assert + expect(res).toBe(undefined); + }); + }) + + test("if message doesn't start with bot prefix, call sendMessageAsMember", () => { + // Arrange + webhookHelper.sendMessageAsMember.mockResolvedValue(); + const message = { + content: "hello", + author: { + bot: false + } + } + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledTimes(1); + expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledWith(client, message) + }); + }) + + test("if sendMessageAsMember returns error, log error", () => { + // Arrange + webhookHelper.sendMessageAsMember.mockImplementation(() => { + throw Error("error") + }); + const message = { + content: "hello", + author: { + bot: false + } + } + jest.mock('console', () => { + return {error: jest.fn()} + }) + // Act + return handleMessageCreate(message).catch(() => { + // Assert + expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledTimes(1); + expect(webhookHelper.sendMessageAsMember).toHaveBeenCalledWith(client, message) + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledWith(new Error('error')) + }); + }) + + test("if no command after prefix, return correct enum", () => { + // Arrange + const message = { + content: "pf;", + author: { + bot: false + }, + reply: jest.fn() + } + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(message.reply).toHaveBeenCalledTimes(1); + expect(message.reply).toHaveBeenCalledWith(enums.help.SHORT_DESC_PLURALFLUX); + expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled(); + }); + }) + + test("if command after prefix, call parseCommandArgs and commands.get", () => { + // Arrange + const message = { + content: "pf;help", + author: { + bot: false + }, + reply: jest.fn() + } + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(messageHelper.parseCommandArgs).toHaveBeenCalledTimes(1); + expect(messageHelper.parseCommandArgs).toHaveBeenCalledWith('pf;help', 'help'); + expect(commands.get).toHaveBeenCalledTimes(1); + expect(commands.get).toHaveBeenCalledWith('help'); + expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled(); + }); + }) + + test("if command exists, call command.execute", () => { + // Arrange + const message = { + content: "pf;member test", + author: { + bot: false + }, + reply: jest.fn() + } + const command = { + execute: jest.fn() + } + messageHelper.parseCommandArgs = jest.fn().mockReturnValue(['test']); + commands.get = jest.fn().mockReturnValue(command); + command.execute = jest.fn().mockResolvedValue(); + + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(command.execute).toHaveBeenCalledTimes(1); + expect(command.execute).toHaveBeenCalledWith(message, client, ['test']); + expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled(); + }); + }) + + test("if command.execute returns error, log error", () => { + // Arrange + const command = { + execute: jest.fn() + } + commands.get = jest.fn().mockReturnValue(command); + command.execute.mockImplementation(() => { + throw Error("error") + }); + // Arrange + const message = { + content: "pf;member test", + author: { + bot: false + }, + reply: jest.fn() + } + jest.mock('console', () => { + return {error: jest.fn()} + }) + // Act + return handleMessageCreate(message).catch(() => { + // Assert + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledWith(new Error('error')) + }); + }) + + test("if command does not exist, return correct enum", () => { + // Arrange + commands.get = jest.fn().mockReturnValue(); + const message = { + content: "pf;asdfjlas", + author: { + bot: false + }, + reply: jest.fn() + } + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(message.reply).toHaveBeenCalledWith(enums.err.COMMAND_NOT_RECOGNIZED); + expect(message.reply).toHaveBeenCalledTimes(1); + }); + }) + }) + + afterEach(() => { // restore the spy created with spyOn jest.restoreAllMocks();