diff --git a/src/buttonEvents/Claim.ts b/src/buttonEvents/Claim.ts index 97ee54d..d139f42 100644 --- a/src/buttonEvents/Claim.ts +++ b/src/buttonEvents/Claim.ts @@ -1,7 +1,6 @@ import { ButtonInteraction } from "discord.js"; import { ButtonEvent } from "../type/buttonEvent"; import Inventory from "../database/entities/app/Inventory"; -import { CoreClient } from "../client/client"; import { default as eClaim } from "../database/entities/app/Claim"; import AppLogger from "../client/appLogger"; import User from "../database/entities/app/User"; @@ -23,10 +22,10 @@ export default class Claim extends ButtonEvent { const userId = interaction.user.id; const whenDropped = interaction.message.createdAt; - const lastClaimableDate = new Date(Date.now() - (1000 * 60 * 5)); // 5 minutes ago + const lastClaimableDate = new Date(Date.now() - (1000 * 60 * 2)); // 2 minutes ago if (whenDropped < lastClaimableDate) { - await interaction.channel.send(`${interaction.user}, Cards can only be claimed within 5 minutes of it being dropped!`); + await interaction.channel.send(`${interaction.user}, Cards can only be claimed within 2 minutes of it being dropped!`); return; } @@ -36,11 +35,6 @@ export default class Claim extends ButtonEvent { AppLogger.LogSilly("Button/Claim", `${user.Id} has ${user.Currency} currency`); - if (!user.RemoveCurrency(CardConstants.ClaimCost)) { - await interaction.channel.send(`${interaction.user}, Not enough currency! You need ${CardConstants.ClaimCost} currency, you have ${user.Currency}!`); - return; - } - const claimed = await eClaim.FetchOneByClaimId(claimId); if (claimed) { @@ -48,13 +42,6 @@ export default class Claim extends ButtonEvent { return; } - if (claimId == CoreClient.ClaimId && userId != droppedBy) { - await interaction.channel.send(`${interaction.user}, The latest dropped card can only be claimed by the user who dropped it!`); - return; - } - - await user.Save(User, user); - let inventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber); if (!inventory) { diff --git a/src/buttonEvents/Reroll.ts b/src/buttonEvents/Reroll.ts index e25e474..6d97024 100644 --- a/src/buttonEvents/Reroll.ts +++ b/src/buttonEvents/Reroll.ts @@ -35,11 +35,13 @@ export default class Reroll extends ButtonEvent { AppLogger.LogInfo("Commands/Drop", `New user (${interaction.user.id}) saved to the database`); } - if (user.Currency < CardConstants.ClaimCost) { + if (!user.RemoveCurrency(CardConstants.ClaimCost)) { await interaction.reply(`Not enough currency! You need ${CardConstants.ClaimCost} currency, you have ${user.Currency}!`); return; } + await user.Save(User, user); + const randomCard = await GetCardsHelper.FetchCard(interaction.user.id); if (!randomCard) { @@ -78,8 +80,6 @@ export default class Reroll extends ButtonEvent { files: files, components: [ row ], }); - - CoreClient.ClaimId = claimId; } catch (e) { AppLogger.LogError("Button/Reroll", `Error sending next drop for card ${randomCard.card.id}: ${e}`); diff --git a/src/client/client.ts b/src/client/client.ts index 87b496e..57551fb 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -31,7 +31,6 @@ export class CoreClient extends Client { private _webhooks: Webhooks; private _timerHelper: TimerHelper; - public static ClaimId: string; public static Environment: Environment; public static AllowDrops: boolean; public static Cards: SeriesMetadata[]; diff --git a/src/commands/drop.ts b/src/commands/drop.ts index 6c93af6..ac8008c 100644 --- a/src/commands/drop.ts +++ b/src/commands/drop.ts @@ -43,11 +43,13 @@ export default class Drop extends Command { AppLogger.LogInfo("Commands/Drop", `New user (${interaction.user.id}) saved to the database`); } - if (user.Currency < CardConstants.ClaimCost) { + if (!user.RemoveCurrency(CardConstants.ClaimCost)) { await interaction.reply(ErrorMessages.NotEnoughCurrency(CardConstants.ClaimCost, user.Currency)); return; } + await user.Save(User, user); + const randomCard = await GetCardsHelper.FetchCard(interaction.user.id); if (!randomCard) { @@ -86,8 +88,6 @@ export default class Drop extends Command { components: [ row ], }); - CoreClient.ClaimId = claimId; - } catch (e) { AppLogger.LogError("Commands/Drop", `Error sending next drop for card ${randomCard.card.id}: ${e}`); diff --git a/src/commands/stage/dropnumber.ts b/src/commands/stage/dropnumber.ts index 2f81862..c61ed43 100644 --- a/src/commands/stage/dropnumber.ts +++ b/src/commands/stage/dropnumber.ts @@ -76,7 +76,5 @@ export default class Dropnumber extends Command { AppLogger.CatchError("Dropnumber", e); await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening"); } - - CoreClient.ClaimId = claimId; } } \ No newline at end of file diff --git a/src/commands/stage/droprarity.ts b/src/commands/stage/droprarity.ts index a535255..f776930 100644 --- a/src/commands/stage/droprarity.ts +++ b/src/commands/stage/droprarity.ts @@ -4,7 +4,6 @@ import { CardRarity, CardRarityChoices, CardRarityParse } from "../../constants/ import { readFileSync } from "fs"; import Inventory from "../../database/entities/app/Inventory"; import { v4 } from "uuid"; -import { CoreClient } from "../../client/client"; import path from "path"; import GetCardsHelper from "../../helpers/DropHelpers/GetCardsHelper"; import DropEmbedHelper from "../../helpers/DropHelpers/DropEmbedHelper"; @@ -42,7 +41,7 @@ export default class Droprarity extends Command { return; } - const card = await GetCardsHelper.GetRandomCardByRarity(rarityType); + const card = GetCardsHelper.GetRandomCardByRarity(rarityType); if (!card) { await interaction.reply("Card not found"); @@ -81,7 +80,5 @@ export default class Droprarity extends Command { AppLogger.CatchError("Droprarity", e); await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening"); } - - CoreClient.ClaimId = claimId; } } \ No newline at end of file diff --git a/src/helpers/DropHelpers/DropEmbedHelper.ts b/src/helpers/DropHelpers/DropEmbedHelper.ts index c739de7..08b5813 100644 --- a/src/helpers/DropHelpers/DropEmbedHelper.ts +++ b/src/helpers/DropHelpers/DropEmbedHelper.ts @@ -3,7 +3,6 @@ import { DropResult } from "../../contracts/SeriesMetadata"; import AppLogger from "../../client/appLogger"; import { CardRarityToColour, CardRarityToString } from "../../constants/CardRarity"; import StringTools from "../StringTools"; -import CardConstants from "../../constants/CardConstants"; export default class DropEmbedHelper { public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string, claimedBy?: string, currency?: number): EmbedBuilder { @@ -74,12 +73,16 @@ export default class DropEmbedHelper { .addComponents( new ButtonBuilder() .setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`) - .setLabel(`Claim (${CardConstants.ClaimCost} 🪙)`) - .setStyle(ButtonStyle.Primary) + .setLabel("Claim") + .setStyle(ButtonStyle.Success) .setDisabled(disabled), + new ButtonBuilder() + .setCustomId(`sacrifice confirm ${userId} ${drop.card.id} 1`) + .setLabel(`Sacrifice`) + .setStyle(ButtonStyle.Danger), new ButtonBuilder() .setCustomId("reroll") - .setLabel("Reroll") - .setStyle(ButtonStyle.Secondary)); + .setEmoji("🔁") + .setStyle(ButtonStyle.Primary),); } } \ No newline at end of file diff --git a/src/timers/PurgeClaims.ts b/src/timers/PurgeClaims.ts index a0ed9d0..4ca8902 100644 --- a/src/timers/PurgeClaims.ts +++ b/src/timers/PurgeClaims.ts @@ -4,7 +4,7 @@ import Claim from "../database/entities/app/Claim"; export default async function PurgeClaims() { const claims = await Claim.FetchAll(Claim); - const whenLastClaimable = new Date(Date.now() - (1000 * 60 * 5)); // 5 minutes ago + const whenLastClaimable = new Date(Date.now() - (1000 * 60 * 2)); // 2 minutes ago const expiredClaims = claims.filter(x => x.WhenCreated < whenLastClaimable); diff --git a/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts b/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts index 26818b3..128c25b 100644 --- a/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts +++ b/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts @@ -4,9 +4,14 @@ export default function GenerateCommandInteractionMock(options?: { subcommand?: string, }): CommandInteraction { return { + deferReply: jest.fn(), + editReply: jest.fn(), isChatInputCommand: jest.fn().mockReturnValue(true), options: { getSubcommand: jest.fn().mockReturnValue(options?.subcommand), }, + user: { + id: "userId", + }, }; } \ No newline at end of file diff --git a/tests/__types__/discord.js.ts b/tests/__types__/discord.js.ts index afd1469..3304af4 100644 --- a/tests/__types__/discord.js.ts +++ b/tests/__types__/discord.js.ts @@ -19,8 +19,13 @@ export type ButtonInteraction = { } export type CommandInteraction = { + deferReply: jest.Func, + editReply: jest.Func, isChatInputCommand: jest.Func, options: { getSubcommand: jest.Func, }, + user: { + id: string, + }, } \ No newline at end of file diff --git a/tests/buttonEvents/Claim.test.ts b/tests/buttonEvents/Claim.test.ts index d9e7e6f..1e7027c 100644 --- a/tests/buttonEvents/Claim.test.ts +++ b/tests/buttonEvents/Claim.test.ts @@ -1,7 +1,6 @@ import { ButtonInteraction, TextChannel } from "discord.js"; import Claim from "../../src/buttonEvents/Claim"; import { ButtonInteraction as ButtonInteractionType } from "../__types__/discord.js"; -import User from "../../src/database/entities/app/User"; import GenerateButtonInteractionMock from "../__functions__/discord.js/GenerateButtonInteractionMock"; jest.mock("../../src/client/appLogger"); @@ -85,25 +84,7 @@ test("GIVEN interaction.message was created more than 5 minutes ago, EXPECT erro // Assert expect(interaction.channel!.send).toHaveBeenCalledTimes(1); - expect(interaction.channel!.send).toHaveBeenCalledWith("[object Object], Cards can only be claimed within 5 minutes of it being dropped!"); - - expect(interaction.editReply).not.toHaveBeenCalled(); -}); - -test("GIVEN user.RemoveCurrency fails, EXPECT error", async () => { - // Arrange - User.FetchOneById = jest.fn().mockResolvedValue({ - RemoveCurrency: jest.fn().mockReturnValue(false), - Currency: 5, - }); - - // Act - const claim = new Claim(); - await claim.execute(interaction as unknown as ButtonInteraction); - - // Assert - expect(interaction.channel!.send).toHaveBeenCalledTimes(1); - expect(interaction.channel!.send).toHaveBeenCalledWith("[object Object], Not enough currency! You need 10 currency, you have 5!"); + expect(interaction.channel!.send).toHaveBeenCalledWith("[object Object], Cards can only be claimed within 2 minutes of it being dropped!"); expect(interaction.editReply).not.toHaveBeenCalled(); }); \ No newline at end of file diff --git a/tests/buttonEvents/Reroll.test.ts b/tests/buttonEvents/Reroll.test.ts new file mode 100644 index 0000000..2021aac --- /dev/null +++ b/tests/buttonEvents/Reroll.test.ts @@ -0,0 +1,7 @@ +describe("GIVEN valid conditions", () => { + test.todo("EXPECT user.RemoveCurrency to be called"); + + test.todo("GIVEN user is saved"); +}); + +test.todo("GIVEN user.RemoveCurrency fails, EXPECT error replied"); \ No newline at end of file diff --git a/tests/commands/drop.test.ts b/tests/commands/drop.test.ts new file mode 100644 index 0000000..81b5f4a --- /dev/null +++ b/tests/commands/drop.test.ts @@ -0,0 +1,142 @@ +import { CommandInteraction } from "discord.js"; +import Drop from "../../src/commands/drop"; +import GenerateCommandInteractionMock from "../__functions__/discord.js/GenerateCommandInteractionMock"; +import { CommandInteraction as CommandInteractionMock } from "../__types__/discord.js"; +import { CoreClient } from "../../src/client/client"; +import Config from "../../src/database/entities/app/Config"; +import User from "../../src/database/entities/app/User"; +import GetCardsHelper from "../../src/helpers/DropHelpers/GetCardsHelper"; +import Inventory from "../../src/database/entities/app/Inventory"; +import DropEmbedHelper from "../../src/helpers/DropHelpers/DropEmbedHelper"; +import CardConstants from "../../src/constants/CardConstants"; +import * as uuid from "uuid"; + +jest.mock("../../src/database/entities/app/Config"); +jest.mock("../../src/database/entities/app/User"); +jest.mock("../../src/helpers/DropHelpers/GetCardsHelper"); +jest.mock("../../src/database/entities/app/Inventory"); +jest.mock("../../src/helpers/DropHelpers/DropEmbedHelper"); + +jest.mock("uuid"); + +beforeEach(() => { + (Config.GetValue as jest.Mock).mockResolvedValue("false"); +}); + +describe("execute", () => { + describe("GIVEN user is in the database", () => { + let interaction: CommandInteractionMock; + let user: User; + const randomCard = { + card: { + id: "cardId", + path: "https://google.com/", + } + }; + + beforeAll(async () => { + // Arrange + CoreClient.AllowDrops = true; + + interaction = GenerateCommandInteractionMock(); + + user = { + Currency: 500, + RemoveCurrency: jest.fn().mockReturnValue(true), + Save: jest.fn(), + } as unknown as User; + + (User.FetchOneById as jest.Mock).mockResolvedValue(user); + (GetCardsHelper.FetchCard as jest.Mock).mockResolvedValue(randomCard); + (Inventory.FetchOneByCardNumberAndUserId as jest.Mock).mockResolvedValue({ + Quantity: 1, + }); + (DropEmbedHelper.GenerateDropEmbed as jest.Mock).mockReturnValue({ + type: "Embed", + }); + (DropEmbedHelper.GenerateDropButtons as jest.Mock).mockReturnValue({ + type: "Button", + }); + + (uuid.v4 as jest.Mock).mockReturnValue("uuid"); + + // Act + const drop = new Drop(); + await drop.execute(interaction as unknown as CommandInteraction); + }); + + test("EXPECT user to be fetched", () => { + expect(User.FetchOneById).toHaveBeenCalledTimes(1); + expect(User.FetchOneById).toHaveBeenCalledWith(User, "userId"); + }); + + test("EXPECT user.RemoveCurrency to be called", () => { + expect(user.RemoveCurrency).toHaveBeenCalledTimes(1); + expect(user.RemoveCurrency).toHaveBeenCalledWith(CardConstants.ClaimCost); + }); + + test("EXPECT user to be saved", () => { + expect(user.Save).toHaveBeenCalledTimes(1); + expect(user.Save).toHaveBeenCalledWith(User, user); + }); + + test("EXPECT random card to be fetched", () => { + expect(GetCardsHelper.FetchCard).toHaveBeenCalledTimes(1); + expect(GetCardsHelper.FetchCard).toHaveBeenCalledWith("userId"); + }); + + test("EXPECT interaction to be deferred", () => { + expect(interaction.deferReply).toHaveBeenCalledTimes(1); + }); + + test("EXPECT Inventory.FetchOneByCardNumberAndUserId to be called", () => { + expect(Inventory.FetchOneByCardNumberAndUserId).toHaveBeenCalledTimes(1); + expect(Inventory.FetchOneByCardNumberAndUserId).toHaveBeenCalledWith("userId", "cardId"); + }); + + test("EXPECT DropEmbedHelper.GenerateDropEmbed to be called", () => { + expect(DropEmbedHelper.GenerateDropEmbed).toHaveBeenCalledTimes(1); + expect(DropEmbedHelper.GenerateDropEmbed).toHaveBeenCalledWith(randomCard, 1, "", undefined, 500); + }); + + test("EXPECT DropEmbedHelper.GenerateDropButtons to be called", () => { + expect(DropEmbedHelper.GenerateDropButtons).toHaveBeenCalledTimes(1); + expect(DropEmbedHelper.GenerateDropButtons).toHaveBeenCalledWith(randomCard, "uuid", "userId"); + }); + + test("EXPECT interaction to be edited", () => { + expect(interaction.editReply).toHaveBeenCalledTimes(1); + expect(interaction.editReply).toHaveBeenCalledWith({ + embeds: [ { type: "Embed" } ], + files: [], + components: [ { type: "Button" } ], + }); + }); + + describe("AND randomCard path is not a url", () => { + test.todo("EXPECT image read from file system"); + + test.todo("EXPECT files on the embed to contain the image as an attachment"); + }); + }); + + describe("GIVEN user is not in the database", () => { + test.todo("EXPECT new user to be created"); + }); + + describe("GIVEN user.RemoveCurrency fails", () => { + test.todo("EXPECT error replied"); + }); + + describe("GIVEN randomCard returns null", () => { + test.todo("EXPECT error logged"); + + test.todo("EXPECT error replied"); + }); + + describe("GIVEN the code throws an error", () => { + test.todo("EXPECT error logged"); + + test.todo("EXPECT interaction edited with error"); + }); +}); \ No newline at end of file diff --git a/tests/helpers/DropHelpers/DropEmbedHelper.test.ts b/tests/helpers/DropHelpers/DropEmbedHelper.test.ts new file mode 100644 index 0000000..743f6c7 --- /dev/null +++ b/tests/helpers/DropHelpers/DropEmbedHelper.test.ts @@ -0,0 +1,3 @@ +describe("GenerateDropButtons", () => { + test.todo("EXPECT row to be returned"); +}); \ No newline at end of file diff --git a/tests/timers/PurgeClaims.test.ts b/tests/timers/PurgeClaims.test.ts new file mode 100644 index 0000000..6d0d09b --- /dev/null +++ b/tests/timers/PurgeClaims.test.ts @@ -0,0 +1,7 @@ +describe("PurgeClaims", () => { + test.todo("EXPECT claims to be fetched"); + + test.todo("EXPECT Claim.RemoveMany to remove the claims older than 2 minutes"); + + test.todo("EXPECT info logged"); +}); \ No newline at end of file