Merge pull request #109 from Skillz4Killz/unit-tests

Unit tests
This commit is contained in:
Skillz4Killz
2020-10-28 14:04:31 -04:00
committed by GitHub
8 changed files with 283 additions and 5 deletions

15
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Test
on:
push:
branches:
- master
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: denolib/setup-deno@master
- name: Cache dependencies
run: deno cache mod.ts test/mod.test.ts
- name: Run test script
run: deno test -A

View File

@@ -4,5 +4,8 @@
"deno.import_intellisense_origins": {
"https://deno.land": true
},
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.defaultFormatter": "denoland.vscode-deno"
}

View File

@@ -6,6 +6,8 @@
![Testing/Linting](https://github.com/Skillz4Killz/Discordeno/workflows/Testing/Linting/badge.svg)
[![nest badge](https://nest.land/badge.svg)](https://nest.land/package/Discordeno)
[WIP] ![Test](https://github.com/Skillz4Killz/Discordeno/workflows/Test/badge.svg)
## Why Discordeno?
### Beginner Developers

View File

@@ -7,4 +7,9 @@ export {
isWebSocketPongEvent,
} from "https://deno.land/std@0.67.0/ws/mod.ts";
export type { WebSocket } from "https://deno.land/std@0.67.0/ws/mod.ts";
export {
assert,
assertArrayIncludes,
assertEquals,
} from "https://deno.land/std@0.75.0/testing/asserts.ts";
export { decompress_with as inflate } from "https://unpkg.com/@evan/wasm@0.0.11/target/zlib/deno.js";

View File

@@ -15,9 +15,6 @@ export async function handleInternalMessageCreate(data: DiscordPayload) {
const channel = await cacheHandlers.get("channels", payload.channel_id);
if (channel) channel.lastMessageID = payload.id;
const message = await structures.createMessage(payload);
// Cache the message
cacheHandlers.set("messages", payload.id, message);
const guild = payload.guild_id
? await cacheHandlers.get("guilds", payload.guild_id)
: undefined;
@@ -46,6 +43,10 @@ export async function handleInternalMessageCreate(data: DiscordPayload) {
}
});
const message = await structures.createMessage(payload);
// Cache the message
cacheHandlers.set("messages", payload.id, message);
eventHandlers.messageCreate?.(message);
}

View File

@@ -32,7 +32,7 @@ import { Permissions } from "../types/permission.ts";
import type { RoleData } from "../types/role.ts";
import { formatImageURL } from "../utils/cdn.ts";
import { Collection } from "../utils/collection.ts";
import { botHasPermission } from "../utils/permissions.ts";
import { botHasPermission, calculateBits } from "../utils/permissions.ts";
import { urlToBase64 } from "../utils/utils.ts";
/** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */
@@ -317,7 +317,12 @@ export function editRole(
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), options);
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
...options,
permissions: options.permissions
? calculateBits(options.permissions)
: undefined,
});
}
/** Delete a guild role. Requires the MANAGE_ROLES permission. */

View File

@@ -188,6 +188,7 @@ export async function hasChannelPermissions(
return botHasPermission(guild.id, permissions);
}
/** This function converts a bitwise string to permission strings */
export function calculatePermissions(permissionBits: bigint) {
return Object.keys(Permissions).filter((perm) => {
if (typeof perm !== "number") return false;
@@ -195,6 +196,14 @@ export function calculatePermissions(permissionBits: bigint) {
}) as Permission[];
}
/** This function converts an array of permissions into the bitwise string. */
export function calculateBits(permissions: Permission[]) {
return permissions.reduce(
(bits, perm) => bits |= BigInt(Permissions[perm]),
BigInt(0),
).toString();
}
export async function highestRole(guildID: string, memberID: string) {
const guild = await cacheHandlers.get("guilds", guildID);
if (!guild) return;

238
tests/mod.test.ts Normal file
View File

@@ -0,0 +1,238 @@
import { assert, assertArrayIncludes, assertEquals, delay } from "../deps.ts";
import {
botID,
cache,
Channel,
createClient,
createGuildChannel,
createGuildRole,
createServer,
deleteChannel,
deleteRole,
deleteServer,
editRole,
getMessage,
Guild,
Intents,
OverwriteType,
Role,
sendMessage,
} from "../mod.ts";
import { editChannel } from "../src/handlers/channel.ts";
import { getChannel } from "../src/handlers/guild.ts";
// TODO: add DISCORD_TOKEN variable to GitHub secrets
const token = Deno.env.get("DISCORD_TOKEN");
if (!token) throw "Token is not provided";
createClient({
token,
intents: [Intents.GUILD_MESSAGES, Intents.GUILDS],
});
// Default options for all test cases
const testOptions = {
sanitizeOps: false,
sanitizeResources: false,
};
Deno.test({
name: "connect to the gateway",
fn: async () => {
// Delay the execution by 15 seconds (15000 ms)
await delay(15000);
// Check whether botID is nil or not
assert(botID);
},
...testOptions,
});
const data = {
guildID: "",
roleID: "",
channelID: "",
};
Deno.test({
name: "create a guild",
async fn() {
// Create a guild "Discordeno Test"
const createdGuild = (await createServer({
name: "Discordeno Test",
})) as Guild;
// Check whether createdGuild is nil or not
assert(createdGuild);
data.guildID = createdGuild.id;
},
...testOptions,
});
// Role
Deno.test({
name: "create a role in a guild",
async fn() {
// Create a role "Role 1" in the guild "Discordeno Test"
const createdRole = await createGuildRole(data.guildID, {
name: "Role 1",
});
// Check whether the created role is nil or not
assert(createdRole);
data.roleID = createdRole.id;
},
...testOptions,
});
Deno.test({
name: "edit a role in a guild",
async fn() {
// Edit a role "Role 1" in the guild "Discordeno Test"
const editedRole = (await editRole(data.guildID, data.roleID, {
name: "Edited Role",
color: 4320244,
hoist: false,
mentionable: false,
})) as Role;
// Assertions
assert(editedRole);
assertEquals(editedRole.name, "Edited Role");
assertEquals(editedRole.color, 4320244);
assertEquals(editedRole.hoist, false);
assertEquals(editedRole.mentionable, false);
data.roleID = editedRole.id;
},
...testOptions,
});
// Channel
Deno.test({
name: "create a channel in a guild",
async fn() {
const guild = cache.guilds.get(data.guildID);
if (!guild) throw "Guild not found";
const createdChannel = await createGuildChannel(guild, "test");
// Check whether the created channel is nil or not
assert(createdChannel);
data.channelID = createdChannel.id;
},
...testOptions,
});
Deno.test({
name: "get a channel in a guild",
async fn() {
const channel = await getChannel(data.channelID);
assertEquals(channel.id, data.channelID);
},
...testOptions,
});
Deno.test({
name: "edit a channel in a guild",
async fn() {
const channel = await editChannel(data.channelID, {
name: "edited channel",
}) as Channel;
assert(channel);
data.channelID = channel.id;
},
});
Deno.test({
name: "channel overwrite has permission",
async fn() {
const channel = cache.channels.get(data.channelID);
if (!channel) throw "Channel not found";
assertArrayIncludes(channel.permission_overwrites!, [
{
id: data.roleID,
type: OverwriteType.ROLE,
// The type for Channel#permission_overwrites is "RawOverwrite[] | undefined"
// not "Overwrite[]"; therefore, permission strings cannot be used.
// allow: ["VIEW_CHANNEL", "SEND_MESSAGES"],
// deny: ["USE_EXTERNAL_EMOJIS"],
},
]);
// THIS TEST CASE SHOULD BE REFACTORED AND IMPROVED
// CURRENTLY, IT USES Channel#permission_overwrites
// but preferably, it should use the channelOverwriteHasPermission()
},
...testOptions,
});
// Message
let messageID: string;
Deno.test({
name: "create a message in a guild",
async fn() {
const createdMessage = await sendMessage(data.channelID, "test");
// Check whether the created message is nil or not
assert(createdMessage);
messageID = createdMessage.id;
},
});
Deno.test({
name: "get a message in a guild",
async fn() {
const message = await getMessage(data.channelID, messageID);
assertEquals(messageID, message.id);
},
});
// Clean up
Deno.test({
name: "delete a role from the guild",
async fn() {
await deleteRole(data.guildID, data.roleID);
data.roleID = "";
assertEquals(data.roleID, "");
},
});
Deno.test({
name: "delete a channel in the guild",
async fn() {
await deleteChannel(data.guildID, data.channelID);
},
...testOptions,
});
Deno.test({
name: "delete a guild",
async fn() {
await deleteServer(data.guildID);
data.guildID = "";
assertEquals(data.guildID, "");
},
...testOptions,
});
// This is meant to be the final test that forcefully crashes the bot
Deno.test({
name: "exit the process forcefully after all the tests are done",
async fn() {
Deno.exit(1);
},
...testOptions,
});