diff --git a/src/buttonEvents/Claim.ts b/src/buttonEvents/Claim.ts index 27b82b1..97ee54d 100644 --- a/src/buttonEvents/Claim.ts +++ b/src/buttonEvents/Claim.ts @@ -73,6 +73,8 @@ export default class Claim extends ButtonEvent { const card = GetCardsHelper.GetCardByCardNumber(cardNumber); if (!card) { + AppLogger.LogError("Button/Claim", `Unable to find card, ${cardNumber}`); + return; } diff --git a/src/buttonEvents/Effects/Use.ts b/src/buttonEvents/Effects/Use.ts index 25c2ba2..2b38bd8 100644 --- a/src/buttonEvents/Effects/Use.ts +++ b/src/buttonEvents/Effects/Use.ts @@ -3,6 +3,7 @@ import { EffectDetails } from "../../constants/EffectDetails"; import EffectHelper from "../../helpers/EffectHelper"; import EmbedColours from "../../constants/EmbedColours"; import TimeLengthInput from "../../helpers/TimeLengthInput"; +import AppLogger from "../../client/appLogger"; export default async function Use(interaction: ButtonInteraction) { const subaction = interaction.customId.split(" ")[2]; @@ -23,6 +24,8 @@ export async function UseConfirm(interaction: ButtonInteraction) { const effectDetail = EffectDetails.get(id); if (!effectDetail) { + AppLogger.LogError("Button/Effects/Use", `Effect not found, ${id}`); + await interaction.reply("Effect not found in system!"); return; } @@ -33,46 +36,46 @@ export async function UseConfirm(interaction: ButtonInteraction) { const result = await EffectHelper.UseEffect(interaction.user.id, id, whenExpires); - if (result) { - const embed = new EmbedBuilder() - .setTitle("Effect Used") - .setDescription("You now have an active effect!") - .setColor(EmbedColours.Green) - .addFields([ - { - name: "Effect", - value: effectDetail.friendlyName, - inline: true, - }, - { - name: "Expires", - value: ``, - inline: true, - }, - ]); - - const row = new ActionRowBuilder() - .addComponents([ - new ButtonBuilder() - .setLabel("Confirm") - .setCustomId(`effects use confirm ${effectDetail.id}`) - .setStyle(ButtonStyle.Primary) - .setDisabled(true), - new ButtonBuilder() - .setLabel("Cancel") - .setCustomId(`effects use cancel ${effectDetail.id}`) - .setStyle(ButtonStyle.Danger) - .setDisabled(true), - ]); - - await interaction.update({ - embeds: [ embed ], - components: [ row ], - }); + if (!result) { + await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); return; } - await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); + const embed = new EmbedBuilder() + .setTitle("Effect Used") + .setDescription("You now have an active effect!") + .setColor(EmbedColours.Green) + .addFields([ + { + name: "Effect", + value: effectDetail.friendlyName, + inline: true, + }, + { + name: "Expires", + value: ``, + inline: true, + }, + ]); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setLabel("Confirm") + .setCustomId(`effects use confirm ${effectDetail.id}`) + .setStyle(ButtonStyle.Primary) + .setDisabled(true), + new ButtonBuilder() + .setLabel("Cancel") + .setCustomId(`effects use cancel ${effectDetail.id}`) + .setStyle(ButtonStyle.Danger) + .setDisabled(true), + ]); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); } export async function UseCancel(interaction: ButtonInteraction) { @@ -81,6 +84,8 @@ export async function UseCancel(interaction: ButtonInteraction) { const effectDetail = EffectDetails.get(id); if (!effectDetail) { + AppLogger.LogError("Button/Effects/Cancel", `Effect not found, ${id}`); + await interaction.reply("Effect not found in system!"); return; } diff --git a/src/helpers/EffectHelper.ts b/src/helpers/EffectHelper.ts index 4c45022..d4673f4 100644 --- a/src/helpers/EffectHelper.ts +++ b/src/helpers/EffectHelper.ts @@ -1,7 +1,7 @@ -import {ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder} from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; import UserEffect from "../database/entities/app/UserEffect"; import EmbedColours from "../constants/EmbedColours"; -import {EffectDetails} from "../constants/EffectDetails"; +import { EffectDetails } from "../constants/EffectDetails"; export default class EffectHelper { public static async AddEffectToUserInventory(userId: string, name: string, quantity: number = 1) { @@ -23,11 +23,9 @@ export default class EffectHelper { const effect = await UserEffect.FetchOneByUserIdAndName(userId, name); - if (!effect) return false; + effect!.UseEffect(whenExpires); - effect.UseEffect(whenExpires); - - await effect.Save(UserEffect, effect); + await effect!.Save(UserEffect, effect!); return true; } diff --git a/tests/buttonEvents/Claim.test.ts b/tests/buttonEvents/Claim.test.ts new file mode 100644 index 0000000..beab3c8 --- /dev/null +++ b/tests/buttonEvents/Claim.test.ts @@ -0,0 +1,23 @@ +test.todo("GIVEN interaction.guild is null, EXPECT nothing to happen"); + +test.todo("GIVEN interaction.guildId is null, EXPECT nothing to happen"); + +test.todo("GIVEN interaction.channel is null, EXPECT nothing to happen"); + +test.todo("GIVEN channel is not sendable, EXPECT nothing to happen"); + +test.todo("GIVEN interaction.message was created more than 5 minutes ago, EXPECT error"); + +test.todo("GIVEN user.RemoveCurrency fails, EXPECT error"); + +test.todo("GIVEN the card has already been claimed, EXPECT error"); + +test.todo("GIVEN the current drop is the latest AND the current user is NOT the one who dropped it, EXPECT error"); + +test.todo("GIVEN the user already has the card in their inventory, EXPECT the entity quantity to be +1"); + +test.todo("GIVEN user does NOT have the card in their inventory, EXPECT a new entity to be created"); + +test.todo("GIVEN card can not be found in the bot, EXPECT error logged"); + +test.todo("EXPECT message to be edited"); \ No newline at end of file diff --git a/tests/buttonEvents/Effects.test.ts b/tests/buttonEvents/Effects.test.ts index 8a2f755..18ccc31 100644 --- a/tests/buttonEvents/Effects.test.ts +++ b/tests/buttonEvents/Effects.test.ts @@ -1,135 +1,5 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, EmbedBuilder } from "discord.js"; -import Effects from "../../src/buttonEvents/Effects"; -import EffectHelper from "../../src/helpers/EffectHelper"; -import { EffectDetails } from "../../src/constants/EffectDetails"; -import TimeLengthInput from "../../src/helpers/TimeLengthInput"; +test.todo("GIVEN action is list, EXPECT list function to be called"); -describe("Effects", () => { - let interaction: ButtonInteraction; - let effects: Effects; +test.todo("GIVEN action is use, EXPECT use function to be called"); - beforeEach(() => { - interaction = { - customId: "effects list 1", - user: { id: "123" }, - reply: jest.fn(), - update: jest.fn(), - } as unknown as ButtonInteraction; - effects = new Effects(); - }); - - it("should call List method when action is 'list'", async () => { - const listSpy = jest.spyOn(effects, "List").mockImplementation(async () => {}); - - await effects.execute(interaction); - - expect(listSpy).toHaveBeenCalledWith(interaction); - }); - - it("should call Use method when action is 'use'", async () => { - interaction.customId = "effects use confirm 1"; - const useSpy = jest.spyOn(effects, "Use").mockImplementation(async () => {}); - - await effects.execute(interaction); - - expect(useSpy).toHaveBeenCalledWith(interaction); - }); - - it("should reply with error message when page option is not a valid number", async () => { - interaction.customId = "effects list invalid"; - await effects.execute(interaction); - - expect(interaction.reply).toHaveBeenCalledWith("Page option is not a valid number"); - }); - - it("should update interaction with generated embed and row", async () => { - const mockEmbed = { - embed: new EmbedBuilder(), - row: new ActionRowBuilder() - }; - - jest.spyOn(EffectHelper, "GenerateEffectEmbed").mockResolvedValue(mockEmbed); - - await effects.List(interaction); - - expect(interaction.update).toHaveBeenCalledWith({ - embeds: [mockEmbed.embed], - components: [mockEmbed.row], - }); - }); - - it("should call UseConfirm method when subaction is 'confirm'", async () => { - interaction.customId = "effects use confirm 1"; - const useConfirmSpy = jest.spyOn(effects, "UseConfirm").mockImplementation(async () => {}); - - await effects.Use(interaction); - - expect(useConfirmSpy).toHaveBeenCalledWith(interaction); - }); - - it("should call UseCancel method when subaction is 'cancel'", async () => { - interaction.customId = "effects use cancel 1"; - const useCancelSpy = jest.spyOn(effects, "UseCancel").mockImplementation(async () => {}); - - await effects.Use(interaction); - - expect(useCancelSpy).toHaveBeenCalledWith(interaction); - }); - - it("should reply with error message when effect detail is not found in UseConfirm", async () => { - interaction.customId = "effects use confirm invalid"; - await effects.UseConfirm(interaction); - - expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!"); - }); - - it("should reply with error message when effect detail is not found in UseCancel", async () => { - interaction.customId = "effects use cancel invalid"; - await effects.UseCancel(interaction); - - expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!"); - }); - - it("should update interaction with embed and row when effect is used successfully", async () => { - const mockEffectDetail = { id: "1", friendlyName: "Test Effect", duration: 1000, cost: 10, cooldown: 5000 }; - const mockResult = true; - - jest.spyOn(EffectDetails, "get").mockReturnValue(mockEffectDetail); - jest.spyOn(EffectHelper, "UseEffect").mockResolvedValue(mockResult); - - await effects.UseConfirm(interaction); - - expect(interaction.update).toHaveBeenCalledWith(expect.objectContaining({ - embeds: expect.any(Array), - components: expect.any(Array), - })); - }); - - it("should reply with error message when effect is not used successfully", async () => { - const mockEffectDetail = { id: "1", friendlyName: "Test Effect", duration: 1000, cost: 0, cooldown: 0 }; - const mockResult = false; - - jest.spyOn(EffectDetails, "get").mockReturnValue(mockEffectDetail); - jest.spyOn(EffectHelper, "UseEffect").mockResolvedValue(mockResult); - - await effects.UseConfirm(interaction); - - expect(interaction.reply).toHaveBeenCalledWith("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown"); - }); - - it("should update interaction with embed and row when effect use is cancelled", async () => { - const mockEffectDetail = { id: "1", friendlyName: "Test Effect", duration: 1000, cost: 0, cooldown: 0 }; - - jest.spyOn(EffectDetails, "get").mockReturnValue(mockEffectDetail); - jest.spyOn(TimeLengthInput, "ConvertFromMilliseconds").mockReturnValue({ - GetLengthShort: () => "1s", - } as TimeLengthInput); - - await effects.UseCancel(interaction); - - expect(interaction.update).toHaveBeenCalledWith(expect.objectContaining({ - embeds: expect.any(Array), - components: expect.any(Array), - })); - }); -}); \ No newline at end of file +test.todo("GIVEN action is unknown, EXPECT nothing to be called"); \ No newline at end of file diff --git a/tests/buttonEvents/Effects/List.test.ts b/tests/buttonEvents/Effects/List.test.ts new file mode 100644 index 0000000..af894b4 --- /dev/null +++ b/tests/buttonEvents/Effects/List.test.ts @@ -0,0 +1,3 @@ +test.todo("GIVEN pageOption is NOT a number, EXPECT error"); + +test.todo("GIVEN pageOption is a number, EXPECT interaction updated"); \ No newline at end of file diff --git a/tests/buttonEvents/Effects/Use.test.ts b/tests/buttonEvents/Effects/Use.test.ts new file mode 100644 index 0000000..5cb6912 --- /dev/null +++ b/tests/buttonEvents/Effects/Use.test.ts @@ -0,0 +1,21 @@ +describe("Use", () => { + test.todo("GIVEN subaction is confirm, EXPECT UseConfirm to be called"); + + test.todo("GIVEN subaction is cancel, EXPECT UseCancel to be called"); + + test.todo("GIVEN subaction is unknown, EXPECT nothing to be called"); +}); + +describe("UseConfirm", () => { + test.todo("GIVEN effectDetail is not found, EXPECT error"); + + test.todo("GIVEN EffectHelper.UseEffect failed, EXPECT error"); + + test.todo("GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated"); +}); + +describe("UseCancel", () => { + test.todo("GIVEN effectDetail is not found, EXPECT error"); + + test.todo("GIVEN effectDetail is found, EXPECT interaction updated"); +}); \ No newline at end of file diff --git a/tests/commands/effects.test.ts b/tests/commands/effects.test.ts new file mode 100644 index 0000000..20c8f35 --- /dev/null +++ b/tests/commands/effects.test.ts @@ -0,0 +1,27 @@ +describe("constructor", () => { + test.todo("EXPECT CommandBuilder to be defined"); +}); + +describe("execute", () => { + test.todo("GIVEN interaction is NOT a ChatInputCommand, EXPECT nothing to happen"); + + test.todo("GIVEN subcommand is list, EXPECT list function to be called"); + + test.todo("GIVEN subcommand is use, EXPECT use function to be called"); +}); + +describe("List", () => { + test.todo("GIVEN pageOption is null, EXPECT page to default to 0"); + + test.todo("GIVEN pageOption.value is undefined, EXPECT page to default to 0"); + + test.todo("EXPECT interaction to be replied"); +}); + +describe("Use", () => { + test.todo("GIVEN effectDetail is not found, EXPECT error"); + + test.todo("GIVEN user can not use effect, EXPECT error"); + + test.todo("EXPECT interaction to be replied"); +}); \ No newline at end of file diff --git a/tests/helpers/EffectHelper.test.ts b/tests/helpers/EffectHelper.test.ts new file mode 100644 index 0000000..e41c2bc --- /dev/null +++ b/tests/helpers/EffectHelper.test.ts @@ -0,0 +1,41 @@ +describe("AddEffectToUserInventory", () => { + test.todo("GIVEN effect is found in database, EXPECT effect unused to be incremented"); + + test.todo("GIVEN effect is NOT found in database, EXPECT new effect to be created"); +}); + +describe("UseEffect", () => { + test.todo("GIVEN user can not use effect, EXPECT false returned"); + + test.todo("GIVEN user has effect, EXPECT entity to be updated AND true returned"); +}); + +describe("CanUseEffect", () => { + test.todo("GIVEN effect is not in database, EXPECT false returned"); + + test.todo("GIVEN user does not have any of the effect unused, EXPECT false returned"); + + test.todo("GIVEN effectDetail can not be found, EXPECT false returned"); + + test.todo("GIVEN effect has NOT passed the cooldown, EXPECT false returned"); + + test.todo("GIVEN effect has passed the cooldown, EXPECT true returned"); + + test.todo("GIVEN effect does not have a WhenExpires date supplied, EXPECT true returned"); +}); + +describe("HasEffect", () => { + test.todo("GIVEN effect is NOT found in database, EXPECT false returned"); + + test.todo("GIVEN effect does NOT have an expiry date, EXPECT false returned"); + + test.todo("GIVEN effect.WhenExpires is in the future, EXPECT false returned"); + + test.todo("GIVEN effect.WhenExpires is in the past, EXPECT true returned"); +}); + +describe("GenerateEffectEmbed", () => { + test.todo("GIVEN user has no effects, EXPECT embed to be generated"); + + test.todo("GIVEN user has some effects, EXPECT embed to be generated"); +}); \ No newline at end of file diff --git a/tests/helpers/TimeLengthInput.test.ts b/tests/helpers/TimeLengthInput.test.ts new file mode 100644 index 0000000..6a23d67 --- /dev/null +++ b/tests/helpers/TimeLengthInput.test.ts @@ -0,0 +1,38 @@ +import TimeLengthInput from "../../src/helpers/TimeLengthInput"; + +describe("ConvertFromMilliseconds", () => { + test("EXPECT 1000ms to be outputted as a second", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(1000); + expect(timeLength.GetLengthShort()).toBe("1s"); + }); + + test("EXPECT 60000ms to be outputted as a minute", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(60000); + expect(timeLength.GetLengthShort()).toBe("1m"); + }); + + test("EXPECT 3600000ms to be outputted as an hour", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(3600000); + expect(timeLength.GetLengthShort()).toBe("1h"); + }); + + test("EXPECT 86400000ms to be outputted as a day", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(86400000); + expect(timeLength.GetLengthShort()).toBe("1d"); + }); + + test("EXPECT a combination to be outputted correctly", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(90061000); + expect(timeLength.GetLengthShort()).toBe("1d 1h 1m 1s"); + }); + + test("EXPECT 0ms to be outputted as empty", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(0); + expect(timeLength.GetLengthShort()).toBe(""); + }); + + test("EXPECT 123456789ms to be outputted correctly", () => { + const timeLength = TimeLengthInput.ConvertFromMilliseconds(123456789); + expect(timeLength.GetLengthShort()).toBe("1d 10h 17m 36s"); + }); +}); \ No newline at end of file