UNPKG

task-master-neo-sdlc

Version:

Enhanced task management system with Neo SDLC agents and MCP tools for comprehensive, AI-driven software development lifecycle management.

302 lines (255 loc) 12.8 kB
import { PerformanceAnalystAgent } from '../performance-analyst'; import { KnowledgeGraph } from '../../knowledge-graph'; // Adjust path as needed import { AgentWorkflowSystem } from '../../agent-workflow'; // Adjust path as needed import { MonitoringSystem } from '../../monitoring'; // Adjust path as needed // Mock implementations for dependencies const mockKnowledgeGraph = { addNode: jest.fn(), findNodes: jest.fn(), updateContext: jest.fn(), findEdges: jest.fn() }; const mockWorkflow = { startTask: jest.fn() // Mock if used }; const mockMonitoringSystem = { getMetrics: jest.fn() // Mock if used }; describe('PerformanceAnalystAgent', () => { let agent; beforeEach(() => { jest.clearAllMocks(); agent = new PerformanceAnalystAgent(mockKnowledgeGraph, mockWorkflow, mockMonitoringSystem); // Mock console.log to prevent test output clutter jest.spyOn(console, 'log').mockImplementation(() => {}); }); afterEach(() => { // Restore console.log console.log.mockRestore(); }); it('should analyze performance data and store report in KG', async () => { const scope = { timeRange: '1h', component: 'api-gateway' }; mockKnowledgeGraph.addNode.mockResolvedValue(undefined); // Mock monitoring system if agent actually fetches data // mockMonitoringSystem.getMetrics.mockResolvedValue({...}); const report = await agent.analyzePerformanceData(scope); expect(report).toBeDefined(); expect(report.id).toMatch(/^perfAnalysis_/); expect(report.scope).toEqual(scope); expect(report.metrics).toBeDefined(); expect(report.metrics.avgResponseTime).toBeGreaterThan(0); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({ id: `performanceAnalysis:${report.id}`, type: 'performance_analysis', data: report }); }); it('should generate optimization recommendations based on analysis', async () => { const analysisId = 'perfAnalysis_123'; const mockAnalysisNode = { id: `performanceAnalysis:${analysisId}`, type: 'performance_analysis', data: { id: analysisId, metrics: { avgResponseTime: 150, errorRate: 0.01, resourceUtilization: { cpu: 50 } }, recommendations: [] } }; mockKnowledgeGraph.findNodes.mockResolvedValue([mockAnalysisNode]); mockKnowledgeGraph.updateContext.mockResolvedValue(undefined); const recommendations = await agent.generateOptimizationRecommendations(analysisId); expect(recommendations).toBeDefined(); expect(recommendations.length).toBeGreaterThan(0); expect(recommendations).toContain('Optimize database queries affecting high response times.'); expect(mockKnowledgeGraph.findNodes).toHaveBeenCalledWith({ type: 'performance_analysis', id: `performanceAnalysis:${analysisId}` }); expect(mockAnalysisNode.data.recommendations).toEqual(recommendations); expect(mockKnowledgeGraph.updateContext).toHaveBeenCalledWith({ id: mockAnalysisNode.id, data: mockAnalysisNode.data }); }); it('should throw error if analysis not found when generating recommendations', async () => { const analysisId = 'nonexistent_analysis'; mockKnowledgeGraph.findNodes.mockResolvedValue([]); await expect(agent.generateOptimizationRecommendations(analysisId)).rejects.toThrow( `Performance analysis ${analysisId} not found` ); expect(mockKnowledgeGraph.findNodes).toHaveBeenCalledWith({ type: 'performance_analysis', id: `performanceAnalysis:${analysisId}` }); }); it('should create a performance benchmark task and store in KG', async () => { const name = 'API Load Test'; const configuration = { endpoint: '/users', concurrentUsers: 100 }; mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const benchmark = await agent.createBenchmark(name, configuration); expect(benchmark).toBeDefined(); expect(benchmark.id).toMatch(/^benchmark_/); expect(benchmark.name).toBe(name); expect(benchmark.configuration).toEqual(configuration); expect(benchmark.status).toBe('pending'); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({ id: `benchmark:${benchmark.id}`, type: 'performance_benchmark', data: benchmark }); // Optionally check if workflow task was started // expect(mockWorkflow.startTask).toHaveBeenCalledWith('runPerformanceBenchmark', { benchmarkId: benchmark.id }); }); // --- Tests for new Performance Optimization methods --- describe('suggestCacheStrategy', () => { it('should suggest server-side caching for high response time', async () => { const targetId = 'api_endpoint:GET:/products'; const mockTargetNode = { id: targetId, data: { /*...*/ } }; const mockPerfReportNode = { id: 'perfAnalysis:1', data: { metrics: { avgResponseTime: 200 } } }; const mockEdge = { source: 'perfAnalysis:1', target: targetId, relationship: 'analyzes' }; mockKnowledgeGraph.findNodes .mockImplementationOnce(async ({ id }) => id === targetId ? [mockTargetNode] : []) // Find target .mockImplementationOnce(async ({ ids, type }) => ids.includes('perfAnalysis:1') && type === 'performance_analysis' ? [mockPerfReportNode] : []); // Find reports mockKnowledgeGraph.findEdges.mockResolvedValue([mockEdge]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); // For suggestion node const result = await agent.suggestCacheStrategy(targetId); expect(result.suggestions).toHaveLength(1); expect(result.suggestions[0]).toEqual(expect.objectContaining({ type: 'server_side', strategy: 'redis' })); expect(result.rationale).toContain('High average response time'); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'performance_suggestion', data: expect.objectContaining({ suggestionType: 'caching' }) })); }); it('should suggest client-side caching for static data', async () => { const targetId = 'config:feature_flags'; const mockTargetNode = { id: targetId, data: { dataType: 'static_config' } }; mockKnowledgeGraph.findNodes.mockResolvedValueOnce([mockTargetNode]); // Find target mockKnowledgeGraph.findEdges.mockResolvedValue([]); // No perf reports linked mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const result = await agent.suggestCacheStrategy(targetId); expect(result.suggestions).toHaveLength(1); expect(result.suggestions[0]).toEqual(expect.objectContaining({ type: 'client_side', strategy: 'localStorage' })); expect(result.rationale).toContain('static or infrequently updated'); }); it('should return no suggestions if no clear opportunity found', async () => { const targetId = 'component:realtime-feed'; const mockTargetNode = { id: targetId, data: { dataType: 'dynamic' } }; mockKnowledgeGraph.findNodes.mockResolvedValueOnce([mockTargetNode]); mockKnowledgeGraph.findEdges.mockResolvedValue([]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const result = await agent.suggestCacheStrategy(targetId); expect(result.suggestions).toEqual([]); expect(result.rationale).toContain('No obvious caching opportunities'); // Even if no suggestion, we might still log the attempt expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'performance_suggestion', data: expect.objectContaining({ suggestions: [] }) })); }); it('should handle target not found gracefully', async () => { const targetId = 'nonexistent'; mockKnowledgeGraph.findNodes.mockResolvedValue([]); // Target not found const result = await agent.suggestCacheStrategy(targetId); expect(result.suggestions).toEqual([]); expect(result.rationale).toBe('Target not found'); expect(mockKnowledgeGraph.addNode).not.toHaveBeenCalled(); // Don't log suggestion if target DNE expect(console.warn).toHaveBeenCalled(); }); }); describe('suggestResourceAllocation', () => { it('should suggest increasing CPU for high utilization', async () => { const analysisId = 'perfAnalysis_high_cpu'; const mockAnalysisNode = { id: `performanceAnalysis:${analysisId}`, type: 'performance_analysis', data: { id: analysisId, scope: { component: 'data-processor' }, metrics: { resourceUtilization: { cpu: 90, memory: 50 } } } }; mockKnowledgeGraph.findNodes.mockResolvedValue([mockAnalysisNode]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); // For suggestion node const result = await agent.suggestResourceAllocation(analysisId); expect(result.suggestions).toHaveLength(1); expect(result.suggestions[0]).toEqual(expect.objectContaining({ resource: 'CPU', action: 'increase' })); expect(result.rationale).toContain('High CPU utilization'); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'performance_suggestion', data: expect.objectContaining({ suggestionType: 'resource_allocation', sourceAnalysis: analysisId }) })); }); it('should suggest decreasing Memory for low utilization', async () => { const analysisId = 'perfAnalysis_low_mem'; const mockAnalysisNode = { id: `performanceAnalysis:${analysisId}`, type: 'performance_analysis', data: { id: analysisId, scope: { component: 'notification-service' }, metrics: { resourceUtilization: { cpu: 40, memory: 15 } } // Low memory } }; mockKnowledgeGraph.findNodes.mockResolvedValue([mockAnalysisNode]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const result = await agent.suggestResourceAllocation(analysisId); expect(result.suggestions).toHaveLength(1); expect(result.suggestions[0]).toEqual(expect.objectContaining({ resource: 'Memory', action: 'decrease' })); expect(result.rationale).toContain('Low Memory utilization'); }); it('should suggest increasing both CPU and Memory if both high', async () => { const analysisId = 'perfAnalysis_high_both'; const mockAnalysisNode = { id: `performanceAnalysis:${analysisId}`, type: 'performance_analysis', data: { metrics: { resourceUtilization: { cpu: 90, memory: 95 } } } }; mockKnowledgeGraph.findNodes.mockResolvedValue([mockAnalysisNode]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const result = await agent.suggestResourceAllocation(analysisId); expect(result.suggestions).toHaveLength(2); expect(result.suggestions).toEqual(expect.arrayContaining([ expect.objectContaining({ resource: 'CPU', action: 'increase' }), expect.objectContaining({ resource: 'Memory', action: 'increase' }) ])); }); it('should suggest nothing if utilization is normal', async () => { const analysisId = 'perfAnalysis_normal'; const mockAnalysisNode = { id: `performanceAnalysis:${analysisId}`, type: 'performance_analysis', data: { metrics: { resourceUtilization: { cpu: 50, memory: 60 } } } }; mockKnowledgeGraph.findNodes.mockResolvedValue([mockAnalysisNode]); mockKnowledgeGraph.addNode.mockResolvedValue(undefined); const result = await agent.suggestResourceAllocation(analysisId); expect(result.suggestions).toEqual([]); expect(result.rationale).toContain('Resource utilization within normal parameters'); expect(mockKnowledgeGraph.addNode).toHaveBeenCalled(); // Still log the suggestion attempt }); it('should throw error if analysis report not found', async () => { const analysisId = 'nonexistent_analysis'; mockKnowledgeGraph.findNodes.mockResolvedValue([]); await expect(agent.suggestResourceAllocation(analysisId)).rejects.toThrow( `Performance analysis ${analysisId} not found` ); expect(mockKnowledgeGraph.addNode).not.toHaveBeenCalled(); }); }); });