vylbot-app/tests/commands/ban.test.ts
Ethan Lane 5409ecaffc
All checks were successful
continuous-integration/drone/push Build is passing
Create ban command tests
2024-03-28 19:02:14 +00:00

509 lines
16 KiB
TypeScript

import { APIEmbedField, CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, SlashCommandStringOption, SlashCommandUserOption } from "discord.js";
import Ban from "../../src/commands/ban";
import SettingsHelper from "../../src/helpers/SettingsHelper";
import Audit from "../../src/database/entities/Audit";
import EmbedColours from "../../src/constants/EmbedColours";
beforeEach(() => {
process.env = {};
});
describe('Constructor', () => {
test('Expect values to be set', () => {
const command = new Ban();
expect(command.CommandBuilder).toBeDefined();
const commandBuilder = command.CommandBuilder as SlashCommandBuilder;
expect(commandBuilder.name).toBe("ban");
expect(commandBuilder.description).toBe("Ban a member from the server with an optional reason");
expect(commandBuilder.default_member_permissions).toBe(PermissionsBitField.Flags.BanMembers.toString());
expect(commandBuilder.options.length).toBe(2);
const commandBuilderTargetOption = commandBuilder.options[0] as SlashCommandUserOption;
expect(commandBuilderTargetOption.name).toBe("target");
expect(commandBuilderTargetOption.description).toBe("The user");
expect(commandBuilderTargetOption.required).toBeTruthy();
const commandBuilderReasonOption = commandBuilder.options[1] as SlashCommandStringOption;
expect(commandBuilderReasonOption.name).toBe("reason");
expect(commandBuilderReasonOption.description).toBe("The reason");
});
});
describe('Execute', () => {
test('GIVEN command is valid, EXPECT user to be banned', async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: true,
ban: jest.fn(),
},
};
const reason = {
value: "reason",
};
const modChannel = {
send: jest.fn().mockImplementation((option) => {
sentWith = option;
}),
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(modChannel),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("channelName");
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1);
expect(interaction.options.get).toHaveBeenCalledTimes(2);
expect(interaction.options.get).toHaveBeenCalledWith("target");
expect(interaction.options.get).toHaveBeenCalledWith("reason");
expect(interaction.guild?.channels.cache.find).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("`userTag` has been banned.");
expect(targetUser.member.ban).toHaveBeenCalledTimes(1);
expect(targetUser.user.avatarURL).toHaveBeenCalledTimes(1);
expect(modChannel.send).toHaveBeenCalledTimes(1);
expect(sentWith).toBeDefined();
expect(sentWith!.embeds).toBeDefined();
expect(sentWith!.embeds.length).toBe(1);
const logEmbed = sentWith!.embeds[0] as EmbedBuilder;
expect(logEmbed.data.color).toBe(EmbedColours.Ok);
expect(logEmbed.data.title).toBe("Member Banned");
expect(logEmbed.data.description).toBe("<@userId> `userTag`");
expect(logEmbed.data.thumbnail).toBeDefined();
expect(logEmbed.data.thumbnail!.url).toBe("https://avatarurl.com/user.png");
expect(logEmbed.data.fields).toBeDefined();
expect(logEmbed.data.fields!.length).toBe(2);
const logEmbedModeratorField = logEmbed.data.fields![0] as APIEmbedField;
expect(logEmbedModeratorField.name).toBe("Moderator");
expect(logEmbedModeratorField.value).toBe("<@moderatorId>");
const logEmbedReasonField = logEmbed.data.fields![1] as APIEmbedField;
expect(logEmbedReasonField.name).toBe("Reason");
expect(logEmbedReasonField.value).toBe("reason");
expect(SettingsHelper.GetSetting).toHaveBeenCalledTimes(1);
expect(SettingsHelper.GetSetting).toHaveBeenCalledWith("channels.logs.mod", "guildId");
});
test("GIVEN interaction is NOT a chat input command, EXPECT nothing to happen", async () => {
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(false),
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1);
expect(interaction.reply).not.toHaveBeenCalled();
});
test("GIVEN interaction.guildId is null, EXPECT nothing to happen", async () => {
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: null,
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1);
expect(interaction.reply).not.toHaveBeenCalled();
});
test("GIVEN interaction.guild is null, EXPECT nothing to happen", async () => {
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: null,
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1);
expect(interaction.reply).not.toHaveBeenCalled();
});
test("GIVEN targetUser is null, EXPECT user not found error", async () => {
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {},
options: {
get: jest.fn().mockReturnValue(null),
},
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("User not found.");
});
test("GIVEN targetUser.user is undefined, EXPECT user not found error", async () => {
const user = {
user: undefined,
}
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {},
options: {
get: jest.fn().mockReturnValue(user),
},
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("User not found.");
});
test("GIVEN targetUser.member is undefined, EXPECT user not found error", async () => {
const user = {
user: {},
member: undefined,
}
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {},
options: {
get: jest.fn().mockReturnValue(user),
},
reply: jest.fn(),
} as unknown as CommandInteraction;
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("User not found.");
});
test("GIVEN reasonInput is null, EXPECT reason to be defaulted", async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: true,
ban: jest.fn(),
},
};
const modChannel = {
send: jest.fn().mockImplementation((option) => {
sentWith = option;
}),
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(modChannel),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(null),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("channelName");
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
const logEmbed = sentWith!.embeds[0] as EmbedBuilder;
const logEmbedReasonField = logEmbed.data.fields![1] as APIEmbedField;
expect(logEmbedReasonField.name).toBe("Reason");
expect(logEmbedReasonField.value).toBe("*none*");
});
test("GIVEN reasonInput.value is undefined, EXPECT reason to be defaulted", async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: true,
ban: jest.fn(),
},
};
const modChannel = {
send: jest.fn().mockImplementation((option) => {
sentWith = option;
}),
};
const reason = {
value: undefined,
}
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(modChannel),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("channelName");
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
const logEmbed = sentWith!.embeds[0] as EmbedBuilder;
const logEmbedReasonField = logEmbed.data.fields![1] as APIEmbedField;
expect(logEmbedReasonField.name).toBe("Reason");
expect(logEmbedReasonField.value).toBe("*none*");
});
test("GIVEN user is not bannable, EXPECT insufficient permissions error", async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: false,
ban: jest.fn(),
},
};
const reason = {
value: "reason",
};
const modChannel = {
send: jest.fn().mockImplementation((option) => {
sentWith = option;
}),
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(modChannel),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("channelName");
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
expect(targetUser.member.ban).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith("Insufficient permissions. Please contact a moderator.");
});
test("GIVEN channels.log.mod setting is not set, EXPECT command to return", async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: true,
ban: jest.fn(),
},
};
const reason = {
value: "reason",
};
const modChannel = {
send: jest.fn().mockImplementation((option) => {
sentWith = option;
}),
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(modChannel),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue(undefined);
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
expect(interaction.guild?.channels.cache.find).not.toHaveBeenCalled();
});
test("GIVEN channel can NOT be found, EXPECT logEmbed not to be sent", async () => {
let sentWith;
const targetUser = {
user: {
tag: "userTag",
id: "userId",
avatarURL: jest.fn().mockReturnValue("https://avatarurl.com/user.png"),
},
member: {
bannable: true,
ban: jest.fn(),
},
};
const reason = {
value: "reason",
};
const interaction = {
isChatInputCommand: jest.fn().mockReturnValue(true),
guildId: "guildId",
guild: {
channels: {
cache: {
find: jest.fn().mockReturnValue(null),
}
}
},
options: {
get: jest.fn().mockReturnValueOnce(targetUser)
.mockReturnValue(reason),
},
user: {
id: "moderatorId",
},
reply: jest.fn(),
} as unknown as CommandInteraction;
SettingsHelper.GetSetting = jest.fn().mockResolvedValue("channelName");
Audit.prototype.Save = jest.fn();
const ban = new Ban();
await ban.execute(interaction);
expect(Audit.prototype.Save).toHaveBeenCalled();
});
});