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
JavaScript
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();
});
});
});