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