Initial commit

This commit is contained in:
2025-02-02 14:10:51 -05:00
commit 9c74e724a8
28 changed files with 10554 additions and 0 deletions
+360
View File
@@ -0,0 +1,360 @@
const axios = require('axios');
const { createMockInteraction } = require('../utils/testUtils');
// Mock axios
jest.mock('axios');
// Mock the discord.js module
jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
addStringOption: jest.fn().mockImplementation(callback => {
const option = {
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
setRequired: jest.fn().mockReturnThis(),
};
callback(option);
return {
addBooleanOption: jest.fn().mockImplementation(boolCallback => {
const boolOption = {
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
setRequired: jest.fn().mockReturnThis(),
};
boolCallback(boolOption);
return {
toJSON: jest.fn().mockReturnValue({
name: 'ask',
description: 'Ask a question to the AI',
options: [
{
name: 'prompt',
description: 'Your question or prompt',
type: 3,
required: true,
},
{
name: 'websearch',
description:
'Enable web search for more up-to-date information',
type: 5,
required: false,
},
],
}),
};
}),
};
}),
toJSON: jest.fn(),
}),
}));
const askCommand = require('../../commands/ask');
describe('Ask Command', () => {
describe('Command Structure', () => {
it('should have correct name and description', () => {
const commandData = askCommand.data.toJSON();
expect(commandData.name).toBe('ask');
expect(commandData.description).toBe('Ask a question to the AI');
});
it('should have required command properties', () => {
expect(askCommand).toHaveProperty('data');
expect(askCommand).toHaveProperty('execute');
expect(typeof askCommand.execute).toBe('function');
});
it('should have correct option configuration', () => {
const commandData = askCommand.data.toJSON();
const [promptOption, websearchOption] = commandData.options;
expect(promptOption.name).toBe('prompt');
expect(promptOption.description).toBe('Your question or prompt');
expect(promptOption.required).toBe(true);
expect(websearchOption.name).toBe('websearch');
expect(websearchOption.description).toBe(
'Enable web search for more up-to-date information'
);
expect(websearchOption.required).toBe(false);
});
});
describe('Command Execution', () => {
let interaction;
const mockPrompt = 'What is the meaning of life?';
const mockApiResponse = {
data: {
choices: [
{
message: {
content: 'The meaning of life is 42.',
},
},
],
},
};
beforeEach(() => {
process.env.OPENROUTER_API_KEY = 'test-api-key';
axios.post.mockReset();
jest.clearAllMocks();
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: false,
},
});
});
it('should handle successful API response with websearch disabled', async () => {
axios.post.mockResolvedValueOnce(mockApiResponse);
await askCommand.execute(interaction);
expect(axios.post).toHaveBeenCalledWith(
'https://openrouter.ai/api/v1/chat/completions',
expect.objectContaining({
model: 'google/gemini-2.0-flash-exp:free',
plugins: undefined,
}),
expect.any(Object)
);
expect(interaction.followUp).toHaveBeenCalledWith({
content: expect.not.stringContaining('Web search enabled'),
split: false,
allowedMentions: { parse: [] },
});
});
it('should handle successful API response with websearch enabled', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
});
axios.post.mockResolvedValueOnce(mockApiResponse);
await askCommand.execute(interaction);
expect(axios.post).toHaveBeenCalledWith(
'https://openrouter.ai/api/v1/chat/completions',
expect.objectContaining({
model: 'google/gemini-2.0-flash-exp:free:online',
plugins: expect.arrayContaining([
expect.objectContaining({
id: 'web',
max_results: 3,
}),
]),
}),
expect.any(Object)
);
expect(interaction.followUp).toHaveBeenCalledWith({
content: expect.stringContaining('Web search enabled'),
split: false,
allowedMentions: { parse: [] },
});
});
it('should use default websearch value when not provided', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
// Not providing websearch option
});
axios.post.mockResolvedValueOnce(mockApiResponse);
await askCommand.execute(interaction);
expect(axios.post).toHaveBeenCalledWith(
'https://openrouter.ai/api/v1/chat/completions',
expect.objectContaining({
model: 'google/gemini-2.0-flash-exp:free',
plugins: undefined,
}),
expect.any(Object)
);
});
it('should handle long responses with proper chunking and websearch enabled', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
});
const longResponse = 'A'.repeat(2500);
axios.post.mockResolvedValueOnce({
data: {
choices: [
{
message: {
content: longResponse,
},
},
],
},
});
await askCommand.execute(interaction);
expect(interaction.followUp).toHaveBeenCalledTimes(2);
expect(interaction.followUp.mock.calls[0][0].content).toContain(
'Web search enabled'
);
expect(interaction.followUp.mock.calls[1][0].content).toContain(
'(continued)'
);
});
it('should handle code blocks in chunked responses with websearch', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
});
const responseWithCodeBlock =
"Here's a code example:\n```python\nprint('hello')\n```".repeat(20);
axios.post.mockResolvedValueOnce({
data: {
choices: [
{
message: {
content: responseWithCodeBlock,
},
},
],
},
});
await askCommand.execute(interaction);
// Verify that code blocks are not split in the middle
interaction.followUp.mock.calls.forEach(call => {
const { content } = call[0];
const openBlocks = (content.match(/```/g) || []).length;
expect(openBlocks % 2).toBe(0); // Should always be even
});
// First chunk should contain websearch indicator
expect(interaction.followUp.mock.calls[0][0].content).toContain(
'Web search enabled'
);
});
it('should handle API errors with websearch enabled', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
});
const error = new Error('API Error');
error.response = { data: 'API Error Details' };
axios.post.mockRejectedValueOnce(error);
await askCommand.execute(interaction);
expect(interaction.followUp).toHaveBeenCalledWith({
content: 'Sorry, there was an error processing your request.',
ephemeral: true,
});
});
it('should send API request with correct headers regardless of websearch', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
});
axios.post.mockResolvedValueOnce(mockApiResponse);
await askCommand.execute(interaction);
expect(axios.post).toHaveBeenCalledWith(
expect.any(String),
expect.any(Object),
{
headers: {
Authorization: 'Bearer test-api-key',
'HTTP-Referer': 'https://github.com/hllywluis/kekbot.js',
'Content-Type': 'application/json',
},
}
);
});
it('should handle defer reply failure with websearch', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
deferFails: true,
});
await expect(askCommand.execute(interaction)).rejects.toThrow(
'Failed to defer reply'
);
});
it('should handle follow up failure with websearch', async () => {
interaction = createMockInteraction({
commandName: 'ask',
stringOptions: {
prompt: mockPrompt,
},
booleanOptions: {
websearch: true,
},
followUpFails: true,
});
axios.post.mockResolvedValueOnce(mockApiResponse);
await expect(askCommand.execute(interaction)).rejects.toThrow(
'Failed to follow up'
);
});
});
});
+106
View File
@@ -0,0 +1,106 @@
const { createMockInteraction } = require('../utils/testUtils');
// Mock the discord.js module
jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockReturnValue({
name: 'help',
description: 'Lists all available commands',
}),
}),
EmbedBuilder: jest.fn().mockReturnValue({
setColor: jest.fn().mockReturnThis(),
setTitle: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
setTimestamp: jest.fn().mockReturnThis(),
addFields: jest.fn().mockReturnThis(),
}),
}));
const helpCommand = require('../../commands/help');
describe('Help Command', () => {
describe('Command Structure', () => {
it('should have correct name and description', () => {
const commandData = helpCommand.data.toJSON();
expect(commandData.name).toBe('help');
expect(commandData.description).toBe('Lists all available commands');
});
it('should have required command properties', () => {
expect(helpCommand).toHaveProperty('data');
expect(helpCommand).toHaveProperty('execute');
expect(typeof helpCommand.execute).toBe('function');
});
});
describe('Command Execution', () => {
let interaction;
const mockCommands = new Map([
['ping', { data: { name: 'ping', description: 'Replies with Pong!' } }],
[
'help',
{ data: { name: 'help', description: 'Lists all available commands' } },
],
]);
beforeEach(() => {
interaction = createMockInteraction({
commandName: 'help',
});
interaction.client.commands = mockCommands;
});
it('should create an embed with all commands', async () => {
await helpCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith(
expect.objectContaining({
embeds: expect.arrayContaining([expect.any(Object)]),
ephemeral: true,
})
);
});
it('should handle empty commands collection', async () => {
interaction.client.commands = new Map();
await helpCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith(
expect.objectContaining({
embeds: expect.arrayContaining([expect.any(Object)]),
ephemeral: true,
})
);
});
it('should handle interaction reply failure', async () => {
interaction = createMockInteraction({
commandName: 'help',
replyFails: true,
});
interaction.client.commands = mockCommands;
await expect(helpCommand.execute(interaction)).rejects.toThrow(
'Failed to reply',
);
});
it('should create embed with correct structure', async () => {
const { EmbedBuilder } = require('discord.js');
await helpCommand.execute(interaction);
const embedInstance = EmbedBuilder.mock.results[0].value;
expect(embedInstance.setColor).toHaveBeenCalledWith('#5dc67b');
expect(embedInstance.setTitle).toHaveBeenCalledWith('Available Commands');
expect(embedInstance.setDescription).toHaveBeenCalledWith(
'Here are all my commands:',
);
expect(embedInstance.setTimestamp).toHaveBeenCalled();
expect(embedInstance.addFields).toHaveBeenCalledTimes(mockCommands.size);
});
});
});
+221
View File
@@ -0,0 +1,221 @@
const { createMockInteraction } = require('../utils/testUtils');
// Mock the discord.js module
jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
addUserOption: jest.fn().mockImplementation(callback => {
const option = {
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
setRequired: jest.fn().mockReturnThis(),
};
callback(option);
return {
addStringOption: jest.fn().mockImplementation(callback => {
const option = {
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
};
callback(option);
return {
setDefaultMemberPermissions: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockReturnValue({
name: 'kick',
description: 'Kick a user from the server',
options: [
{
name: 'target',
description: 'The user to kick',
type: 6,
required: true,
},
{
name: 'reason',
description: 'Reason for kicking',
type: 3,
required: false,
},
],
}),
};
}),
};
}),
toJSON: jest.fn(),
}),
PermissionFlagsBits: {
KickMembers: 0x2n,
},
}));
const kickCommand = require('../../commands/kick');
describe('Kick Command', () => {
describe('Command Structure', () => {
it('should have correct name and description', () => {
const commandData = kickCommand.data.toJSON();
expect(commandData.name).toBe('kick');
expect(commandData.description).toBe('Kick a user from the server');
});
it('should have required command properties', () => {
expect(kickCommand).toHaveProperty('data');
expect(kickCommand).toHaveProperty('execute');
expect(typeof kickCommand.execute).toBe('function');
});
it('should have correct option configuration', () => {
const commandData = kickCommand.data.toJSON();
const [targetOption, reasonOption] = commandData.options;
expect(targetOption.name).toBe('target');
expect(targetOption.description).toBe('The user to kick');
expect(targetOption.required).toBe(true);
expect(reasonOption.name).toBe('reason');
expect(reasonOption.description).toBe('Reason for kicking');
expect(reasonOption.required).toBe(false);
});
});
describe('Command Execution', () => {
let interaction;
const mockKick = jest.fn();
beforeEach(() => {
mockKick.mockReset();
const mockTarget = {
kickable: true,
kick: mockKick,
user: {
tag: 'TestUser#1234',
},
};
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: mockTarget,
},
stringOptions: {
reason: 'Test reason',
},
});
});
it('should successfully kick a user with reason', async () => {
await kickCommand.execute(interaction);
expect(mockKick).toHaveBeenCalledWith('Test reason');
expect(interaction.reply).toHaveBeenCalledWith({
content: expect.stringContaining('Successfully kicked TestUser#1234'),
ephemeral: true,
});
});
it('should use default reason when none provided', async () => {
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: {
kickable: true,
kick: mockKick,
user: { tag: 'TestUser#1234' },
},
},
stringOptions: {
reason: null,
},
});
await kickCommand.execute(interaction);
expect(mockKick).toHaveBeenCalledWith('No reason provided');
expect(interaction.reply).toHaveBeenCalledWith({
content: expect.stringContaining('No reason provided'),
ephemeral: true,
});
});
it('should handle non-existent target', async () => {
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: null,
},
});
await kickCommand.execute(interaction);
expect(mockKick).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledWith({
content: 'That user is not in this server!',
ephemeral: true,
});
});
it('should handle non-kickable target', async () => {
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: {
kickable: false,
user: { tag: 'Admin#1234' },
},
},
});
await kickCommand.execute(interaction);
expect(mockKick).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledWith({
content:
'I cannot kick this user! They may have higher permissions than me.',
ephemeral: true,
});
});
it('should handle kick failure', async () => {
const mockTarget = {
kickable: true,
kick: jest.fn().mockRejectedValueOnce(new Error('Failed to kick user')),
user: { tag: 'TestUser#1234' },
};
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: mockTarget,
},
});
await kickCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to kick this user!',
ephemeral: true,
});
});
it('should handle interaction reply failure', async () => {
interaction = createMockInteraction({
commandName: 'kick',
memberOptions: {
target: {
kickable: true,
kick: mockKick,
user: { tag: 'TestUser#1234' },
},
},
replyFails: true,
});
await expect(kickCommand.execute(interaction)).rejects.toThrow(
'Failed to reply',
);
});
});
});
+57
View File
@@ -0,0 +1,57 @@
const { createMockInteraction } = require('../utils/testUtils');
// Mock the discord.js module
jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockReturnValue({
name: 'ping',
description: 'Replies with Pong!',
}),
}),
}));
const pingCommand = require('../../commands/ping');
describe('Ping Command', () => {
describe('Command Structure', () => {
it('should have correct name and description', () => {
const commandData = pingCommand.data.toJSON();
expect(commandData.name).toBe('ping');
expect(commandData.description).toBe('Replies with Pong!');
});
it('should have required command properties', () => {
expect(pingCommand).toHaveProperty('data');
expect(pingCommand).toHaveProperty('execute');
expect(typeof pingCommand.execute).toBe('function');
});
});
describe('Command Execution', () => {
let interaction;
beforeEach(() => {
interaction = createMockInteraction({
commandName: 'ping',
});
});
it('should reply with "Pong! 🏓"', async () => {
await pingCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledTimes(1);
expect(interaction.reply).toHaveBeenCalledWith('Pong! 🏓');
});
it('should handle interaction reply failure', async () => {
// Mock a failed reply
interaction.reply.mockRejectedValueOnce(new Error('Failed to reply'));
await expect(pingCommand.execute(interaction)).rejects.toThrow(
'Failed to reply',
);
});
});
});
+140
View File
@@ -0,0 +1,140 @@
const { createMockInteraction } = require('../utils/testUtils');
// Mock the discord.js module
jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
addIntegerOption: jest.fn().mockImplementation(callback => {
const option = {
setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(),
setMinValue: jest.fn().mockReturnThis(),
setMaxValue: jest.fn().mockReturnThis(),
setRequired: jest.fn().mockReturnThis(),
};
callback(option);
return {
setDefaultMemberPermissions: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockReturnValue({
name: 'prune',
description: 'Prune up to 99 messages.',
options: [
{
name: 'amount',
description: 'Number of messages to prune',
type: 4,
required: true,
min_value: 1,
max_value: 99,
},
],
}),
};
}),
toJSON: jest.fn(),
}),
PermissionFlagsBits: {
ManageMessages: 0x2000n,
},
}));
const pruneCommand = require('../../commands/prune');
describe('Prune Command', () => {
describe('Command Structure', () => {
it('should have correct name and description', () => {
const commandData = pruneCommand.data.toJSON();
expect(commandData.name).toBe('prune');
expect(commandData.description).toBe('Prune up to 99 messages.');
});
it('should have required command properties', () => {
expect(pruneCommand).toHaveProperty('data');
expect(pruneCommand).toHaveProperty('execute');
expect(typeof pruneCommand.execute).toBe('function');
});
it('should have correct option configuration', () => {
const commandData = pruneCommand.data.toJSON();
const option = commandData.options[0];
expect(option.name).toBe('amount');
expect(option.description).toBe('Number of messages to prune');
expect(option.required).toBe(true);
expect(option.min_value).toBe(1);
expect(option.max_value).toBe(99);
});
});
describe('Command Execution', () => {
let interaction;
const mockBulkDelete = jest.fn();
beforeEach(() => {
mockBulkDelete.mockReset();
interaction = createMockInteraction({
commandName: 'prune',
integerOptions: {
amount: 5,
},
});
interaction.channel.bulkDelete = mockBulkDelete;
});
it('should successfully delete messages', async () => {
mockBulkDelete.mockResolvedValueOnce({ size: 5 });
await pruneCommand.execute(interaction);
expect(mockBulkDelete).toHaveBeenCalledWith(5, true);
expect(interaction.reply).toHaveBeenCalledWith({
content: 'Successfully deleted 5 message(s).',
ephemeral: true,
});
});
it('should handle bulk delete failure', async () => {
mockBulkDelete.mockRejectedValueOnce(
new Error('Failed to delete messages'),
);
await pruneCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to prune messages in this channel!',
ephemeral: true,
});
});
it('should handle messages older than 14 days', async () => {
mockBulkDelete.mockRejectedValueOnce(
new Error('Messages older than 14 days cannot be deleted'),
);
await pruneCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to prune messages in this channel!',
ephemeral: true,
});
});
it('should handle interaction reply failure', async () => {
mockBulkDelete.mockResolvedValueOnce({ size: 5 });
interaction = createMockInteraction({
commandName: 'prune',
integerOptions: {
amount: 5,
},
replyFails: true,
});
interaction.channel.bulkDelete = mockBulkDelete;
await expect(pruneCommand.execute(interaction)).rejects.toThrow(
'Failed to reply',
);
});
});
});
+96
View File
@@ -0,0 +1,96 @@
// Test utilities for Discord.js bot testing
/**
* Creates a mock interaction object with common properties and methods
* @param {Object} options - Customization options for the mock interaction
* @returns {Object} Mock interaction object
*/
const createMockInteraction = (options = {}) => ({
commandName: options.commandName || 'test-command',
user: {
id: options.userId || 'mock-user-id',
username: options.username || 'MockUser',
tag: options.userTag || 'MockUser#0000',
},
guild: {
id: options.guildId || 'mock-guild-id',
name: options.guildName || 'Mock Guild',
},
channel: {
id: options.channelId || 'mock-channel-id',
name: options.channelName || 'mock-channel',
send: jest.fn().mockResolvedValue({ id: 'mock-message-id' }),
bulkDelete: jest.fn().mockResolvedValue({ size: 0 }),
},
client: {
commands: new Map(),
user: {
id: 'mock-client-id',
username: 'MockBot',
setActivity: jest.fn(),
},
},
reply: jest.fn().mockImplementation(async _response => {
if (options.replyFails) {
throw new Error('Failed to reply');
}
return { id: 'mock-reply-id' };
}),
deferReply: jest.fn().mockImplementation(async () => {
if (options.deferFails) {
throw new Error('Failed to defer reply');
}
return true;
}),
editReply: jest.fn().mockResolvedValue({ id: 'mock-edit-id' }),
followUp: jest.fn().mockImplementation(async _response => {
if (options.followUpFails) {
throw new Error('Failed to follow up');
}
return { id: 'mock-followup-id' };
}),
options: {
getString: jest.fn(name => options.stringOptions?.[name]),
getInteger: jest.fn(name => options.integerOptions?.[name]),
getBoolean: jest.fn(name => options.booleanOptions?.[name]),
getUser: jest.fn(name => options.userOptions?.[name]),
getMember: jest.fn(name => options.memberOptions?.[name]),
get: jest.fn(name => options.options?.[name]),
},
member: {
permissions: {
has: jest.fn().mockReturnValue(options.hasPermission ?? true),
},
roles: {
cache: new Map(),
},
},
isCommand: () => true,
isChatInputCommand: () => true,
});
/**
* Creates a mock client object with common properties and methods
* @param {Object} options - Customization options for the mock client
* @returns {Object} Mock client object
*/
const createMockClient = (options = {}) => ({
user: {
id: options.clientId || 'mock-client-id',
username: options.clientUsername || 'MockBot',
setActivity: jest.fn(),
},
guilds: {
cache: new Map(),
},
commands: new Map(),
on: jest.fn(),
once: jest.fn(),
login: jest.fn().mockResolvedValue('token'),
destroy: jest.fn().mockResolvedValue(),
});
module.exports = {
createMockInteraction,
createMockClient,
};
+61
View File
@@ -0,0 +1,61 @@
const { createMockInteraction, createMockClient } = require('./testUtils');
describe('Test Utilities', () => {
describe('createMockInteraction', () => {
it('should create a mock interaction with default values', () => {
const interaction = createMockInteraction();
expect(interaction.commandName).toBe('test-command');
expect(interaction.user.id).toBe('mock-user-id');
expect(interaction.guild.id).toBe('mock-guild-id');
expect(interaction.channel.id).toBe('mock-channel-id');
expect(typeof interaction.reply).toBe('function');
expect(typeof interaction.deferReply).toBe('function');
expect(typeof interaction.editReply).toBe('function');
expect(typeof interaction.followUp).toBe('function');
});
it('should create a mock interaction with custom values', () => {
const customOptions = {
commandName: 'custom-command',
userId: 'custom-user-id',
username: 'CustomUser',
guildId: 'custom-guild-id',
channelId: 'custom-channel-id',
};
const interaction = createMockInteraction(customOptions);
expect(interaction.commandName).toBe('custom-command');
expect(interaction.user.id).toBe('custom-user-id');
expect(interaction.user.username).toBe('CustomUser');
expect(interaction.guild.id).toBe('custom-guild-id');
expect(interaction.channel.id).toBe('custom-channel-id');
});
});
describe('createMockClient', () => {
it('should create a mock client with default values', () => {
const client = createMockClient();
expect(client.user.id).toBe('mock-client-id');
expect(client.user.username).toBe('MockBot');
expect(typeof client.login).toBe('function');
expect(typeof client.destroy).toBe('function');
expect(typeof client.on).toBe('function');
expect(typeof client.once).toBe('function');
});
it('should create a mock client with custom values', () => {
const customOptions = {
clientId: 'custom-client-id',
clientUsername: 'CustomBot',
};
const client = createMockClient(customOptions);
expect(client.user.id).toBe('custom-client-id');
expect(client.user.username).toBe('CustomBot');
});
});
});