diff --git a/packages/rest/tests/e2e/automod.spec.ts b/packages/rest/tests/e2e/automod.spec.ts index 4ed4168b2..340c2c8b0 100644 --- a/packages/rest/tests/e2e/automod.spec.ts +++ b/packages/rest/tests/e2e/automod.spec.ts @@ -1,8 +1,8 @@ import { AutoModerationActionType, AutoModerationEventTypes, AutoModerationTriggerTypes } from '@discordeno/types' import { expect } from 'chai' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' -describe('Automod tests', async () => { +describe('Automod tests', () => { it('Create a MessageSend rule for Keyword with BlockMessage action.', async () => { const rule = await rest.createAutomodRule(e2eCache.guild.id, { name: 'test', @@ -17,6 +17,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -30,8 +31,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions).to.be.exist expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with Timeout action.', async () => { @@ -51,6 +50,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -65,8 +65,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[0].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with BlockMessage & Timeout action.', async () => { @@ -89,6 +87,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -103,8 +102,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[1].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) describe('with a channel', () => { @@ -125,6 +122,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -139,8 +137,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.SendAlertMessage) expect(fetchedRule.actions[0].metadata?.channelId).to.equal(e2eCache.channel.id) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with SendAlertMessage & Timeout action.', async () => { @@ -166,6 +162,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -182,8 +179,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].metadata?.channelId).to.equal(e2eCache.channel.id) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[1].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with BlockMessage & SendAlertMessage & Timeout action.', async () => { @@ -212,6 +207,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) // Get the rule again to make sure it was created correctly const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -232,8 +228,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.SendAlertMessage) expect(fetchedRule.actions[2].type).to.equal(AutoModerationActionType.Timeout) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) }) }) diff --git a/packages/rest/tests/e2e/constants.ts b/packages/rest/tests/e2e/constants.ts index 910a65a2f..fb4a97e32 100644 --- a/packages/rest/tests/e2e/constants.ts +++ b/packages/rest/tests/e2e/constants.ts @@ -1,9 +1,9 @@ import dotenv from 'dotenv' -dotenv.config({ path: '../../.env' }) +dotenv.config({ path: '../../.env', quiet: true }) -if (!process.env.DISCORD_TOKEN) throw new Error('Token was not provided.') -export const token = process.env.DISCORD_TOKEN +export const token = process.env.DISCORD_TOKEN! +if (!token) throw new Error('Token was not provided.') export const E2E_TEST_GUILD_ID = process.env.E2E_TEST_GUILD_ID! if (!E2E_TEST_GUILD_ID) throw new Error('COMMUNITY guild id was not provided.') diff --git a/packages/rest/tests/e2e/emoji.spec.ts b/packages/rest/tests/e2e/emoji.spec.ts index 2810eb6e3..15ffb9822 100644 --- a/packages/rest/tests/e2e/emoji.spec.ts +++ b/packages/rest/tests/e2e/emoji.spec.ts @@ -3,7 +3,7 @@ import { urlToBase64 } from '@discordeno/utils' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) @@ -15,9 +15,10 @@ describe('Create and delete emojis', () => { roles: [], }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) + // Assertions - expect(emoji.id).to.be.exist - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + expect(emoji.id).to.exist }) // delete an emoji without a reason @@ -27,11 +28,15 @@ describe('Create and delete emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + const cleanEmoji = async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + toDispose.add(cleanEmoji) // Assertions - expect(emoji.id).to.be.exist + expect(emoji.id).to.exist await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + // Remove from toDispose since we already deleted it + toDispose.delete(cleanEmoji) await expect(rest.getEmoji(e2eCache.guild.id, emoji.id!)).to.eventually.rejected }) @@ -43,11 +48,15 @@ describe('Create and delete emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + const cleanEmoji = async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + toDispose.add(cleanEmoji) // Assertions - expect(emoji.id).to.be.exist + expect(emoji.id).to.exist await rest.deleteEmoji(e2eCache.guild.id, emoji.id!, 'with a reason') + // Remove from toDispose since we already deleted it + toDispose.delete(cleanEmoji) await expect(rest.getEmoji(e2eCache.guild.id, emoji.id!)).to.eventually.rejected }) @@ -62,10 +71,8 @@ describe('Edit and get emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], })) as Camelize & { id: string } - }) - afterEach(async () => { - await rest.deleteEmoji(e2eCache.guild.id, emoji.id) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id)) }) // edit an emoji name @@ -83,9 +90,8 @@ describe('Edit and get emojis', () => { const role = await rest.createRole(e2eCache.guild.id, { name: 'dd-test-emoji', }) - after(async () => { - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) + await rest.editEmoji(e2eCache.guild.id, emoji.id, { roles: [role.id], }) @@ -108,12 +114,12 @@ describe('Edit and get emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, newEmoji.id!)) const exists = await rest.getEmojis(e2eCache.guild.id) + expect(exists.length).to.greaterThan(1) expect(exists.find((x) => x.id === newEmoji.id)).to.exist expect(exists.find((x) => x.id === emoji.id)).to.exist - - await rest.deleteEmoji(e2eCache.guild.id, newEmoji.id!) }) }) diff --git a/packages/rest/tests/e2e/guild.spec.ts b/packages/rest/tests/e2e/guild.spec.ts index 1bb3d2f0f..a82a34cfc 100644 --- a/packages/rest/tests/e2e/guild.spec.ts +++ b/packages/rest/tests/e2e/guild.spec.ts @@ -2,11 +2,11 @@ import { ChannelTypes } from '@discordeno/types' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) -describe('Manage Guilds', async () => { +describe('Manage Guilds', () => { it('Get a guild', async () => { const exists = await rest.getGuild(e2eCache.guildId) expect(exists).to.be.exist @@ -20,14 +20,10 @@ describe('Manage Guilds', async () => { name: 'e2e-afk-channel', type: ChannelTypes.GuildVoice, }) + toDispose.add(async () => await rest.deleteChannel(voiceChannel.id)) expect(voiceChannel.id).to.be.exist - after(async () => { - // Clean up the AFK channel created for testing - await rest.deleteChannel(voiceChannel.id) - }) - // Set the AFK channel const edited = await rest.editGuild(e2eCache.guild.id, { afkChannelId: voiceChannel.id, @@ -65,7 +61,6 @@ describe('Manage Guilds', async () => { expect(fetchedBan).to.be.exist expect(fetchedBan.user.id).to.equal('379643682984296448') - // Assertions expect(fetchedBans).to.be.exist expect(fetchedBans.length).to.greaterThanOrEqual(2) diff --git a/packages/rest/tests/e2e/message.spec.ts b/packages/rest/tests/e2e/message.spec.ts index 723438533..8f6b34915 100644 --- a/packages/rest/tests/e2e/message.spec.ts +++ b/packages/rest/tests/e2e/message.spec.ts @@ -1,14 +1,18 @@ import { processReactionString, urlToBase64 } from '@discordeno/utils' import { expect } from 'chai' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' describe('Send a message', () => { it('With content', async () => { const message = await rest.sendMessage(e2eCache.channel.id, { content: 'testing rate limit manager' }) + expect(message.content).to.be.equal('testing rate limit manager') + const edited = await rest.editMessage(message.channelId, message.id, { content: 'testing rate limit manager edited' }) + expect(message.content).to.be.not.equal(edited.content) + await rest.deleteMessage(message.channelId, message.id) }) @@ -16,40 +20,43 @@ describe('Send a message', () => { const image = await fetch('https://cdn.discordapp.com/avatars/270010330782892032/d031ea881688526d1ae235fd2843e53c.jpg?size=2048') .then(async (res) => await res.blob()) .catch(() => undefined) + expect(image).to.not.be.undefined - if (!image) throw new Error('Was not able to fetch the image.') - const message = await rest.sendMessage(e2eCache.channel.id, { files: [{ blob: image, name: 'gamer' }] }) - expect(message.attachments.length).to.be.greaterThan(0) + + const message = await rest.sendMessage(e2eCache.channel.id, { files: [{ blob: image!, name: 'gamer' }] }) const [attachment] = message.attachments + + expect(message.attachments.length).to.be.greaterThan(0) expect(attachment.filename).to.be.equal('gamer') }) it('With a file attachment', async () => { - const txtFile = new Blob(['hello world'], { type: 'text/plain' }) const fileMsg = await rest.sendMessage(e2eCache.channel.id, { content: '222', files: [ { name: 'application.txt', - blob: txtFile, + blob: new Blob(['hello world'], { type: 'text/plain' }), }, ], }) + expect(fileMsg.id).not.equals(undefined) expect(fileMsg.content).equals('222') expect(fileMsg.attachments.length).equals(1) expect(fileMsg.attachments.at(0)?.filename).equals('application.txt') expect(fileMsg.attachments.at(0)?.size).equals(11) - const txtFile2 = new Blob(['hello world edit'], { type: 'text/plain' }) + const edited = await rest.editMessage(e2eCache.channel.id, fileMsg.id, { content: '222 edit', files: [ { name: 'application_edit.txt', - blob: txtFile2, + blob: new Blob(['hello world edit'], { type: 'text/plain' }), }, ], }) + expect(edited.id).not.equals(undefined) expect(edited.content).equals('222 edit') expect(edited.attachments.length).equals(1) @@ -58,7 +65,7 @@ describe('Send a message', () => { }) }) -describe('Manage reactions', async () => { +describe('Manage reactions', () => { it('Add and delete a unicode reaction', async () => { const message = await rest.sendMessage(e2eCache.channel.id, { content: 'add reaction test' }) await rest.addReaction(message.channelId, message.id, '📙') @@ -79,11 +86,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -109,11 +112,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -136,11 +135,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -197,10 +192,7 @@ describe('Rate limit manager testing', () => { // Create 10 channels and send a message to each const promises = Array.from({ length: 10 }, async (_, i) => { const channel = await rest.createChannel(e2eCache.guild.id, { name: `rate-limit-${i}` }) - - after(async () => { - await rest.deleteChannel(channel.id) - }) + toDispose.add(async () => await rest.deleteChannel(channel.id)) const messagePromises = Array.from({ length: 10 }, async (_, j) => { await rest.sendMessage(channel.id, { content: `testing rate limit manager ${j}` }) diff --git a/packages/rest/tests/e2e/misc.spec.ts b/packages/rest/tests/e2e/misc.spec.ts index 8505c207f..d0f3e8f92 100644 --- a/packages/rest/tests/e2e/misc.spec.ts +++ b/packages/rest/tests/e2e/misc.spec.ts @@ -1,13 +1,16 @@ +import { use as chaiUse, expect } from 'chai' +import chaiAsPromised from 'chai-as-promised' import { describe } from 'mocha' import { e2eCache, rest } from './utils.js' +chaiUse(chaiAsPromised) + describe('Typings', () => { it('Trigger Typing Indication', async () => { await rest.triggerTypingIndicator(e2eCache.channel.id) }) }) -/* TODO: Add this back when bot's name is changed (https://discord.com/channels/785384884197392384/785384884197392387/1142474846811459776) describe('Commands', () => { it('Upsert global commands', async () => { await rest.upsertGlobalApplicationCommands([ @@ -33,7 +36,6 @@ describe('Commands', () => { await rest.deleteGlobalApplicationCommand(created!.id) - expect(rest.getGlobalApplicationCommand(created!.id)).to.throw + await expect(rest.getGlobalApplicationCommand(created!.id)).to.eventually.be.rejected }) }) -*/ diff --git a/packages/rest/tests/e2e/role.spec.ts b/packages/rest/tests/e2e/role.spec.ts index 82de30b45..faa33d857 100644 --- a/packages/rest/tests/e2e/role.spec.ts +++ b/packages/rest/tests/e2e/role.spec.ts @@ -1,17 +1,13 @@ import { calculateBits } from '@discordeno/utils' import { expect } from 'chai' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' -describe('Role tests', async () => { +describe('Role tests', () => { // Create a role with a reason it('Create a role with a reason', async () => { const role = await rest.createRole(e2eCache.guild?.id, { name: `test role ${Date.now()}` }, 'test reason') - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) expect(role.id).to.exist }) @@ -21,11 +17,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) expect(role.id).to.exist }) @@ -47,11 +39,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) const edited = await rest.editRole(e2eCache.guild.id, role.id, { name: 'test role 4', @@ -82,11 +70,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) // Assign the role to the user await rest.addRole(e2eCache.guild.id, rest.applicationId, role.id) diff --git a/packages/rest/tests/e2e/stickers.spec.ts b/packages/rest/tests/e2e/stickers.spec.ts index a36566577..6eeb3b3be 100644 --- a/packages/rest/tests/e2e/stickers.spec.ts +++ b/packages/rest/tests/e2e/stickers.spec.ts @@ -2,12 +2,12 @@ import { StickerFormatTypes } from '@discordeno/types' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) // waiting for channel -describe('Sticker tests', async () => { +describe('Sticker tests', () => { it('Can get a sticker', async () => { const sticker = await rest.getSticker(749054660769218631n) expect(sticker.name).to.equal('Wave') @@ -23,6 +23,10 @@ describe('Sticker tests', async () => { name: 'ddlogo.png', }, }) + const cleanSticker = async () => await rest.deleteGuildSticker(e2eCache.guild.id, sticker.id) + + // We may remove this as we also test sticker deletion at the end of the test, so we need a var to reference the function + toDispose.add(cleanSticker) expect(sticker.name).to.equal('sticker name') expect(sticker.description).to.equal('sticker description') @@ -62,15 +66,15 @@ describe('Sticker tests', async () => { }, }) - after(async () => { - // Clean up the sticker created for testing - await rest.deleteGuildSticker(e2eCache.guild.id, sticker2.id) - }) + toDispose.add(async () => await rest.deleteGuildSticker(e2eCache.guild.id, sticker2.id)) const stickers = await rest.getGuildStickers(e2eCache.guild.id) expect(stickers.length).to.greaterThan(1) await rest.deleteGuildSticker(e2eCache.guild.id, sticker.id) + // Since we have already deleted the sticker, we can remove it from the toDispose set + toDispose.delete(cleanSticker) + await expect(rest.getGuildSticker(e2eCache.guild.id, sticker.id)).to.eventually.rejected }) }) diff --git a/packages/rest/tests/e2e/utils.ts b/packages/rest/tests/e2e/utils.ts index 82ca8b25b..f6487baca 100644 --- a/packages/rest/tests/e2e/utils.ts +++ b/packages/rest/tests/e2e/utils.ts @@ -3,7 +3,6 @@ import { createRestManager } from '../../src/manager.js' import { E2E_TEST_GUILD_ID, token } from './constants.js' // For debugging purposes // logger.setLevel(LogLevels.Debug) -// logger.setDepth(LogDepth.Full) export const rest = createRestManager({ token, @@ -29,3 +28,25 @@ export const e2eCache = { guild, channel, } + +// Some resources created during tests need to be disposed of afterwards, as they will persist otherwise (e.g., emojis, roles, automod rules) +export const toDispose = new Set<() => Promise>() + +afterEach(async () => { + let aggregateError: AggregateError | null = null + + for (const dispose of toDispose) { + try { + await dispose() + } catch (error) { + console.error('Error during cleanup:', error) + + aggregateError ??= new AggregateError([], 'Errors occurred during cleanup') + aggregateError.errors.push(error) + } + } + + toDispose.clear() + + if (aggregateError) throw aggregateError +}) diff --git a/packages/rest/tests/e2e/webhook.spec.ts b/packages/rest/tests/e2e/webhook.spec.ts index 3175af6ec..654452275 100644 --- a/packages/rest/tests/e2e/webhook.spec.ts +++ b/packages/rest/tests/e2e/webhook.spec.ts @@ -5,7 +5,7 @@ import { e2eCache, rest } from './utils.js' chaiUse(chaiAsPromised) -describe('Webhook helpers', async () => { +describe('Webhook helpers', () => { it('Manage webhooks', async () => { const webhook = await rest.createWebhook(e2eCache.channel.id, { name: 'idk',