Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
research-question-tool.test.ts21.3 kB
/** * Comprehensive tests for research question tool * Tests the generateResearchQuestions function and related utilities */ import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals'; // Mock the dependencies const mockCorrelateProblemKnowledge = jest.fn() as jest.MockedFunction<any>; const mockFindRelevantAdrPatterns = jest.fn() as jest.MockedFunction<any>; const mockGenerateContextAwareQuestions = jest.fn() as jest.MockedFunction<any>; const mockCreateResearchTaskTracking = jest.fn() as jest.MockedFunction<any>; const mockGenerateArchitecturalKnowledge = jest.fn() as jest.MockedFunction<any>; const mockExecuteResearchPrompt = jest.fn() as jest.MockedFunction<any>; const mockFormatMCPResponse = jest.fn() as jest.MockedFunction<any>; // Mock research-questions utilities jest.unstable_mockModule('../src/utils/research-questions.js', () => ({ correlateProblemKnowledge: mockCorrelateProblemKnowledge, findRelevantAdrPatterns: mockFindRelevantAdrPatterns, generateContextAwareQuestions: mockGenerateContextAwareQuestions, createResearchTaskTracking: mockCreateResearchTaskTracking, })); // Mock knowledge generation utilities jest.unstable_mockModule('../src/utils/knowledge-generation.js', () => ({ generateArchitecturalKnowledge: mockGenerateArchitecturalKnowledge, })); // Mock prompt execution utilities jest.unstable_mockModule('../src/utils/prompt-execution.js', () => ({ executeResearchPrompt: mockExecuteResearchPrompt, formatMCPResponse: mockFormatMCPResponse, })); // Import the function to test after mocking const { generateResearchQuestions } = await import('../src/tools/research-question-tool.js'); describe('Research Question Tool', () => { beforeEach(() => { jest.clearAllMocks(); // Setup default mock implementations mockCorrelateProblemKnowledge.mockResolvedValue({ correlationPrompt: 'Test correlation prompt', instructions: 'Test correlation instructions', }); mockFindRelevantAdrPatterns.mockResolvedValue({ relevancePrompt: 'Test relevance prompt', instructions: 'Test relevance instructions', }); mockGenerateContextAwareQuestions.mockResolvedValue({ questionPrompt: 'Test question prompt', instructions: 'Test question instructions', }); mockCreateResearchTaskTracking.mockResolvedValue({ trackingPrompt: 'Test tracking prompt', instructions: 'Test tracking instructions', }); mockGenerateArchitecturalKnowledge.mockResolvedValue({ prompt: 'Test knowledge prompt', }); mockExecuteResearchPrompt.mockResolvedValue({ isAIGenerated: false, content: 'Test AI content', }); mockFormatMCPResponse.mockReturnValue({ content: [{ type: 'text', text: 'Formatted response', }], }); }); afterEach(() => { jest.restoreAllMocks(); }); describe('generateResearchQuestions', () => { const sampleProblems = [ { id: 'problem-1', description: 'Performance issue', category: 'performance', severity: 'high', context: 'Database queries', }, ]; const sampleKnowledgeGraph = { technologies: [{ name: 'React', description: 'UI Library' }], patterns: [{ name: 'MVC', description: 'Model-View-Controller' }], adrs: [{ id: 'adr-1', title: 'Use React', status: 'accepted' }], relationships: [{ source: 'React', target: 'UI', type: 'implements' }], }; const sampleResearchContext = { topic: 'Architecture Analysis', category: 'architecture', scope: 'project', objectives: ['Improve performance', 'Reduce complexity'], constraints: ['Budget limitations'], timeline: '3 months', }; const sampleRelevantKnowledge = { adrs: [{ id: 'adr-1', title: 'Use React' }], patterns: [{ name: 'MVC' }], gaps: [{ area: 'Testing' }], opportunities: [{ area: 'Performance' }], }; const sampleResearchQuestions = [ { id: 'q1', question: 'How can we improve performance?', type: 'investigative', priority: 'high', timeline: '2 weeks', methodology: 'analysis', }, ]; describe('correlation analysis type', () => { test('should generate correlation analysis when problems and knowledge graph provided', async () => { const result = await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, }); expect(mockCorrelateProblemKnowledge).toHaveBeenCalledWith( sampleProblems, sampleKnowledgeGraph ); expect(result.content).toHaveLength(1); expect(result.content[0].text).toContain('Problem-Knowledge Correlation Analysis'); expect(result.content[0].text).toContain('Generated Knowledge Prompting'); }); test('should throw error when problems missing for correlation', async () => { await expect( generateResearchQuestions({ analysisType: 'correlation', knowledgeGraph: sampleKnowledgeGraph, }) ).rejects.toThrow('Problems and knowledge graph are required for correlation analysis'); }); test('should throw error when knowledge graph missing for correlation', async () => { await expect( generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, }) ).rejects.toThrow('Problems and knowledge graph are required for correlation analysis'); }); test('should handle knowledge generation with enhanced mode enabled', async () => { await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, enhancedMode: true, knowledgeEnhancement: true, }); expect(mockGenerateArchitecturalKnowledge).toHaveBeenCalledWith( expect.objectContaining({ projectPath: process.cwd(), technologies: [], patterns: [], projectType: 'research-methodology', }), expect.objectContaining({ domains: ['data-architecture'], depth: 'intermediate', cacheEnabled: true, }) ); }); test('should handle knowledge generation failure gracefully', async () => { mockGenerateArchitecturalKnowledge.mockRejectedValue(new Error('Knowledge generation failed')); const result = await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, enhancedMode: true, knowledgeEnhancement: true, }); expect(result.content[0].text).toContain('Research methodology knowledge generation unavailable'); }); }); describe('relevance analysis type', () => { test('should generate relevance analysis with provided context', async () => { const result = await generateResearchQuestions({ analysisType: 'relevance', researchContext: sampleResearchContext, adrDirectory: 'docs/adrs', }); expect(mockFindRelevantAdrPatterns).toHaveBeenCalledWith( sampleResearchContext, 'docs/adrs' ); expect(result.content).toHaveLength(1); expect(result.content[0].text).toContain('Relevant ADR and Pattern Discovery'); }); test('should use intelligent defaults when no research context provided', async () => { const result = await generateResearchQuestions({ analysisType: 'relevance', }); expect(mockFindRelevantAdrPatterns).toHaveBeenCalledWith( expect.objectContaining({ topic: 'Architectural Relevance Analysis', category: 'architecture', scope: 'project', objectives: ['Identify relevant architectural patterns and decisions'], constraints: [], timeline: 'Not specified', }), 'docs/adrs' ); expect(result.content[0].text).toContain('Relevant ADR and Pattern Discovery'); }); }); describe('questions analysis type', () => { test('should generate context-aware questions when relevant knowledge provided', async () => { mockExecuteResearchPrompt.mockResolvedValue({ isAIGenerated: true, content: 'Generated research questions content', }); await generateResearchQuestions({ analysisType: 'questions', researchContext: sampleResearchContext, relevantKnowledge: sampleRelevantKnowledge, projectPath: '/test/path', }); expect(mockGenerateContextAwareQuestions).toHaveBeenCalledWith( sampleResearchContext, sampleRelevantKnowledge, '/test/path' ); expect(mockExecuteResearchPrompt).toHaveBeenCalled(); expect(mockFormatMCPResponse).toHaveBeenCalled(); }); test('should throw error when relevant knowledge missing for questions', async () => { await expect( generateResearchQuestions({ analysisType: 'questions', researchContext: sampleResearchContext, }) ).rejects.toThrow('Relevant knowledge is required for question generation'); }); test('should use intelligent defaults for research context in questions', async () => { await generateResearchQuestions({ analysisType: 'questions', relevantKnowledge: sampleRelevantKnowledge, }); expect(mockGenerateContextAwareQuestions).toHaveBeenCalledWith( expect.objectContaining({ topic: 'Architectural Question Generation', category: 'architecture', scope: 'project', objectives: ['Generate relevant research questions for architectural analysis'], constraints: [], timeline: 'Not specified', }), sampleRelevantKnowledge, process.cwd() ); }); test('should handle AI generation failure and fallback to prompt-only mode', async () => { mockExecuteResearchPrompt.mockResolvedValue({ isAIGenerated: false, content: 'Prompt only content', }); const result = await generateResearchQuestions({ analysisType: 'questions', researchContext: sampleResearchContext, relevantKnowledge: sampleRelevantKnowledge, }); expect(result.content[0].text).toContain('Context-Aware Research Question Generation with File Persistence'); expect(result.content[0].text).toContain('AI Question Generation Prompt'); }); }); describe('tracking analysis type', () => { test('should create research task tracking when questions provided', async () => { const result = await generateResearchQuestions({ analysisType: 'tracking', researchQuestions: sampleResearchQuestions, }); expect(mockCreateResearchTaskTracking).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ id: 'q1', question: 'How can we improve performance?', complexity: 'medium', expectedOutcome: 'Research findings and recommendations', successCriteria: ['Clear findings documented', 'Recommendations provided'], relatedKnowledge: [], resources: [], risks: [], dependencies: [], }), ]), undefined ); expect(result.content[0].text).toContain('Research Task Tracking System'); }); test('should throw error when research questions missing for tracking', async () => { await expect( generateResearchQuestions({ analysisType: 'tracking', }) ).rejects.toThrow('Research questions are required for task tracking'); }); test('should handle current progress when provided', async () => { const currentProgress = [ { questionId: 'q1', status: 'in-progress', progress: 50, findings: ['Initial finding'], blockers: ['Resource constraint'], }, ]; await generateResearchQuestions({ analysisType: 'tracking', researchQuestions: sampleResearchQuestions, currentProgress, }); expect(mockCreateResearchTaskTracking).toHaveBeenCalledWith( expect.any(Array), currentProgress ); }); }); describe('comprehensive analysis type', () => { test('should generate comprehensive analysis with provided context', async () => { const result = await generateResearchQuestions({ analysisType: 'comprehensive', researchContext: sampleResearchContext, projectPath: '/test/path', adrDirectory: 'docs/adrs', }); expect(mockFindRelevantAdrPatterns).toHaveBeenCalledWith( sampleResearchContext, 'docs/adrs' ); expect(result.content[0].text).toContain('Comprehensive Research Question Generation'); expect(result.content[0].text).toContain('Research Context'); expect(result.content[0].text).toContain(sampleResearchContext.topic); }); test('should use intelligent defaults for comprehensive analysis', async () => { const result = await generateResearchQuestions({ analysisType: 'comprehensive', }); expect(mockFindRelevantAdrPatterns).toHaveBeenCalledWith( expect.objectContaining({ topic: 'General Architectural Research', category: 'architecture', scope: 'project', objectives: [ 'Identify architectural patterns and best practices', 'Analyze current system design decisions', 'Discover improvement opportunities', 'Evaluate technology choices and alternatives', ], constraints: ['Time and resource limitations'], timeline: 'Not specified', }), 'docs/adrs' ); expect(result.content[0].text).toContain('Using intelligent defaults'); }); test('should include all workflow steps in comprehensive analysis', async () => { const result = await generateResearchQuestions({ analysisType: 'comprehensive', researchContext: sampleResearchContext, }); const content = result.content[0].text; expect(content).toContain('1. **Relevance Analysis**'); expect(content).toContain('2. **Research Question Generation**'); expect(content).toContain('3. **Task Tracking System**'); expect(content).toContain('4. **Problem Correlation**'); expect(content).toContain('File Persistence Requirements'); }); }); describe('error handling', () => { test('should throw error for unknown analysis type', async () => { await expect( generateResearchQuestions({ analysisType: 'unknown' as any, }) ).rejects.toThrow('Unknown analysis type: unknown'); }); test('should wrap and rethrow correlation errors', async () => { mockCorrelateProblemKnowledge.mockRejectedValue(new Error('Correlation failed')); await expect( generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, }) ).rejects.toThrow('Failed to generate research questions: Correlation failed'); }); test('should wrap and rethrow relevance errors', async () => { mockFindRelevantAdrPatterns.mockRejectedValue(new Error('Relevance analysis failed')); await expect( generateResearchQuestions({ analysisType: 'relevance', researchContext: sampleResearchContext, }) ).rejects.toThrow('Failed to generate research questions: Relevance analysis failed'); }); test('should wrap and rethrow question generation errors', async () => { mockGenerateContextAwareQuestions.mockRejectedValue(new Error('Question generation failed')); await expect( generateResearchQuestions({ analysisType: 'questions', researchContext: sampleResearchContext, relevantKnowledge: sampleRelevantKnowledge, }) ).rejects.toThrow('Failed to generate research questions: Question generation failed'); }); test('should wrap and rethrow tracking errors', async () => { mockCreateResearchTaskTracking.mockRejectedValue(new Error('Tracking failed')); await expect( generateResearchQuestions({ analysisType: 'tracking', researchQuestions: sampleResearchQuestions, }) ).rejects.toThrow('Failed to generate research questions: Tracking failed'); }); }); describe('default parameter handling', () => { test('should use default values for optional parameters', async () => { const result = await generateResearchQuestions({}); expect(result.content[0].text).toContain('Comprehensive Research Question Generation'); // Should default to comprehensive analysis type }); test('should apply default project path when not provided', async () => { await generateResearchQuestions({ analysisType: 'questions', relevantKnowledge: sampleRelevantKnowledge, }); expect(mockGenerateContextAwareQuestions).toHaveBeenCalledWith( expect.any(Object), sampleRelevantKnowledge, process.cwd() ); }); test('should apply default ADR directory when not provided', async () => { await generateResearchQuestions({ analysisType: 'relevance', researchContext: sampleResearchContext, }); expect(mockFindRelevantAdrPatterns).toHaveBeenCalledWith( sampleResearchContext, 'docs/adrs' ); }); test('should enable enhanced mode and knowledge enhancement by default', async () => { await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, }); expect(mockGenerateArchitecturalKnowledge).toHaveBeenCalled(); }); }); describe('response format validation', () => { test('should return proper MCP response format for correlation', async () => { const result = await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, }); 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'); }); test('should return proper MCP response format for AI-generated questions', async () => { mockExecuteResearchPrompt.mockResolvedValue({ isAIGenerated: true, content: 'AI generated content', }); mockFormatMCPResponse.mockReturnValue({ content: [{ type: 'text', text: 'Formatted AI response', }], }); await generateResearchQuestions({ analysisType: 'questions', researchContext: sampleResearchContext, relevantKnowledge: sampleRelevantKnowledge, }); expect(mockFormatMCPResponse).toHaveBeenCalledWith( expect.objectContaining({ isAIGenerated: true, content: expect.stringContaining('Context-Aware Research Question Generation Results'), }) ); }); }); describe('configuration options', () => { test('should handle disabled enhanced mode', async () => { await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, enhancedMode: false, knowledgeEnhancement: false, }); expect(mockGenerateArchitecturalKnowledge).not.toHaveBeenCalled(); }); test('should handle disabled knowledge enhancement only', async () => { await generateResearchQuestions({ analysisType: 'correlation', problems: sampleProblems, knowledgeGraph: sampleKnowledgeGraph, enhancedMode: true, knowledgeEnhancement: false, }); expect(mockGenerateArchitecturalKnowledge).not.toHaveBeenCalled(); }); }); }); });

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