Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
perform-research-tool.test.ts.backup16.2 kB
/** * Tests for Perform Research Tool */ import { jest } from '@jest/globals'; import { performResearch } from '../../src/tools/perform-research-tool.js'; import { McpAdrError } from '../../src/types/index.js'; import { ResearchOrchestrator } from '../../src/utils/research-orchestrator.js'; // Mock dependencies const mockResearchOrchestrator = jest.fn(); jest.mock('../../src/utils/research-orchestrator.js', () => ({ ResearchOrchestrator: mockResearchOrchestrator, })); describe('Perform Research Tool', () => { let mockOrchestratorInstance: any; beforeEach(() => { jest.clearAllMocks(); // Create a default mock orchestrator instance mockOrchestratorInstance = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0.5, needsWebSearch: false, answer: 'Test answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; // Set up the mock to return this instance mockResearchOrchestrator.mockImplementation(() => mockOrchestratorInstance); }); describe('Parameter Validation', () => { it('should require question parameter', async () => { await expect( performResearch({ question: '', projectPath: process.cwd(), }) ).rejects.toThrow(McpAdrError); }); it('should reject undefined question', async () => { await expect( performResearch({ question: undefined as any, projectPath: process.cwd(), }) ).rejects.toThrow('Research question is required'); }); it('should reject whitespace-only question', async () => { await expect( performResearch({ question: ' ', projectPath: process.cwd(), }) ).rejects.toThrow('Research question is required'); }); it('should accept valid question with default parameters', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'What is Docker?', sources: [], confidence: 0.5, needsWebSearch: true, answer: 'No relevant information found.', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0, }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator as any); const result = await performResearch({ question: 'What is Docker?', }); expect(result.content).toBeDefined(); expect(result.content[0].type).toBe('text'); }); }); describe('Confidence Threshold Handling', () => { it('should use default confidence threshold of 0.6', async () => { await performResearch({ question: 'Test question' }); expect(mockOrchestratorInstance.setConfidenceThreshold).toHaveBeenCalledWith(0.6); }); it('should use custom confidence threshold when provided', async () => { await performResearch({ question: 'Test question', confidenceThreshold: 0.8, }); expect(mockOrchestratorInstance.setConfidenceThreshold).toHaveBeenCalledWith(0.8); }); }); describe('Response Structure', () => { it('should return proper MCP response structure', async () => { mockOrchestratorInstance.answerResearchQuestion.mockResolvedValue({ question: 'Test question', sources: [ { type: 'project_files', data: { files: ['test.ts'], relevance: { 'test.ts': 0.9 } }, confidence: 0.9, timestamp: new Date().toISOString(), }, ], confidence: 0.85, needsWebSearch: false, answer: 'Found test files.', metadata: { duration: 150, sourcesQueried: ['project_files'], filesAnalyzed: 1 }, }); const result = await performResearch({ question: 'Test question' }); expect(result).toHaveProperty('content'); expect(Array.isArray(result.content)).toBe(true); expect(result.content[0]).toHaveProperty('type', 'text'); expect(result.content[0]).toHaveProperty('text'); expect(typeof result.content[0].text).toBe('string'); }); it('should include confidence score in response text', async () => { mockOrchestratorInstance.answerResearchQuestion.mockResolvedValue({ question: 'Test', sources: [], confidence: 0.75, needsWebSearch: false, answer: 'Answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('75.0%'); }); it('should include metadata in response', async () => { mockOrchestratorInstance.answerResearchQuestion.mockResolvedValue({ question: 'Test', sources: [], confidence: 0.8, needsWebSearch: false, answer: 'Answer', metadata: { duration: 250, sourcesQueried: ['project_files'], filesAnalyzed: 5 }, }); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('250ms'); expect(result.content[0].text).toContain('project_files'); expect(result.content[0].text).toContain('Files Analyzed: 5'); }); }); describe('Source Integration', () => { it('should format project files source', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [ { type: 'project_files', data: { files: ['src/index.ts', 'src/utils.ts'], relevance: { 'src/index.ts': 0.9, 'src/utils.ts': 0.8 }, }, confidence: 0.85, timestamp: new Date().toISOString(), }, ], confidence: 0.85, needsWebSearch: false, answer: 'Found files', metadata: { duration: 100, sourcesQueried: ['project_files'], filesAnalyzed: 2 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('📁 Project Files'); expect(result.content[0].text).toContain('src/index.ts'); expect(result.content[0].text).toContain('relevance: 90%'); }); it('should format knowledge graph source', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [ { type: 'knowledge_graph', data: { nodes: [{ id: 'adr-1' }, { id: 'adr-2' }], relationships: [] }, confidence: 0.85, timestamp: new Date().toISOString(), }, ], confidence: 0.85, needsWebSearch: false, answer: 'Found ADRs', metadata: { duration: 100, sourcesQueried: ['knowledge_graph'], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('🧠 Knowledge Graph'); expect(result.content[0].text).toContain('Related ADRs: 2'); }); it('should format environment source', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [ { type: 'environment', data: { capabilities: ['docker', 'kubernetes'], data: [ { capability: 'docker', found: true }, { capability: 'kubernetes', found: false }, ], }, confidence: 0.95, timestamp: new Date().toISOString(), }, ], confidence: 0.95, needsWebSearch: false, answer: 'Found environment', metadata: { duration: 100, sourcesQueried: ['environment'], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('🔧 Environment Resources'); expect(result.content[0].text).toContain('docker, kubernetes'); expect(result.content[0].text).toContain('✅ Data found'); }); }); describe('Web Search Recommendation', () => { it('should recommend web search when confidence is low', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'What is quantum computing?', sources: [], confidence: 0.3, needsWebSearch: true, answer: 'Low confidence answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'What is quantum computing?', performWebSearch: true, }); expect(result.content[0].text).toContain('🌐 Web Search Recommended'); expect(result.content[0].text).toContain('Suggested search queries'); }); it('should not recommend web search when confidence is high', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [{ type: 'project_files', data: {}, confidence: 0.9, timestamp: '' }], confidence: 0.9, needsWebSearch: false, answer: 'High confidence answer', metadata: { duration: 100, sourcesQueried: ['project_files'], filesAnalyzed: 1 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).not.toContain('🌐 Web Search Recommended'); }); it('should respect performWebSearch flag', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0.3, needsWebSearch: true, answer: 'Low confidence', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test', performWebSearch: false, }); expect(result.content[0].text).not.toContain('🌐 Web Search Recommended'); }); }); describe('Error Handling', () => { it('should wrap orchestrator errors in McpAdrError', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockRejectedValue(new Error('Orchestrator failed')), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); await expect(performResearch({ question: 'Test' })).rejects.toThrow(McpAdrError); await expect(performResearch({ question: 'Test' })).rejects.toThrow( 'Failed to perform research: Orchestrator failed' ); }); it('should handle non-Error exceptions', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockRejectedValue('String error'), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); await expect(performResearch({ question: 'Test' })).rejects.toThrow(McpAdrError); await expect(performResearch({ question: 'Test' })).rejects.toThrow( 'Failed to perform research: String error' ); }); }); describe('Next Steps Recommendations', () => { it('should provide high confidence next steps', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0.85, needsWebSearch: false, answer: 'Answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('✅ High confidence answer'); }); it('should provide moderate confidence next steps', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0.65, needsWebSearch: false, answer: 'Answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('⚠️ Moderate confidence'); }); it('should provide low confidence next steps', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0.3, needsWebSearch: true, answer: 'Answer', metadata: { duration: 100, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('❌ Low confidence'); }); }); describe('Edge Cases', () => { it('should handle empty sources array', async () => { const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [], confidence: 0, needsWebSearch: true, answer: 'No information found', metadata: { duration: 50, sourcesQueried: [], filesAnalyzed: 0 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('*No relevant sources found*'); }); it('should handle many files in project source', async () => { const files = Array.from({ length: 15 }, (_, i) => `file${i}.ts`); const mockOrchestrator = { setConfidenceThreshold: jest.fn(), answerResearchQuestion: jest.fn().mockResolvedValue({ question: 'Test', sources: [ { type: 'project_files', data: { files, relevance: {} }, confidence: 0.8, timestamp: new Date().toISOString(), }, ], confidence: 0.8, needsWebSearch: false, answer: 'Found many files', metadata: { duration: 200, sourcesQueried: ['project_files'], filesAnalyzed: 15 }, }), }; mockResearchOrchestrator.mockImplementation(() => mockOrchestrator); const result = await performResearch({ question: 'Test' }); expect(result.content[0].text).toContain('... and 5 more files'); }); }); });

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/tosin2013/mcp-adr-analysis-server'

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