259 lines
9.2 KiB
TypeScript
259 lines
9.2 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
import events from '../src/events/index.js'
|
|
import { Client, TextChannel, User, Message } from 'discord.js'
|
|
import { redis, ollama } from '../src/client.js'
|
|
import { Queue } from '../src/queues/queue.js'
|
|
import { UserMessage } from '../src/utils/index.js'
|
|
|
|
// Mock Redis client
|
|
vi.mock('../src/client.js', () => ({
|
|
redis: {
|
|
get: vi.fn().mockResolvedValue('0.50'),
|
|
set: vi.fn().mockResolvedValue('OK'),
|
|
},
|
|
ollama: {
|
|
chat: vi.fn(), // Mock the chat method for messageCreate
|
|
pull: vi.fn(), // Retain mock for pull method
|
|
},
|
|
}))
|
|
|
|
/**
|
|
* Events test suite, tests the events object and messageCreate event behavior
|
|
*/
|
|
describe('Events Tests', () => {
|
|
// Test definition of events object
|
|
it('references defined object', () => {
|
|
expect(typeof events).toBe('object')
|
|
})
|
|
|
|
// Test specific events in the object
|
|
it('references specific events', () => {
|
|
const eventsString = events.map(e => e.key.toString()).join(', ')
|
|
expect(eventsString).toBe('ready, messageCreate, interactionCreate, threadDelete')
|
|
})
|
|
|
|
// Test messageCreate event for bot-to-bot response
|
|
describe('messageCreate', () => {
|
|
const messageCreateEvent = events.find(e => e.key === 'messageCreate')
|
|
if (!messageCreateEvent) throw new Error('messageCreate event not found')
|
|
|
|
it('should respond to bot message with random chance and respect cooldown', async () => {
|
|
const client = { user: { id: 'bot1', username: 'TestBot' } } as Client
|
|
const message = {
|
|
author: { id: 'bot2', bot: true, tag: 'OtherBot#1234', username: 'OtherBot' },
|
|
content: 'Hello from another bot!',
|
|
mentions: { has: () => false },
|
|
channelId: 'channel1',
|
|
channel: { name: 'test-channel' } as TextChannel,
|
|
reply: vi.fn(),
|
|
attachments: { first: () => null },
|
|
guildId: 'guild1',
|
|
} as unknown as Message
|
|
const msgHist = new Queue<UserMessage>()
|
|
msgHist.capacity = 50
|
|
const defaultModel = 'aidoll-gemma3-12b-q6:latest'
|
|
|
|
// Mock random chance to pass (10% probability)
|
|
vi.spyOn(Math, 'random').mockReturnValue(0.05)
|
|
|
|
// Mock Redis: no cooldown initially
|
|
vi.mocked(redis.get).mockResolvedValueOnce(null) // No last_bot_response
|
|
vi.mocked(redis.get).mockResolvedValueOnce('0.50') // Bot sentiment for bot2
|
|
vi.mocked(redis.get).mockResolvedValueOnce('0.50') // Self sentiment
|
|
|
|
// Mock fs for personality.json
|
|
vi.spyOn(fs, 'readFile').mockResolvedValue(
|
|
JSON.stringify({
|
|
character: 'You are Kuroki Tomoko, a shy and socially awkward high school girl from WataMote.',
|
|
})
|
|
)
|
|
|
|
// Mock utils functions
|
|
vi.mock('../src/utils/index.js', () => ({
|
|
clean: vi.fn(content => content),
|
|
getServerConfig: vi.fn((_, cb) => cb({ options: { 'toggle-chat': true } })),
|
|
getUserConfig: vi.fn((_, cb) =>
|
|
cb({
|
|
options: {
|
|
'message-style': false,
|
|
'switch-model': 'aidoll-gemma3-12b-q6:latest',
|
|
'modify-capacity': 50,
|
|
},
|
|
})
|
|
),
|
|
getChannelInfo: vi.fn((_, cb) => cb({ messages: [] })),
|
|
openChannelInfo: vi.fn(),
|
|
openConfig: vi.fn(),
|
|
}))
|
|
|
|
// Mock Ollama response
|
|
vi.mocked(ollama.chat).mockResolvedValue({
|
|
message: {
|
|
content: JSON.stringify({
|
|
status: 'success',
|
|
reply: 'Hmph, another bot, huh? Trying to steal my spotlight?',
|
|
metadata: {
|
|
timestamp: '2025-05-21T14:00:00Z',
|
|
self_sentiment: 0.50,
|
|
user_sentiment: { 'bot2': 0.50 },
|
|
redis_ops: [
|
|
{ action: 'set', key: 'user:bot2:sentiment', value: 0.50 },
|
|
{ action: 'set', key: 'bot:self_sentiment', value: 0.50 },
|
|
],
|
|
need_help: false,
|
|
},
|
|
}),
|
|
},
|
|
})
|
|
|
|
// Execute messageCreate event
|
|
await messageCreateEvent.execute(
|
|
{ log: console.log, msgHist, ollama, client, defaultModel },
|
|
message
|
|
)
|
|
|
|
expect(message.reply).toHaveBeenCalledWith('Hmph, another bot, huh? Trying to steal my spotlight?')
|
|
expect(redis.set).toHaveBeenCalledWith(
|
|
'bot:bot1:last_bot_response',
|
|
expect.any(String),
|
|
{ EX: 60 }
|
|
)
|
|
expect(msgHist.size()).toBe(2) // User message + bot response
|
|
})
|
|
|
|
it('should skip bot message response if within cooldown', async () => {
|
|
const client = { user: { id: 'bot1', username: 'TestBot' } } as Client
|
|
const message = {
|
|
author: { id: 'bot2', bot: true, tag: 'OtherBot#1234', username: 'OtherBot' },
|
|
content: 'Hello again!',
|
|
mentions: { has: () => false },
|
|
channelId: 'channel1',
|
|
channel: { name: 'test-channel' } as TextChannel,
|
|
reply: vi.fn(),
|
|
attachments: { first: () => null },
|
|
guildId: 'guild1',
|
|
} as unknown as Message
|
|
const msgHist = new Queue<UserMessage>()
|
|
msgHist.capacity = 50
|
|
const defaultModel = 'aidoll-gemma3-12b-q6:latest'
|
|
|
|
// Mock random chance to pass
|
|
vi.spyOn(Math, 'random').mockReturnValue(0.05)
|
|
|
|
// Mock Redis: within cooldown
|
|
const currentTime = Math.floor(Date.now() / 1000)
|
|
vi.mocked(redis.get).mockResolvedValueOnce((currentTime - 30).toString()) // Cooldown active
|
|
|
|
// Execute messageCreate event
|
|
await messageCreateEvent.execute(
|
|
{ log: console.log, msgHist, ollama, client, defaultModel },
|
|
message
|
|
)
|
|
|
|
expect(message.reply).not.toHaveBeenCalled()
|
|
expect(redis.set).not.toHaveBeenCalled()
|
|
expect(msgHist.size()).toBe(0) // No messages added
|
|
})
|
|
|
|
it('should respond to user mention', async () => {
|
|
const client = { user: { id: 'bot1', username: 'TestBot' } } as Client
|
|
const message = {
|
|
author: { id: 'user1', bot: false, tag: 'User#1234', username: 'User' },
|
|
content: '<@bot1> Hi!',
|
|
mentions: { has: (id: string) => id === 'bot1' },
|
|
channelId: 'channel1',
|
|
channel: { name: 'test-channel' } as TextChannel,
|
|
reply: vi.fn(),
|
|
attachments: { first: () => null },
|
|
guildId: 'guild1',
|
|
} as unknown as Message
|
|
const msgHist = new Queue<UserMessage>()
|
|
msgHist.capacity = 50
|
|
const defaultModel = 'aidoll-gemma3-12b-q6:latest'
|
|
|
|
// Mock fs for personality.json
|
|
vi.spyOn(fs, 'readFile').mockResolvedValue(
|
|
JSON.stringify({
|
|
character: 'You are Kuroki Tomoko, a shy and socially awkward high school girl from WataMote.',
|
|
})
|
|
)
|
|
|
|
// Mock utils functions
|
|
vi.mock('../src/utils/index.js', () => ({
|
|
clean: vi.fn(content => content),
|
|
getServerConfig: vi.fn((_, cb) => cb({ options: { 'toggle-chat': true } })),
|
|
getUserConfig: vi.fn((_, cb) =>
|
|
cb({
|
|
options: {
|
|
'message-style': false,
|
|
'switch-model': 'aidoll-gemma3-12b-q6:latest',
|
|
'modify-capacity': 50,
|
|
},
|
|
})
|
|
),
|
|
getChannelInfo: vi.fn((_, cb) => cb({ messages: [] })),
|
|
openChannelInfo: vi.fn(),
|
|
openConfig: vi.fn(),
|
|
}))
|
|
|
|
// Mock Ollama response
|
|
vi.mocked(ollama.chat).mockResolvedValue({
|
|
message: {
|
|
content: JSON.stringify({
|
|
status: 'success',
|
|
reply: 'U-um... hi... you talking to me?',
|
|
metadata: {
|
|
timestamp: '2025-05-21T14:00:00Z',
|
|
self_sentiment: 0.50,
|
|
user_sentiment: { 'user1': 0.50 },
|
|
redis_ops: [
|
|
{ action: 'set', key: 'user:user1:sentiment', value: 0.50 },
|
|
{ action: 'set', key: 'bot:self_sentiment', value: 0.50 },
|
|
],
|
|
need_help: false,
|
|
},
|
|
}),
|
|
},
|
|
})
|
|
|
|
// Execute messageCreate event
|
|
await messageCreateEvent.execute(
|
|
{ log: console.log, msgHist, ollama, client, defaultModel },
|
|
message
|
|
)
|
|
|
|
expect(message.reply).toHaveBeenCalledWith('U-um... hi... you talking to me?')
|
|
expect(redis.set).toHaveBeenCalledWith('user:user1:sentiment', '0.50')
|
|
expect(redis.set).toHaveBeenCalledWith('bot:self_sentiment', '0.50')
|
|
expect(msgHist.size()).toBe(2) // User message + bot response
|
|
})
|
|
|
|
it('should not respond to own message', async () => {
|
|
const client = { user: { id: 'bot1', username: 'TestBot' } } as Client
|
|
const message = {
|
|
author: { id: 'bot1', bot: true, tag: 'TestBot#1234', username: 'TestBot' },
|
|
content: 'I said something!',
|
|
mentions: { has: () => false },
|
|
channelId: 'channel1',
|
|
channel: { name: 'test-channel' } as TextChannel,
|
|
reply: vi.fn(),
|
|
attachments: { first: () => null },
|
|
guildId: 'guild1',
|
|
} as unknown as Message
|
|
const msgHist = new Queue<UserMessage>()
|
|
msgHist.capacity = 50
|
|
const defaultModel = 'aidoll-gemma3-12b-q6:latest'
|
|
|
|
// Execute messageCreate event
|
|
await messageCreateEvent.execute(
|
|
{ log: console.log, msgHist, ollama, client, defaultModel },
|
|
message
|
|
)
|
|
|
|
expect(message.reply).not.toHaveBeenCalled()
|
|
expect(redis.set).not.toHaveBeenCalled()
|
|
expect(msgHist.size()).toBe(0) // No messages added
|
|
})
|
|
})
|
|
})
|