UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

339 lines (282 loc) 11 kB
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import express, { Application } from 'express'; import request from 'supertest'; import { setupMetricsAPI } from '../../api/metrics.js'; import { PerformanceMonitor } from '../../../utils/performance-monitor.js'; // Mock the PerformanceMonitor vi.mock('../../../utils/performance-monitor.js', () => ({ PerformanceMonitor: { getInstance: vi.fn() } })); describe('Metrics API', () => { let app: Application; let mockPerformanceMonitor: any; beforeEach(() => { app = express(); app.use(express.json()); // Create mock performance monitor mockPerformanceMonitor = { getSystemMetrics: vi.fn(), getToolMetrics: vi.fn(), getQualityMetrics: vi.fn(), getPerformanceAlerts: vi.fn(), generateAlerts: vi.fn(), // Add missing method recordToolExecution: vi.fn(), recordModulePerformance: vi.fn(), recordAPICall: vi.fn(), exportMetrics: vi.fn(), recordQualityEvaluation: vi.fn(), getModuleMetrics: vi.fn(), analyzeBottlenecks: vi.fn(), generatePerformanceReport: vi.fn(), setAlertThreshold: vi.fn(), clearMetrics: vi.fn() }; // Mock getInstance to return our mock (PerformanceMonitor.getInstance as any).mockReturnValue(mockPerformanceMonitor); // Setup the API with exportEnabled parameter setupMetricsAPI(app, mockPerformanceMonitor, true); }); afterEach(() => { vi.clearAllMocks(); }); describe('GET /api/metrics/overview', () => { it('should return system metrics successfully', async () => { const mockSystemMetrics = { cpu: { usage: 45.5, count: 8 }, memory: { used: 1024, total: 8192, percentage: 12.5 }, uptime: 3600, timestamp: new Date().toISOString() }; mockPerformanceMonitor.getSystemMetrics.mockResolvedValue(mockSystemMetrics); const response = await request(app).get('/api/metrics/overview'); expect(response.status).toBe(200); expect(response.body).toEqual({ success: true, data: mockSystemMetrics, timestamp: expect.any(String) }); expect(mockPerformanceMonitor.getSystemMetrics).toHaveBeenCalled(); }); it('should handle errors gracefully', async () => { mockPerformanceMonitor.getSystemMetrics.mockRejectedValue(new Error('System error')); const response = await request(app).get('/api/metrics/overview'); expect(response.status).toBe(500); expect(response.body).toEqual({ success: false, error: 'Failed to fetch system metrics', message: 'System error' }); }); }); describe('GET /api/metrics/tools', () => { it('should return tool metrics with filters', async () => { const mockToolMetrics = [ { toolName: 'create_epic', executions: 10, avgResponseTime: 150, successRate: 90, lastExecuted: new Date().toISOString() } ]; mockPerformanceMonitor.getToolMetrics.mockResolvedValue(mockToolMetrics); const response = await request(app) .get('/api/metrics/tools') .query({ toolName: 'create_epic', timeRange: '24h' }); expect(response.status).toBe(200); expect(response.body.success).toBe(true); expect(response.body.data).toEqual({ toolName: 'create_epic', metrics: mockToolMetrics, timeRange: '24h' }); expect(mockPerformanceMonitor.getToolMetrics).toHaveBeenCalledWith('create_epic'); }); }); describe('GET /api/metrics/quality', () => { it('should return quality metrics', async () => { const mockQualityMetrics = { testCoverage: 80, codeQuality: 85, buildSuccess: 95, deploymentFrequency: 5 }; mockPerformanceMonitor.getQualityMetrics.mockResolvedValue(mockQualityMetrics); const response = await request(app).get('/api/metrics/quality'); expect(response.status).toBe(200); expect(response.body.data).toEqual({ toolName: 'all', timeRange: '7d', qualityMetrics: mockQualityMetrics }); }); }); describe('GET /api/metrics/alerts', () => { it('should return performance alerts filtered by severity', async () => { const mockAlerts = [ { id: '1', severity: 'high', metric: 'cpu', message: 'High CPU usage', timestamp: new Date().toISOString() } ]; mockPerformanceMonitor.generateAlerts.mockResolvedValue(mockAlerts); const response = await request(app) .get('/api/metrics/alerts') .query({ severity: 'high', active: 'true' }); expect(response.status).toBe(200); expect(response.body.data).toEqual({ alerts: mockAlerts, count: mockAlerts.length }); expect(mockPerformanceMonitor.generateAlerts).toHaveBeenCalled(); }); }); describe('POST /api/metrics/record', () => { it('should record tool execution metrics', async () => { const executionData = { tool: 'create_epic', success: true, duration: 150, metadata: { epicId: '123' } }; const response = await request(app) .post('/api/metrics/record') .send(executionData); expect(response.status).toBe(200); expect(response.body.success).toBe(true); expect(mockPerformanceMonitor.recordToolExecution).toHaveBeenCalledWith( 'create_epic', 150, true ); }); it('should validate required fields', async () => { const response = await request(app) .post('/api/metrics/record') .send({ tool: 'test_tool' }); // missing success field expect(response.status).toBe(400); expect(response.body.error).toBe('Tool name and success status are required'); }); }); describe('GET /api/metrics/export', () => { it('should export metrics for specified time range', async () => { const mockExportData = { timeRange: { start: '2024-01-01', end: '2024-01-31' }, metrics: { tool: [], system: [], quality: [] } }; mockPerformanceMonitor.exportMetrics.mockResolvedValue(mockExportData); const response = await request(app) .get('/api/metrics/export') .query({ format: 'json', startDate: '2024-01-01', endDate: '2024-01-31' }); expect(response.status).toBe(200); expect(response.body.data).toHaveProperty('exportPath'); expect(response.body.data).toHaveProperty('format', 'json'); expect(mockPerformanceMonitor.exportMetrics).toHaveBeenCalledWith('json'); }); }); describe('GET /api/metrics/trends', () => { it('should return performance trends (currently mock data)', async () => { const response = await request(app) .get('/api/metrics/trends') .query({ metric: 'response_time', timeRange: '7d' }); expect(response.status).toBe(200); expect(response.body.success).toBe(true); expect(response.body.data).toHaveProperty('dataPoints'); expect(response.body.data).toHaveProperty('timeRange', '7d'); expect(response.body.data).toHaveProperty('metric', 'response_time'); }); }); describe('POST /api/metrics/quality/evaluate', () => { it('should record quality evaluation', async () => { const qualityData = { toolName: 'test_tool', quality: 8, feedback: 'Good quality output' }; const response = await request(app) .post('/api/metrics/quality/evaluate') .send(qualityData); expect(response.status).toBe(200); expect(mockPerformanceMonitor.recordQualityEvaluation).toHaveBeenCalledWith({ toolName: 'test_tool', outputQuality: 8, appropriatenessScore: 8, completenessScore: 8, accuracyScore: 8, comments: 'Good quality output', evaluationCriteria: ['user_feedback'] }); }); }); describe('GET /api/metrics/modules', () => { it('should return module-specific metrics', async () => { const mockModuleMetrics = { 'agile-management': { calls: 100, avgTime: 50 }, 'kanban': { calls: 150, avgTime: 30 } }; mockPerformanceMonitor.getModuleMetrics.mockResolvedValue(mockModuleMetrics); const response = await request(app) .get('/api/metrics/modules') .query({ moduleName: 'agile-management' }); expect(response.status).toBe(200); expect(response.body.data).toEqual(mockModuleMetrics); expect(mockPerformanceMonitor.getModuleMetrics).toHaveBeenCalledWith('agile-management'); }); }); describe('GET /api/metrics/bottlenecks', () => { it('should analyze and return performance bottlenecks', async () => { const mockBottlenecks = { slowestTools: ['complex_analysis', 'generate_report'], highMemoryUsage: ['data_processing'], recommendations: ['Consider caching for complex_analysis'] }; mockPerformanceMonitor.analyzeBottlenecks.mockResolvedValue(mockBottlenecks); const response = await request(app) .get('/api/metrics/bottlenecks') .query({ threshold: '500' }); expect(response.status).toBe(200); expect(response.body.data).toEqual(mockBottlenecks); expect(mockPerformanceMonitor.analyzeBottlenecks).toHaveBeenCalledWith({ threshold: 500 }); }); }); describe('GET /api/metrics/report', () => { it('should generate performance report', async () => { const mockReport = { summary: 'System performing well', metrics: {}, recommendations: [] }; mockPerformanceMonitor.generatePerformanceReport.mockResolvedValue(mockReport); const response = await request(app) .get('/api/metrics/report') .query({ format: 'json', includeRecommendations: 'true' }); expect(response.status).toBe(200); expect(response.body.data).toEqual(mockReport); expect(mockPerformanceMonitor.generatePerformanceReport).toHaveBeenCalledWith({ format: 'json', includeRecommendations: true }); }); }); describe('DELETE /api/metrics/clear', () => { it('should clear metrics with confirmation', async () => { mockPerformanceMonitor.clearMetrics.mockResolvedValue({ cleared: true }); const response = await request(app) .delete('/api/metrics/clear') .query({ confirm: 'yes', before: '2024-01-01' }); expect(response.status).toBe(200); expect(response.body.success).toBe(true); expect(mockPerformanceMonitor.clearMetrics).toHaveBeenCalledWith({ before: '2024-01-01' }); }); it('should require confirmation', async () => { const response = await request(app).delete('/api/metrics/clear'); expect(response.status).toBe(400); expect(response.body.error).toBe('Confirmation required to clear metrics'); }); }); });