Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
research-questions.test.ts22.4 kB
import { jest } from '@jest/globals'; import { correlateProblemKnowledge, findRelevantAdrPatterns, generateContextAwareQuestions, createResearchTaskTracking, ResearchProblem, KnowledgeGraph, ResearchContext, ResearchQuestion, } from '../../src/utils/research-questions.js'; // Mock dependencies jest.mock('../../src/prompts/research-question-prompts.js', () => ({ generateProblemKnowledgeCorrelationPrompt: jest.fn().mockReturnValue('correlation prompt'), generateContextAwareResearchQuestionsPrompt: jest .fn() .mockReturnValue('question generation prompt'), generateResearchTaskTrackingPrompt: jest.fn().mockReturnValue('task tracking prompt'), })); // Mock the adr-discovery module with unstable_mockModule for ESM compatibility const mockDiscoverAdrsInDirectory = jest.fn().mockResolvedValue({ totalAdrs: 2, adrs: [ { title: 'Test ADR 1', filename: 'adr-001.md', status: 'accepted', path: '/test/adr-001.md', content: 'Test content 1', }, { title: 'Test ADR 2', filename: 'adr-002.md', status: 'proposed', path: '/test/adr-002.md', content: 'Test content 2', }, ], }); jest.unstable_mockModule('../../src/utils/adr-discovery.js', () => ({ discoverAdrsInDirectory: mockDiscoverAdrsInDirectory, })); jest.mock('../../src/utils/actual-file-operations.js', () => ({ scanProjectStructure: jest.fn().mockResolvedValue({ files: ['package.json', 'src/index.ts'], directories: ['src', 'tests'], structure: { type: 'project' }, }), })); describe('Research Questions Utilities', () => { beforeEach(() => { jest.clearAllMocks(); mockDiscoverAdrsInDirectory.mockClear(); }); describe('correlateProblemKnowledge', () => { const mockProblems: ResearchProblem[] = [ { id: 'prob-1', description: 'A test research problem', category: 'architecture', severity: 'medium', context: 'software development', }, { id: 'prob-2', description: 'Performance optimization issue', category: 'performance', severity: 'high', context: 'web application', }, ]; const mockKnowledgeGraph: KnowledgeGraph = { technologies: [ { name: 'React', description: 'Frontend framework', category: 'frontend' }, { name: 'Node.js', description: 'Backend runtime', category: 'backend' }, ], patterns: [ { name: 'MVC', description: 'Model-View-Controller', category: 'architectural' }, { name: 'Observer', description: 'Observer pattern', category: 'behavioral' }, ], adrs: [ { id: 'adr-1', title: 'Use React for frontend', status: 'accepted', content: 'React ADR content', }, { id: 'adr-2', title: 'Database selection', status: 'proposed', content: 'Database ADR content', }, ], relationships: [ { source: 'React', target: 'frontend', type: 'implements' }, { source: 'Node.js', target: 'backend', type: 'implements' }, ], }; it('should successfully correlate problems with knowledge graph', async () => { const result = await correlateProblemKnowledge(mockProblems, mockKnowledgeGraph); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.correlationPrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should include problem and knowledge graph statistics in instructions', async () => { const result = await correlateProblemKnowledge(mockProblems, mockKnowledgeGraph); expect(result.instructions).toContain('2 problems identified'); expect(result.instructions).toContain('2 technologies in knowledge graph'); expect(result.instructions).toContain('2 patterns in knowledge graph'); expect(result.instructions).toContain('2 ADRs in knowledge graph'); expect(result.instructions).toContain('2 relationships mapped'); }); it('should handle empty problems array', async () => { const result = await correlateProblemKnowledge([], mockKnowledgeGraph); expect(result.instructions).toContain('0 problems identified'); expect(result.correlationPrompt).toBeDefined(); }); it('should handle empty knowledge graph', async () => { const emptyGraph: KnowledgeGraph = { technologies: [], patterns: [], adrs: [], relationships: [], }; const result = await correlateProblemKnowledge(mockProblems, emptyGraph); expect(result.instructions).toContain('0 technologies in knowledge graph'); expect(result.instructions).toContain('0 patterns in knowledge graph'); expect(result.instructions).toContain('0 ADRs in knowledge graph'); expect(result.instructions).toContain('0 relationships mapped'); }); it('should include usage instructions', async () => { const result = await correlateProblemKnowledge(mockProblems, mockKnowledgeGraph); expect(result.instructions).toContain('Submit the correlation prompt'); expect(result.instructions).toContain('Parse the JSON response'); expect(result.instructions).toContain('Usage Example'); }); it('should include expected response format', async () => { const result = await correlateProblemKnowledge(mockProblems, mockKnowledgeGraph); expect(result.instructions).toContain('correlationAnalysis'); expect(result.instructions).toContain('problemCorrelations'); expect(result.instructions).toContain('knowledgeGaps'); expect(result.instructions).toContain('researchOpportunities'); }); it('should handle import failure gracefully', async () => { // Mock the import to fail const originalImport = (global as any).__original_import || (global as any).import; (global as any).import = jest.fn().mockRejectedValue(new Error('Import failed')); const result = await correlateProblemKnowledge(mockProblems, mockKnowledgeGraph); // Should handle import failure gracefully and still return a result expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.correlationPrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); // Restore original import (global as any).import = originalImport; }); }); describe('findRelevantAdrPatterns', () => { const mockResearchContext: ResearchContext = { topic: 'Database Architecture', category: 'architecture', scope: 'backend', objectives: ['Select appropriate database', 'Ensure scalability'], constraints: ['Budget limitations', 'Time constraints'], timeline: 'Q1 2025', }; it('should successfully find relevant ADR patterns', async () => { const result = await findRelevantAdrPatterns(mockResearchContext); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.relevancePrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should include research context in analysis', async () => { const result = await findRelevantAdrPatterns(mockResearchContext); expect(result.relevancePrompt).toContain('Database Architecture'); expect(result.relevancePrompt).toContain('architecture'); expect(result.relevancePrompt).toContain('backend'); expect(result.relevancePrompt).toContain('Select appropriate database'); }); it('should include discovered ADRs in prompt', async () => { const result = await findRelevantAdrPatterns(mockResearchContext); // Since actual file system is used and no ADRs exist, expect empty result expect(result.relevancePrompt).toContain('Discovered ADRs'); expect(result.relevancePrompt).toContain('Database Architecture'); expect(result.relevancePrompt).toContain('Pattern Relevance Assessment'); }); it('should handle custom ADR directory', async () => { const result = await findRelevantAdrPatterns(mockResearchContext, 'custom/adrs'); expect(result.instructions).toContain('custom/adrs'); }); it('should include architectural patterns', async () => { const result = await findRelevantAdrPatterns(mockResearchContext); expect(result.relevancePrompt).toContain('Architectural Patterns'); }); it('should provide analysis instructions', async () => { const result = await findRelevantAdrPatterns(mockResearchContext); expect(result.instructions).toContain('Submit the relevance prompt'); expect(result.instructions).toContain('ADRs Found**: 0 files'); expect(result.instructions).toContain('relevanceAnalysis'); }); }); describe('generateContextAwareQuestions', () => { const _mockResearchContext: ResearchContext = { topic: 'Microservices Migration', category: 'architecture', scope: 'system-wide', objectives: ['Improve scalability', 'Reduce coupling'], constraints: ['Limited budget'], timeline: '6 months', }; const mockKnowledge: KnowledgeGraph = { technologies: [{ name: 'Node.js', description: 'JavaScript runtime', category: 'backend' }], patterns: [ { name: 'Microservices', description: 'Distributed architecture', category: 'architecture', }, ], adrs: [{ id: 'adr-001', title: 'Use Microservices', status: 'accepted' }], relationships: [{ source: 'Node.js', target: 'Microservices', type: 'implements' }], }; const mockProblems: ResearchProblem[] = [ { id: 'prob-1', description: 'A test research problem', category: 'architecture', severity: 'medium', context: 'software development', }, ]; it('should correlate problems with knowledge graph', async () => { const result = await correlateProblemKnowledge(mockProblems, mockKnowledge); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.correlationPrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should handle empty knowledge graph', async () => { const emptyKnowledge: KnowledgeGraph = { technologies: [], patterns: [], adrs: [], relationships: [], }; const result = await correlateProblemKnowledge(mockProblems, emptyKnowledge); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle empty problems array', async () => { const result = await correlateProblemKnowledge([], mockKnowledge); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle multiple problems', async () => { const multipleProblems: ResearchProblem[] = [ ...mockProblems, { id: 'prob-2', description: 'Another research problem', category: 'performance', severity: 'high', context: 'optimization', }, ]; const result = await correlateProblemKnowledge(multipleProblems, mockKnowledge); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); }); }); describe('findRelevantAdrPatterns', () => { const mockContext: ResearchContext = { topic: 'Performance Optimization', category: 'architecture', scope: 'system-wide', objectives: ['improve-performance', 'reduce-complexity'], constraints: ['scalability', 'performance'], timeline: '3 months', }; it('should find relevant ADR patterns', async () => { const result = await findRelevantAdrPatterns(mockContext); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.relevancePrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should handle custom ADR directory', async () => { const result = await findRelevantAdrPatterns(mockContext, 'custom/adrs'); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle minimal context', async () => { const minimalContext: ResearchContext = { topic: 'Test Topic', category: 'test', scope: 'limited', objectives: ['test-objective'], }; const result = await findRelevantAdrPatterns(minimalContext); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle context with all optional fields', async () => { const fullContext: ResearchContext = { topic: 'Full Context Test', category: 'comprehensive', scope: 'enterprise', objectives: ['obj1', 'obj2', 'obj3'], constraints: ['constraint1', 'constraint2'], timeline: '6 months', }; const result = await findRelevantAdrPatterns(fullContext); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); }); }); describe('generateContextAwareQuestions', () => { const mockContext: ResearchContext = { topic: 'Performance Optimization', category: 'architecture', scope: 'system-wide', objectives: ['improve-performance', 'reduce-complexity'], constraints: ['scalability', 'performance'], timeline: '3 months', }; const mockRelevantKnowledge = { adrs: [{ id: 'adr-001', title: 'Use Microservices', status: 'accepted' }], patterns: [{ name: 'Event Sourcing', category: 'data' }], gaps: [{ area: 'monitoring', severity: 'medium' }], opportunities: [{ type: 'optimization', priority: 'high' }], }; it('should generate context-aware questions', async () => { const result = await generateContextAwareQuestions(mockContext, mockRelevantKnowledge); expect(result).toHaveProperty('questionPrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.questionPrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should handle custom project path', async () => { const result = await generateContextAwareQuestions( mockContext, mockRelevantKnowledge, '/custom/path' ); expect(result).toHaveProperty('questionPrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle empty relevant knowledge', async () => { const emptyKnowledge = { adrs: [], patterns: [], gaps: [], opportunities: [], }; const result = await generateContextAwareQuestions(mockContext, emptyKnowledge); expect(result).toHaveProperty('questionPrompt'); expect(result).toHaveProperty('instructions'); }); }); describe('createResearchTaskTracking', () => { const mockCurrentProgress = [ { questionId: 'q-1', status: 'in_progress', progress: 50, findings: ['finding 1', 'finding 2'], blockers: ['blocker 1'], }, { questionId: 'q-2', status: 'not_started', progress: 0, findings: [], blockers: [], }, ]; it('should create research task tracking', async () => { const result = await createResearchTaskTracking(mockCurrentProgress); expect(result).toHaveProperty('trackingPrompt'); expect(result).toHaveProperty('instructions'); expect(typeof result.trackingPrompt).toBe('string'); expect(typeof result.instructions).toBe('string'); }); it('should handle empty progress array', async () => { const result = await createResearchTaskTracking([]); expect(result).toHaveProperty('trackingPrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle progress with multiple statuses', async () => { const mixedProgress = [ ...mockCurrentProgress, { questionId: 'q-3', status: 'completed', progress: 100, findings: ['final finding'], blockers: [], }, { questionId: 'q-4', status: 'blocked', progress: 25, findings: ['partial finding'], blockers: ['critical blocker'], }, ]; const result = await createResearchTaskTracking(mixedProgress); expect(result).toHaveProperty('trackingPrompt'); expect(result).toHaveProperty('instructions'); }); }); describe('Interface Validation', () => { it('should validate ResearchProblem interface', () => { const problem: ResearchProblem = { id: 'test-id', description: 'Test Description', category: 'test-category', severity: 'medium', context: 'test-context', }; expect(problem.id).toBe('test-id'); expect(problem.description).toBe('Test Description'); expect(problem.category).toBe('test-category'); expect(problem.severity).toBe('medium'); expect(problem.context).toBe('test-context'); }); it('should validate KnowledgeGraph interface', () => { const knowledge: KnowledgeGraph = { technologies: [{ name: 'Test Tech', description: 'Test', category: 'test' }], patterns: [{ name: 'Test Pattern', description: 'Test', category: 'test' }], adrs: [{ id: 'adr-1', title: 'Test ADR', status: 'accepted' }], relationships: [{ source: 'src', target: 'tgt', type: 'test' }], }; expect(Array.isArray(knowledge.technologies)).toBe(true); expect(Array.isArray(knowledge.patterns)).toBe(true); expect(Array.isArray(knowledge.adrs)).toBe(true); expect(Array.isArray(knowledge.relationships)).toBe(true); }); it('should validate ResearchContext interface', () => { const context: ResearchContext = { topic: 'Test Topic', category: 'test-category', scope: 'test-scope', objectives: ['obj1', 'obj2'], constraints: ['constraint1'], timeline: '3 months', }; expect(typeof context.topic).toBe('string'); expect(typeof context.category).toBe('string'); expect(typeof context.scope).toBe('string'); expect(Array.isArray(context.objectives)).toBe(true); expect(Array.isArray(context.constraints)).toBe(true); expect(typeof context.timeline).toBe('string'); }); it('should validate ResearchQuestion interface', () => { const question: ResearchQuestion = { id: 'q-test', question: 'Test question?', type: 'exploratory', priority: 'medium', complexity: 'low', timeline: '2 weeks', methodology: 'analysis', expectedOutcome: 'Test outcome', successCriteria: ['criteria1'], relatedKnowledge: ['knowledge1'], resources: ['resource1'], risks: ['risk1'], dependencies: ['dep1'], }; expect(typeof question.id).toBe('string'); expect(typeof question.question).toBe('string'); expect(typeof question.type).toBe('string'); expect(typeof question.priority).toBe('string'); expect(Array.isArray(question.successCriteria)).toBe(true); expect(Array.isArray(question.dependencies)).toBe(true); }); }); describe('Error Handling', () => { it('should handle null inputs gracefully', async () => { await expect(correlateProblemKnowledge(null as any, null as any)).rejects.toThrow(); }); it('should handle undefined inputs gracefully', async () => { await expect(findRelevantAdrPatterns(undefined as any)).rejects.toThrow(); }); it('should handle invalid context gracefully', async () => { const invalidContext = {} as ResearchContext; await expect(findRelevantAdrPatterns(invalidContext)).rejects.toThrow(); }); }); describe('Edge Cases', () => { it('should handle very large knowledge graphs', async () => { const largeKnowledge: KnowledgeGraph = { technologies: Array.from({ length: 100 }, (_, i) => ({ name: `Tech ${i}`, description: `Description ${i}`, category: `Category ${i % 5}`, })), patterns: Array.from({ length: 50 }, (_, i) => ({ name: `Pattern ${i}`, description: `Description ${i}`, category: `Category ${i % 3}`, })), adrs: Array.from({ length: 25 }, (_, i) => ({ id: `adr-${i}`, title: `ADR ${i}`, status: i % 2 === 0 ? 'accepted' : 'proposed', })), relationships: Array.from({ length: 75 }, (_, i) => ({ source: `Tech ${i % 10}`, target: `Pattern ${i % 5}`, type: 'implements', })), }; const problems: ResearchProblem[] = [ { id: 'large-test', description: 'Testing with large knowledge graph', category: 'performance', severity: 'high', context: 'scalability testing', }, ]; const result = await correlateProblemKnowledge(problems, largeKnowledge); expect(result).toHaveProperty('correlationPrompt'); expect(result).toHaveProperty('instructions'); }); it('should handle special characters in inputs', async () => { const specialContext: ResearchContext = { topic: 'Special Characters: @#$%^&*()', category: 'tëst-cätëgöry', scope: 'ünïcödë-scöpë', objectives: ['öbjëctïvë-1', 'öbjëctïvë-2'], constraints: ['cönstraïnt-1'], timeline: '3 mönths', }; const result = await findRelevantAdrPatterns(specialContext); expect(result).toHaveProperty('relevancePrompt'); expect(result).toHaveProperty('instructions'); }); }); });

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