Skip to main content
Glama
tools.test.ts8.92 kB
/** * Unit tests for the tool handlers */ import { handleChatWithOnyx } from '../../tools/chatTool.js'; import { handleSearchOnyx } from '../../tools/searchTool.js'; import { OnyxApiService } from '../../services/onyxApi.js'; import type { OnyxSearchResult, ChatContentResponse } from '../../types/index.js'; import { jest } from '@jest/globals'; describe('Tool Handlers', () => { let mockOnyxApiService: OnyxApiService; beforeEach(() => { // Create a mock instance of OnyxApiService mockOnyxApiService = { apiUrl: 'http://test-api.com/api', apiToken: 'test-token', createChatSession: jest.fn<() => Promise<string>>(), sendChatMessage: jest.fn(), searchOnyx: jest.fn(), fetchDocumentChunk: jest.fn(), fetchDocumentContent: jest.fn(), buildContext: jest.fn() } as unknown as OnyxApiService; }); describe('handleChatWithOnyx', () => { beforeEach(() => { // Set up the mock implementations (mockOnyxApiService.createChatSession as jest.Mock).mockImplementation(() => { return Promise.resolve('test-session-id'); }); (mockOnyxApiService.sendChatMessage as jest.Mock).mockImplementation(() => { return Promise.resolve({ answer: 'Test answer', documents: [ { document_id: 'doc1', semantic_identifier: 'Test Document 1' }, { document_id: 'doc2', semantic_identifier: 'Test Document 2' }, ], }); }); }); it('should throw an error for invalid arguments', async () => { // Test with null arguments const result1 = await handleChatWithOnyx(null, mockOnyxApiService); expect(result1.isError).toBe(true); expect(result1.content[0].text).toContain('Invalid arguments'); // Test with non-object arguments const result2 = await handleChatWithOnyx('not an object', mockOnyxApiService); expect(result2.isError).toBe(true); expect(result2.content[0].text).toContain('Invalid arguments'); }); it('should throw an error for missing query', async () => { const result = await handleChatWithOnyx({ personaId: 15 }, mockOnyxApiService); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Query is required'); }); it('should create a new chat session when chatSessionId is not provided', async () => { await handleChatWithOnyx({ query: 'test query' }, mockOnyxApiService); expect(mockOnyxApiService.createChatSession).toHaveBeenCalledWith(15); // Default personaId }); it('should use existing chat session when chatSessionId is provided', async () => { await handleChatWithOnyx( { query: 'test query', chatSessionId: 'existing-session' }, mockOnyxApiService ); expect(mockOnyxApiService.createChatSession).not.toHaveBeenCalled(); expect(mockOnyxApiService.sendChatMessage).toHaveBeenCalledWith( 'existing-session', 'test query', [] ); }); it('should send a chat message and return formatted response', async () => { const result = await handleChatWithOnyx({ query: 'test query' }, mockOnyxApiService); expect(mockOnyxApiService.sendChatMessage).toHaveBeenCalledWith( 'test-session-id', 'test query', [] ); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Test answer'); expect(result.content[0].text).toContain('Test Document 1'); expect(result.content[0].text).toContain('doc2'); // Use type assertion to access metadata const content = result.content[0] as ChatContentResponse; expect(content.metadata?.chat_session_id).toBe('test-session-id'); }); it('should handle API errors gracefully', async () => { // Mock the API to throw an error (mockOnyxApiService.createChatSession as jest.Mock).mockImplementation(() => { return Promise.reject(new Error('API error')); }); const result = await handleChatWithOnyx({ query: 'test query' }, mockOnyxApiService); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Error chatting with Onyx'); expect(result.content[0].text).toContain('API error'); }); }); describe('handleSearchOnyx', () => { beforeEach(() => { // Set up the mock implementations (mockOnyxApiService.searchOnyx as jest.Mock).mockImplementation(() => { const results: OnyxSearchResult[] = [ { document_id: 'doc1', chunk_ind: 1, semantic_identifier: 'Test Document 1', score: 0.95, link: 'http://example.com/doc1', blurb: 'Test blurb 1', source_type: 'test' }, { document_id: 'doc2', chunk_ind: 2, semantic_identifier: 'Test Document 2', score: 0.85, link: 'http://example.com/doc2', blurb: 'Test blurb 2', source_type: 'test' } ]; return Promise.resolve(results); }); // @ts-expect-error - TypeScript errors for mock implementation mockOnyxApiService.fetchDocumentChunk = jest.fn().mockImplementation( () => Promise.resolve('Mocked chunk content') ); // @ts-expect-error - TypeScript errors for mock implementation mockOnyxApiService.fetchDocumentContent = jest.fn().mockImplementation( () => Promise.resolve('Mocked document content') ); // @ts-expect-error - TypeScript errors for mock implementation mockOnyxApiService.buildContext = jest.fn().mockReturnValue('Formatted context'); }); it('should throw an error for invalid arguments', async () => { // Test with null arguments const result1 = await handleSearchOnyx(null, mockOnyxApiService); expect(result1.isError).toBe(true); expect(result1.content[0].text).toContain('Invalid arguments'); // Test with non-object arguments const result2 = await handleSearchOnyx('not an object', mockOnyxApiService); expect(result2.isError).toBe(true); expect(result2.content[0].text).toContain('Invalid arguments'); }); it('should throw an error for missing query', async () => { const result = await handleSearchOnyx({ maxResults: 5 }, mockOnyxApiService); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Query is required'); }); it('should search with default parameters when only query is provided', async () => { await handleSearchOnyx({ query: 'test query' }, mockOnyxApiService); expect(mockOnyxApiService.searchOnyx).toHaveBeenCalledWith( 'test query', [], 1, 1 ); }); it('should search with custom parameters when provided', async () => { await handleSearchOnyx({ query: 'test query', documentSets: ['set1', 'set2'], maxResults: 10, chunksAbove: 2, chunksBelow: 3, }, mockOnyxApiService); expect(mockOnyxApiService.searchOnyx).toHaveBeenCalledWith( 'test query', ['set1', 'set2'], 2, 3 ); }); it('should fetch document chunks by default', async () => { await handleSearchOnyx({ query: 'test query' }, mockOnyxApiService); expect(mockOnyxApiService.fetchDocumentChunk).toHaveBeenCalledTimes(2); expect(mockOnyxApiService.fetchDocumentContent).not.toHaveBeenCalled(); }); it('should fetch full documents when retrieveFullDocuments is true', async () => { await handleSearchOnyx({ query: 'test query', retrieveFullDocuments: true, }, mockOnyxApiService); expect(mockOnyxApiService.fetchDocumentContent).toHaveBeenCalledTimes(2); expect(mockOnyxApiService.fetchDocumentChunk).not.toHaveBeenCalled(); }); it('should build and return context from search results', async () => { const result = await handleSearchOnyx({ query: 'test query' }, mockOnyxApiService); expect(mockOnyxApiService.buildContext).toHaveBeenCalled(); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toBe('Formatted context'); }); it('should handle API errors gracefully', async () => { // Mock the API to throw an error (mockOnyxApiService.searchOnyx as jest.Mock).mockImplementation(() => { return Promise.reject(new Error('API error')); }); const result = await handleSearchOnyx({ query: 'test query' }, mockOnyxApiService); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Error searching Onyx'); expect(result.content[0].text).toContain('API error'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lupuletic/onyx-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server