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.

401 lines (347 loc) 15.2 kB
import { MonitoringSystem } from '../monitoring.js'; import { KnowledgeGraph } from '../knowledge-graph.js'; import { jest } from '@jest/globals'; // Mock KnowledgeGraph jest.mock('../knowledge-graph.js'); describe('MonitoringSystem', () => { let monitoring; let mockKnowledgeGraph; beforeEach(() => { // Reset mocks jest.clearAllMocks(); // Setup mock KnowledgeGraph mockKnowledgeGraph = { addNode: jest.fn(), updateContext: jest.fn(), findNodes: jest.fn(), }; monitoring = new MonitoringSystem(mockKnowledgeGraph); }); describe('initialize', () => { it('should set default thresholds', async () => { await monitoring.initialize(); expect(monitoring.thresholds.get('agent_load')).toEqual({ warning: 0.8, critical: 0.95 }); expect(monitoring.thresholds.get('workflow_completion_rate')).toEqual({ warning: 0.7, critical: 0.5 }); expect(monitoring.thresholds.get('error_rate')).toEqual({ warning: 0.1, critical: 0.2 }); expect(monitoring.thresholds.get('quality_score')).toEqual({ warning: 0.7, critical: 0.5 }); expect(monitoring.thresholds.get('response_time')).toEqual({ warning: 5000, critical: 10000 }); }); it('should register monitoring node in knowledge graph', async () => { await monitoring.initialize(); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({ id: 'monitoring', type: 'system', data: expect.objectContaining({ thresholds: expect.any(Object), status: 'active', startTime: expect.any(Number) }) }); }); }); describe('takeSnapshot', () => { beforeEach(() => { // Mock agent and workflow data mockKnowledgeGraph.findNodes.mockImplementation((type) => { if (type === 'agent') { return [ { data: { status: 'busy', metrics: { qualityScore: 0.9, successRate: 0.95, avgCompletionTime: 1000 } } }, { data: { status: 'available', metrics: { qualityScore: 0.8, successRate: 0.85, avgCompletionTime: 2000 } } } ]; } if (type === 'workflow') { return [ { data: { status: 'completed', metrics: { qualityScore: 0.85, totalTime: 5000 } } }, { data: { status: 'failed', metrics: { qualityScore: 0.6, totalTime: 8000 } } }, { data: { status: 'in_progress', metrics: { qualityScore: 0.75, totalTime: 3000 } } } ]; } }); }); it('should capture system metrics', async () => { const snapshot = await monitoring.takeSnapshot(); expect(snapshot).toEqual(expect.objectContaining({ timestamp: expect.any(Number), system: expect.objectContaining({ memory: expect.any(Object), activeAlerts: expect.any(Number) }), agents: expect.any(Object), workflows: expect.any(Object) })); }); it('should maintain max snapshots limit', async () => { for (let i = 0; i < 105; i++) { await monitoring.takeSnapshot(); } expect(monitoring.snapshots.length).toBe(100); }); }); describe('checkThresholds', () => { it('should detect agent load violations', async () => { const metrics = { agents: { total: 10, active: 9 }, workflows: { metrics: { successRate: 0.8 } } }; const violations = await monitoring.checkThresholds(metrics); expect(violations).toContainEqual(expect.objectContaining({ metric: 'agent_load', value: 0.9, level: 'warning' })); }); it('should detect workflow completion rate violations', async () => { const metrics = { agents: { total: 10, active: 5 }, workflows: { metrics: { successRate: 0.4 } } }; const violations = await monitoring.checkThresholds(metrics); expect(violations).toContainEqual(expect.objectContaining({ metric: 'workflow_completion_rate', value: 0.4, level: 'critical' })); }); }); describe('createAlert', () => { it('should create and store alert', async () => { const violation = { metric: 'agent_load', value: 0.9, threshold: 0.8, level: 'warning' }; const alert = await monitoring.createAlert(violation); expect(alert).toEqual(expect.objectContaining({ id: expect.stringContaining('agent_load'), status: 'active', created: expect.any(Number), ...violation })); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({ id: alert.id, type: 'alert', data: alert }); }); }); describe('resolveAlert', () => { it('should resolve existing alert', async () => { // Create test alert const alert = await monitoring.createAlert({ metric: 'agent_load', value: 0.9, threshold: 0.8, level: 'warning' }); const resolved = await monitoring.resolveAlert(alert.id); expect(resolved.status).toBe('resolved'); expect(resolved.resolvedAt).toBeDefined(); expect(monitoring.alerts.has(alert.id)).toBe(false); expect(mockKnowledgeGraph.updateContext).toHaveBeenCalledWith({ id: alert.id, type: 'alert', status: 'resolved', resolvedAt: expect.any(Number) }); }); it('should throw error for non-existent alert', async () => { await expect(monitoring.resolveAlert('fake-id')) .rejects.toThrow('Alert not found'); }); }); describe('getMetricsReport', () => { beforeEach(async () => { // Create test snapshots const baseTime = Date.now() - 3600000; // 1 hour ago const snapshots = []; for (let i = 0; i < 5; i++) { snapshots.push({ timestamp: baseTime + (i * 900000), // 15 min intervals system: { memory: { heapUsed: 1000000 + (i * 100000) } }, agents: { total: 10, active: 5 + i, metrics: { avgQuality: 0.8 + (i * 0.02) } }, workflows: { completed: 20 + i, total: 30, metrics: { avgQuality: 0.85 + (i * 0.01) } } }); } monitoring.snapshots = snapshots; }); it('should generate report for valid timeframe', async () => { const report = await monitoring.getMetricsReport('1h'); expect(report).toEqual(expect.objectContaining({ timeframe: '1h', snapshots: 5, duration: expect.any(Number), system: expect.any(Object), agents: expect.any(Object), workflows: expect.any(Object), activeAlerts: expect.any(Array) })); }); it('should handle empty timeframe', async () => { const report = await monitoring.getMetricsReport('5m'); expect(report).toEqual({ timeframe: '5m', snapshots: 0, message: 'No data available for specified timeframe' }); }); it('should calculate trends correctly', async () => { const report = await monitoring.getMetricsReport('1h'); expect(report.agents.avgLoad).toEqual(expect.objectContaining({ start: expect.any(Number), end: expect.any(Number), min: expect.any(Number), max: expect.any(Number), avg: expect.any(Number), trend: expect.any(Number) })); }); }); describe('parseTimeframe', () => { it('should parse valid timeframes', () => { expect(monitoring.parseTimeframe('1h')).toBe(3600000); expect(monitoring.parseTimeframe('30m')).toBe(1800000); expect(monitoring.parseTimeframe('2d')).toBe(172800000); }); it('should throw error for invalid timeframe', () => { expect(() => monitoring.parseTimeframe('1x')).toThrow('Invalid timeframe format'); }); }); // --- Tests for Maintenance Alerts --- describe('checkForMaintenanceAlerts', () => { beforeEach(() => { // Clear alerts map before each maintenance test monitoring.alerts = new Map(); mockKnowledgeGraph.addNode.mockClear(); // Clear KG addNode mock }); it('should return empty if not enough snapshots', async () => { monitoring.snapshots = [{ timestamp: Date.now(), system: {}, agents: {}, workflows: {} }]; // Only one snapshot const alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toEqual([]); expect(mockKnowledgeGraph.addNode).not.toHaveBeenCalled(); }); it('should detect prolonged high memory usage', async () => { const now = Date.now(); const highMem = 500 * 1024 * 1024 * 0.9; // 90% of 500MB monitoring.snapshots = [ { timestamp: now - 3600 * 1000, system: { memory: { heapUsed: highMem } }, agents: {}, workflows: {} }, { timestamp: now - 1800 * 1000, system: { memory: { heapUsed: highMem + 1000 } }, agents: {}, workflows: {} }, { timestamp: now - 10 * 1000, system: { memory: { heapUsed: highMem + 2000 } }, agents: {}, workflows: {} }, ]; const alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toHaveLength(1); expect(alerts[0]).toEqual(expect.objectContaining({ type: 'memory_leak_suspected', severity: 'medium' })); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'maintenance_alert' })); }); it('should detect persistently low workflow success rate', async () => { const now = Date.now(); const lowRate = 0.6; // Below warning threshold of 0.7 monitoring.thresholds.set('workflow_completion_rate', { warning: 0.7, critical: 0.5 }); monitoring.snapshots = [ { timestamp: now - 3600 * 1000, system: {}, agents: {}, workflows: { metrics: { successRate: lowRate } } }, { timestamp: now - 1800 * 1000, system: {}, agents: {}, workflows: { metrics: { successRate: lowRate - 0.01 } } }, { timestamp: now - 10 * 1000, system: {}, agents: {}, workflows: { metrics: { successRate: lowRate + 0.02 } } }, ]; const alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toHaveLength(1); expect(alerts[0]).toEqual(expect.objectContaining({ type: 'persistent_workflow_failures', severity: 'high' })); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'maintenance_alert' })); }); it('should detect increasing error rate above warning', async () => { const now = Date.now(); monitoring.thresholds.set('error_rate', { warning: 0.1, critical: 0.2 }); monitoring.snapshots = [ { timestamp: now - 3600 * 1000, error_rate: 0.05, system: {}, agents: {}, workflows: {} }, { timestamp: now - 1800 * 1000, error_rate: 0.08, system: {}, agents: {}, workflows: {} }, { timestamp: now - 10 * 1000, error_rate: 0.11, system: {}, agents: {}, workflows: {} }, // Trend > 0.05, end > warning ]; const alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toHaveLength(1); expect(alerts[0]).toEqual(expect.objectContaining({ type: 'increasing_error_rate', severity: 'medium' })); expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith(expect.objectContaining({ type: 'maintenance_alert' })); }); it('should NOT detect increasing error rate if trend is small or below warning', async () => { const now = Date.now(); monitoring.thresholds.set('error_rate', { warning: 0.1, critical: 0.2 }); // Scenario 1: Trend small monitoring.snapshots = [ { timestamp: now - 3600 * 1000, error_rate: 0.11, system: {}, agents: {}, workflows: {} }, { timestamp: now - 10 * 1000, error_rate: 0.12, system: {}, agents: {}, workflows: {} }, ]; let alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toEqual([]); // Scenario 2: End value below warning monitoring.snapshots = [ { timestamp: now - 3600 * 1000, error_rate: 0.01, system: {}, agents: {}, workflows: {} }, { timestamp: now - 10 * 1000, error_rate: 0.08, system: {}, agents: {}, workflows: {} }, // Trend > 0.05, but end < warning ]; alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toEqual([]); }); it('should not generate alerts if conditions are normal', async () => { const now = Date.now(); monitoring.thresholds.set('workflow_completion_rate', { warning: 0.7, critical: 0.5 }); monitoring.snapshots = [ { timestamp: now - 3600 * 1000, system: { memory: { heapUsed: 100 * 1024 * 1024 } }, agents: {}, workflows: { metrics: { successRate: 0.95 } }, error_rate: 0.01 }, { timestamp: now - 1800 * 1000, system: { memory: { heapUsed: 110 * 1024 * 1024 } }, agents: {}, workflows: { metrics: { successRate: 0.92 } }, error_rate: 0.02 }, { timestamp: now - 10 * 1000, system: { memory: { heapUsed: 105 * 1024 * 1024 } }, agents: {}, workflows: { metrics: { successRate: 0.98 } }, error_rate: 0.015 }, ]; const alerts = await monitoring.checkForMaintenanceAlerts(); expect(alerts).toEqual([]); expect(mockKnowledgeGraph.addNode).not.toHaveBeenCalled(); }); }); // --- Test for Audit Logging --- describe('logAuditEvent', () => { it('should log an audit event and add it to the knowledge graph', async () => { const eventType = 'agent_action'; const description = 'Agent X performed action Y on target Z'; const details = { agentId: 'agent:X', action: 'actionY', targetId: 'target:Z', status: 'success' }; mockKnowledgeGraph.addNode.mockResolvedValue(undefined); // Spy on local log const logSpy = jest.spyOn(require('../../utils/logging.js'), 'log').mockImplementation(() => {}); const eventData = await monitoring.logAuditEvent(eventType, description, details); expect(eventData).toBeDefined(); expect(eventData.id).toMatch(/^auditEvent:agent_action_/); expect(eventData.eventType).toBe(eventType); expect(eventData.description).toBe(description); expect(eventData.details).toEqual(details); expect(eventData.timestamp).toBeDefined(); // Check local logging expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Audit Event'), details); // Check KG logging expect(mockKnowledgeGraph.addNode).toHaveBeenCalledWith({ id: eventData.id, type: 'audit_log_event', data: expect.objectContaining({ eventType, description, details }) // Check edges if they were added based on details }); logSpy.mockRestore(); }); }); });