mirror of
https://github.com/discordeno/discordeno.git
synced 2026-05-31 16:00:07 +00:00
e2e test stuff (#2754)
* fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint * fix: dont send heartbeat if socket is not open * fix: remove logs * fox: remove more logs * fix some bugs in role tests * Switch to after hook style * hoti's speed snaker * auto convert objects for discord * Fix code style issues with ESLint * fix: remove dup imports * fix: i hate linters * speeder --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
This commit is contained in:
@@ -496,6 +496,8 @@ export class Shard {
|
||||
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating
|
||||
const jitter = Math.ceil(this.heart.interval * (Math.random() || 0.5))
|
||||
this.heart.timeoutId = setTimeout(() => {
|
||||
if (!this.isOpen()) return;
|
||||
|
||||
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
||||
this.socket?.send(
|
||||
JSON.stringify({
|
||||
@@ -509,6 +511,7 @@ export class Shard {
|
||||
|
||||
// After the random heartbeat jitter we can start a normal interval.
|
||||
this.heart.intervalId = setInterval(async () => {
|
||||
if (!this.isOpen()) return;
|
||||
// gateway.debug("GW DEBUG", `Running setInterval in heartbeat file. Shard: ${shardId}`);
|
||||
|
||||
// gateway.debug("GW HEARTBEATING", { shardId, shard: currentShard });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { InteractionResponseTypes } from '@discordeno/types'
|
||||
import { camelize, delay, findFiles, getBotIdFromToken, logger, urlToBase64 } from '@discordeno/utils'
|
||||
import { camelize, camelToSnakeCase, delay, findFiles, getBotIdFromToken, logger, urlToBase64 } from '@discordeno/utils'
|
||||
|
||||
import { createInvalidRequestBucket } from './invalidBucket.js'
|
||||
import { Queue } from './queue.js'
|
||||
@@ -12,8 +12,10 @@ import type {
|
||||
CreateAutoModerationRuleOptions,
|
||||
CreateChannelInvite,
|
||||
CreateForumPostWithMessage,
|
||||
CreateGuild,
|
||||
CreateGuildChannel,
|
||||
CreateGuildEmoji,
|
||||
CreateGuildRole,
|
||||
CreateMessageOptions,
|
||||
CreateScheduledEvent,
|
||||
CreateStageInstance,
|
||||
@@ -30,6 +32,7 @@ import type {
|
||||
DiscordFollowAnnouncementChannel,
|
||||
DiscordFollowedChannel,
|
||||
DiscordGetGatewayBot,
|
||||
DiscordGuild,
|
||||
DiscordIntegration,
|
||||
DiscordInvite,
|
||||
DiscordInviteMetadata,
|
||||
@@ -38,6 +41,7 @@ import type {
|
||||
DiscordMember,
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessage,
|
||||
DiscordRole,
|
||||
DiscordScheduledEvent,
|
||||
DiscordStageInstance,
|
||||
DiscordStickerPack,
|
||||
@@ -47,6 +51,7 @@ import type {
|
||||
DiscordWebhook,
|
||||
EditAutoModerationRuleOptions,
|
||||
EditChannelPermissionOverridesOptions,
|
||||
EditGuildRole,
|
||||
EditMessage,
|
||||
EditScheduledEvent,
|
||||
EditStageInstanceOptions,
|
||||
@@ -64,18 +69,13 @@ import type {
|
||||
ModifyChannel,
|
||||
ModifyGuildChannelPositions,
|
||||
ModifyGuildEmoji,
|
||||
ModifyRolePositions,
|
||||
ModifyWebhook,
|
||||
SearchMembers,
|
||||
StartThreadWithMessage,
|
||||
StartThreadWithoutMessage,
|
||||
WithReason,
|
||||
|
||||
CreateGuild,
|
||||
CreateGuildRole,
|
||||
DiscordGuild,
|
||||
DiscordRole,
|
||||
EditGuildRole,
|
||||
ModifyRolePositions} from '@discordeno/types'
|
||||
} from '@discordeno/types'
|
||||
import type { InvalidRequestBucket } from './invalidBucket.js'
|
||||
|
||||
// TODO: make dynamic based on package.json file
|
||||
@@ -536,6 +536,33 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
return false
|
||||
},
|
||||
|
||||
changeToDiscordFormat(obj: any): any {
|
||||
if (obj === null) return null
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => rest.changeToDiscordFormat(item))
|
||||
}
|
||||
|
||||
const newObj: any = {}
|
||||
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (key === 'permissions') {
|
||||
newObj.permissions = '1234567890'
|
||||
continue
|
||||
}
|
||||
|
||||
newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(obj[key])
|
||||
}
|
||||
|
||||
return newObj
|
||||
}
|
||||
|
||||
if (typeof obj === 'bigint') return obj.toString()
|
||||
|
||||
return obj
|
||||
},
|
||||
|
||||
createRequest(options) {
|
||||
const headers: Record<string, string> = {
|
||||
'user-agent': `DiscordBot (https://github.com/discordeno/discordeno, v${version})`,
|
||||
@@ -561,32 +588,36 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
options.body.reason = undefined
|
||||
}
|
||||
|
||||
if (options.body?.file) {
|
||||
const files = findFiles(options.body.file)
|
||||
const form = new FormData()
|
||||
if (options.body) {
|
||||
const { file } = options.body
|
||||
if (file) {
|
||||
const files = findFiles(file)
|
||||
const form = new FormData()
|
||||
|
||||
// WHEN CREATING A STICKER, DISCORD WANTS FORM DATA ONLY
|
||||
if (options.url?.endsWith('/stickers') && options.method === 'POST') {
|
||||
form.append('file', files[0].blob, files[0].name)
|
||||
form.append('name', options.body.name as string)
|
||||
form.append('description', options.body.description as string)
|
||||
form.append('tags', options.body.tags as string)
|
||||
} else {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
form.append(`file${i}`, files[i].blob, files[i].name)
|
||||
// WHEN CREATING A STICKER, DISCORD WANTS FORM DATA ONLY
|
||||
if (options.url?.endsWith('/stickers') && options.method === 'POST') {
|
||||
form.append('file', files[0].blob, files[0].name)
|
||||
form.append('name', options.body.name as string)
|
||||
form.append('description', options.body.description as string)
|
||||
form.append('tags', options.body.tags as string)
|
||||
} else {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
form.append(`file${i}`, files[i].blob, files[i].name)
|
||||
}
|
||||
|
||||
if (file) options.body.file = undefined
|
||||
form.append('payload_json', JSON.stringify(rest.changeToDiscordFormat(options.body)))
|
||||
}
|
||||
|
||||
form.append('payload_json', JSON.stringify({ ...options.body, file: undefined }))
|
||||
options.body.file = form
|
||||
} else if (options.body && !['GET', 'DELETE'].includes(options.method)) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
options.body.file = form
|
||||
} else if (options.body && !['GET', 'DELETE'].includes(options.method)) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
return {
|
||||
headers,
|
||||
body: (options.body?.file ?? JSON.stringify(options.body)) as FormData | string,
|
||||
body: (options.body?.file ?? JSON.stringify(rest.changeToDiscordFormat(options.body))) as FormData | string,
|
||||
method: options.method,
|
||||
}
|
||||
},
|
||||
@@ -698,11 +729,9 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
},
|
||||
|
||||
async sendRequest(options) {
|
||||
console.log('in send request')
|
||||
const url = options.url.startsWith('https://') ? options.url : `${rest.baseUrl}/v${rest.version}${options.url}`
|
||||
const payload = rest.createRequest({ method: options.method, url: options.url, body: options.body, ...options.options })
|
||||
|
||||
console.log(`sending request to ${url}`, 'with payload:', { ...payload, headers: { ...payload.headers, authorization: 'Bot tokenhere' } })
|
||||
logger.debug(`sending request to ${url}`, 'with payload:', { ...payload, headers: { ...payload.headers, authorization: 'Bot tokenhere' } })
|
||||
const response = await fetch(url, payload)
|
||||
logger.debug(`request fetched from ${url} with status ${response.status} & ${response.statusText}`)
|
||||
@@ -736,6 +765,8 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
|
||||
return await options.retryRequest?.(options)
|
||||
}
|
||||
|
||||
return options.reject(await response.json())
|
||||
}
|
||||
|
||||
// Discord sometimes sends no response
|
||||
@@ -1407,7 +1438,6 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
},
|
||||
|
||||
async createGuild(options) {
|
||||
console.log('in create guild')
|
||||
return await rest.post<DiscordGuild>(rest.routes.guilds.all(), options)
|
||||
},
|
||||
|
||||
@@ -2170,6 +2200,8 @@ export interface RestManager {
|
||||
}
|
||||
/** Check the rate limits for a url or a bucket. */
|
||||
checkRateLimits: (url: string) => number | false
|
||||
/** Reshapes and modifies the obj as needed to make it ready for discords api. */
|
||||
changeToDiscordFormat: (obj: any) => any
|
||||
/** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */
|
||||
createRequest: (options: CreateRequestBodyOptions) => RequestBody
|
||||
/** This will create a infinite loop running in 1 seconds using tail recursion to keep rate limits clean. When a rate limit resets, this will remove it so the queue can proceed. */
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { rest } from './utils.js'
|
||||
import { e2ecache, rest } from './utils.js'
|
||||
|
||||
before(async () => {
|
||||
if (!e2ecache.guild) {
|
||||
e2ecache.guild = await rest.createGuild({
|
||||
name: 'Discordeno-test',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id) await rest.deleteGuild(e2ecache.guild.id)
|
||||
})
|
||||
|
||||
describe('[rest] Message related tests', () => {
|
||||
describe('Send a message', () => {
|
||||
|
||||
@@ -4,13 +4,18 @@ import { expect } from 'chai'
|
||||
import { afterEach, before, beforeEach, describe, it } from 'mocha'
|
||||
import { e2ecache, rest } from './utils.js'
|
||||
|
||||
// before(async () => {
|
||||
// if (!e2ecache.guild) {
|
||||
// e2ecache.guild = await rest.createGuild({
|
||||
// name: 'Discordeno-test'
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
before(async () => {
|
||||
if (!e2ecache.guild) {
|
||||
e2ecache.guild = await rest.createGuild({
|
||||
name: 'Discordeno-test',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id) await rest.deleteGuild(e2ecache.guild.id)
|
||||
})
|
||||
|
||||
describe('[role] Role tests', async () => {
|
||||
// Create a role with a reason
|
||||
@@ -18,9 +23,9 @@ describe('[role] Role tests', async () => {
|
||||
const role = await rest.createRole(
|
||||
e2ecache.guild?.id,
|
||||
{
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
},
|
||||
'test reason'
|
||||
'test reason',
|
||||
)
|
||||
|
||||
expect(role.id).to.exist
|
||||
@@ -31,7 +36,7 @@ describe('[role] Role tests', async () => {
|
||||
// Create a role without a reason
|
||||
it('Create a role without a reason', async () => {
|
||||
const role = await rest.createRole(e2ecache.guild.id, {
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
})
|
||||
|
||||
expect(role.id).to.exist
|
||||
@@ -42,11 +47,11 @@ describe('[role] Role tests', async () => {
|
||||
// Delete a role
|
||||
it('Delete a role', async () => {
|
||||
const role = await rest.createRole(e2ecache.guild.id, {
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
})
|
||||
await rest.deleteRole(e2ecache.guild.id, role.id)
|
||||
const deletedRoles = await rest.getRoles(e2ecache.guild.id)
|
||||
expect(deletedRoles.some(r => r.id === role.id)).to.equal(false)
|
||||
expect(deletedRoles.some((r) => r.id === role.id)).to.equal(false)
|
||||
})
|
||||
|
||||
// Edit a role
|
||||
@@ -55,7 +60,7 @@ describe('[role] Role tests', async () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await rest.createRole(e2ecache.guild.id, {
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -66,7 +71,7 @@ describe('[role] Role tests', async () => {
|
||||
// Edit the roles name
|
||||
it('Edit the roles name', async () => {
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
name: 'test role 4'
|
||||
name: 'test role 4',
|
||||
})
|
||||
expect(edited.name).to.equal('test role 4')
|
||||
})
|
||||
@@ -74,7 +79,7 @@ describe('[role] Role tests', async () => {
|
||||
// Edit the roles color
|
||||
it('Edit the roles color', async () => {
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
color: 0x0000ff
|
||||
color: 0x0000ff,
|
||||
})
|
||||
expect(edited.color).to.equal(0x0000ff)
|
||||
})
|
||||
@@ -121,11 +126,9 @@ describe('[role] Role tests', async () => {
|
||||
// Edit the roles permissions
|
||||
it('Edit the roles permissions', async () => {
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
permissions: ['SEND_MESSAGES', 'VIEW_CHANNEL']
|
||||
permissions: ['SEND_MESSAGES', 'VIEW_CHANNEL'],
|
||||
})
|
||||
expect(edited.permissions.toString()).to.equal(
|
||||
calculateBits(['SEND_MESSAGES', 'VIEW_CHANNEL'])
|
||||
)
|
||||
expect(edited.permissions.toString()).to.equal(calculateBits(['SEND_MESSAGES', 'VIEW_CHANNEL']))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -134,13 +137,9 @@ describe('[role] Role tests', async () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await rest.createRole(e2ecache.guild.id, {
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
})
|
||||
await rest.addRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id
|
||||
)
|
||||
await rest.addRole(e2ecache.guild.id, rest.applicationId, role.id)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -148,29 +147,15 @@ describe('[role] Role tests', async () => {
|
||||
})
|
||||
|
||||
it('without a reason', async () => {
|
||||
await rest.removeRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id
|
||||
)
|
||||
const member = await rest.getMember(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n
|
||||
)
|
||||
await rest.removeRole(e2ecache.guild.id, rest.applicationId, role.id)
|
||||
const member = await rest.getMember(e2ecache.guild.id, rest.applicationId)
|
||||
// console.log('member', member.errors.userId.Errors)
|
||||
expect(member?.roles.includes(role.id)).to.equal(false)
|
||||
})
|
||||
|
||||
it('with a reason', async () => {
|
||||
await rest.removeRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id,
|
||||
'test reason'
|
||||
)
|
||||
const member = await rest.getMember(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n
|
||||
)
|
||||
await rest.removeRole(e2ecache.guild.id, rest.applicationId, role.id, 'test reason')
|
||||
const member = await rest.getMember(e2ecache.guild.id, rest.applicationId)
|
||||
expect(member?.roles.includes(role.id)).to.equal(false)
|
||||
})
|
||||
})
|
||||
@@ -180,45 +165,26 @@ describe('[role] Role tests', async () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await rest.createRole(e2ecache.guild.id, {
|
||||
name: `test role ${Date.now()}`
|
||||
name: `test role ${Date.now()}`,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await rest.removeRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id
|
||||
)
|
||||
await rest.removeRole(e2ecache.guild.id, rest.applicationId, role.id)
|
||||
await rest.deleteRole(e2ecache.guild.id, role.id)
|
||||
})
|
||||
|
||||
it('Without a reason.', async () => {
|
||||
// Assign the role to the user
|
||||
await rest.addRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id
|
||||
)
|
||||
const member = await rest.getMember(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n
|
||||
)
|
||||
await rest.addRole(e2ecache.guild.id, rest.applicationId, role.id)
|
||||
const member = await rest.getMember(e2ecache.guild.id, rest.applicationId)
|
||||
expect(member?.roles.includes(role.id)).to.equal(true)
|
||||
})
|
||||
|
||||
// Add the role to the user with a reason
|
||||
it('With a reason', async () => {
|
||||
await rest.addRole(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n,
|
||||
role.id,
|
||||
'test reason'
|
||||
)
|
||||
const member = await rest.getMember(
|
||||
e2ecache.guild.id,
|
||||
130136895395987456n
|
||||
)
|
||||
await rest.addRole(e2ecache.guild.id, rest.applicationId, role.id, 'test reason')
|
||||
const member = await rest.getMember(e2ecache.guild.id, rest.applicationId)
|
||||
expect(member?.roles.includes(role.id)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import chai, { expect } from 'chai'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import { describe, it } from 'mocha'
|
||||
import { rest } from './utils.js'
|
||||
import { e2ecache, rest } from './utils.js'
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
before(async () => {
|
||||
if (!e2ecache.guild) {
|
||||
e2ecache.guild = await rest.createGuild({
|
||||
name: 'Discordeno-test',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id) await rest.deleteGuild(e2ecache.guild.id)
|
||||
})
|
||||
|
||||
describe('[rest] User related tests', () => {
|
||||
describe('Get a user from the api', () => {
|
||||
it('With a valid user id', async () => {
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { logger, LogLevels } from '@discordeno/utils'
|
||||
import { LogDepth, logger, LogLevels } from '@discordeno/utils'
|
||||
|
||||
import { createRestManager } from '../../src/manager.js'
|
||||
import { token } from './constants.js'
|
||||
// For debugging purposes
|
||||
logger.setLevel(LogLevels.Debug)
|
||||
// logger.setLevel(LogLevels.Debug)
|
||||
// logger.setDepth(LogDepth.Full)
|
||||
|
||||
export const rest = createRestManager({
|
||||
token,
|
||||
})
|
||||
rest.deleteQueueDelay = 10000
|
||||
|
||||
console.log('CREATING GUILD')
|
||||
export const e2ecache = {
|
||||
guild: await rest.createGuild({ name: 'ddenotester' }),
|
||||
}
|
||||
|
||||
console.log('CACHED check', e2ecache.guild)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import chai from 'chai'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import { describe, it } from 'mocha'
|
||||
import { e2ecache, rest } from './utils.js'
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
// The xyz.spec.ts file name will make this test run last as tests are ran in alphabetical file name order.
|
||||
|
||||
describe('[rest] Cleanup tests', () => {
|
||||
describe('Remove the timers', () => {
|
||||
it('In the invalid bucket', async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
})
|
||||
})
|
||||
|
||||
it('Delete the created guild', async () => {
|
||||
if (e2ecache.guild.id) await rest.deleteGuild(e2ecache.guild.id);
|
||||
})
|
||||
})
|
||||
@@ -32,3 +32,18 @@ export function snakeToCamelCase(str: string): string {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function camelToSnakeCase(str: string): string {
|
||||
let result = "";
|
||||
for (let i = 0, len = str.length; i < len; ++i) {
|
||||
if (str[i] >= "A" && str[i] <= "Z") {
|
||||
result += `_${str[i].toLowerCase()}`;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
result += str[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user