import { jest, expect, describe, beforeEach, it } from '@jest/globals' import { Bot } from '../bot' import { Options, AIOptions } from '../options' // Mock chatgpt module jest.mock('chatgpt', () => ({ ChatGPTAPI: jest.fn().mockImplementation(() => ({ sendMessage: jest.fn(), })), ChatGPTError: class ChatGPTError extends Error { status?: number constructor(message: string, status?: number) { super(message) this.status = status } }, })) describe('Bot', () => { let options: Options let aiOptions: AIOptions beforeEach(() => { jest.clearAllMocks() options = new Options( false, false, false, '0', false, false, null, '', 'meta-llama/llama-4-scout-17b-16e-instruct', 'llama-3.3-70b-versatile', '0.0', '3', '120000', '1', '1', 'https://api.groq.com/openai/v1', 'en-US', false, 'minor', '0', 'false', 'true' ) aiOptions = new AIOptions('meta-llama/llama-4-scout-17b-16e-instruct') }) describe('constructor', () => { it('should throw error if API key is missing', () => { const originalKey = process.env.GROQ_API_KEY delete process.env.GROQ_API_KEY delete process.env.AI_API_KEY expect(() => new Bot(options, aiOptions)).toThrow("Missing 'GROQ_API_KEY' or 'AI_API_KEY'") process.env.GROQ_API_KEY = originalKey }) it('should initialize with valid API key', () => { const bot = new Bot(options, aiOptions) expect(bot).toBeInstanceOf(Bot) }) it('should set up fallback models for heavy models', () => { const heavyOptions = new AIOptions('llama-3.3-70b-versatile') const bot = new Bot(options, heavyOptions) expect(bot).toBeInstanceOf(Bot) }) }) describe('chat', () => { it('should return empty response for empty message', async () => { const bot = new Bot(options, aiOptions) const result = await bot.chat('', {}) expect(result).toEqual(['', {}]) }) it('should handle AI errors gracefully', async () => { const { ChatGPTAPI } = await import('chatgpt') const mockSendMessage = jest.fn().mockRejectedValue(new Error('API Error')) ;(ChatGPTAPI as jest.Mock).mockImplementation(() => ({ sendMessage: mockSendMessage, })) const bot = new Bot(options, aiOptions) const result = await bot.chat('test message', {}) expect(result[0]).toBe('') // Should return empty on error }) it('should implement retry logic on rate limit', async () => { const { ChatGPTAPI } = await import('chatgpt') const mockSendMessage = jest.fn() .mockRejectedValueOnce({ status: 429, message: 'Rate limited' }) .mockResolvedValueOnce({ text: 'Success', id: 'msg-1' }) ;(ChatGPTAPI as jest.Mock).mockImplementation(() => ({ sendMessage: mockSendMessage, })) const bot = new Bot(options, aiOptions) // Should switch to fallback model on 429 await bot.chat('test', {}) expect(mockSendMessage).toHaveBeenCalled() }) }) describe('fallback chain', () => { it('should cycle through fallback models on errors', () => { const heavyOptions = new AIOptions('llama-3.3-70b-versatile') const bot = new Bot(options, heavyOptions) // Fallback chain should be initialized expect(bot).toBeInstanceOf(Bot) }) }) })