import { describe, it, expect, jest, beforeEach } from '@jest/globals'; import { askDuckTool } from '../../src/tools/ask-duck.js'; import { ProviderManager } from '../../src/providers/manager.js'; // Mock dependencies jest.mock('../../src/utils/logger'); jest.mock('../../src/providers/manager.js'); describe('askDuckTool', () => { let mockProviderManager: jest.Mocked; const mockResponse = { provider: 'openai', nickname: 'OpenAI Duck', content: 'This is a test response from the duck.', model: 'gpt-4', latency: 150, usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30, }, }; beforeEach(() => { mockProviderManager = { askDuck: jest.fn().mockResolvedValue(mockResponse), validateModel: jest.fn().mockReturnValue(true), } as unknown as jest.Mocked; }); it('should throw error when prompt is missing', async () => { await expect(askDuckTool(mockProviderManager, {})).rejects.toThrow( 'Prompt is required' ); }); it('should ask duck with basic prompt', async () => { const result = await askDuckTool(mockProviderManager, { prompt: 'What is TypeScript?', }); expect(mockProviderManager.askDuck).toHaveBeenCalledWith(undefined, 'What is TypeScript?', { model: undefined, temperature: undefined, }); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('OpenAI Duck'); expect(result.content[0].text).toContain('test response'); }); it('should include usage info in response', async () => { const result = await askDuckTool(mockProviderManager, { prompt: 'Test prompt', }); expect(result.content[0].text).toContain('Tokens used: 30'); expect(result.content[0].text).toContain('10 prompt'); expect(result.content[0].text).toContain('20 completion'); }); it('should show latency in response', async () => { const result = await askDuckTool(mockProviderManager, { prompt: 'Test prompt', }); expect(result.content[0].text).toContain('150ms'); }); it('should pass provider option', async () => { await askDuckTool(mockProviderManager, { prompt: 'Test', provider: 'groq', }); expect(mockProviderManager.askDuck).toHaveBeenCalledWith('groq', 'Test', expect.any(Object)); }); it('should pass model and temperature options', async () => { await askDuckTool(mockProviderManager, { prompt: 'Test', provider: 'openai', model: 'gpt-3.5-turbo', temperature: 0.5, }); expect(mockProviderManager.askDuck).toHaveBeenCalledWith('openai', 'Test', { model: 'gpt-3.5-turbo', temperature: 0.5, }); }); it('should validate model when both provider and model are specified', async () => { await askDuckTool(mockProviderManager, { prompt: 'Test', provider: 'openai', model: 'gpt-4', }); expect(mockProviderManager.validateModel).toHaveBeenCalledWith('openai', 'gpt-4'); }); it('should not validate model when provider is not specified', async () => { await askDuckTool(mockProviderManager, { prompt: 'Test', model: 'gpt-4', }); expect(mockProviderManager.validateModel).not.toHaveBeenCalled(); }); it('should handle response without usage info', async () => { const responseWithoutUsage = { ...mockResponse, usage: undefined }; mockProviderManager.askDuck.mockResolvedValue(responseWithoutUsage); const result = await askDuckTool(mockProviderManager, { prompt: 'Test', }); expect(result.content[0].text).not.toContain('Tokens used'); }); it('should warn when model is not valid for provider', async () => { mockProviderManager.validateModel.mockReturnValue(false); // Should still work, just log a warning const result = await askDuckTool(mockProviderManager, { prompt: 'Test', provider: 'openai', model: 'invalid-model', }); expect(mockProviderManager.validateModel).toHaveBeenCalledWith('openai', 'invalid-model'); expect(result.content[0].text).toContain('test response'); }); });