import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { PerformanceMonitor } from '../../src/monitoring/PerformanceMonitor.js';
describe('PerformanceMonitor', () => {
let monitor: PerformanceMonitor;
beforeEach(() => {
monitor = new PerformanceMonitor(100, 1000); // 100 max history, 1000ms slow threshold
});
describe('Basic Timing', () => {
it('should start and end timing operations', async () => {
const requestId = monitor.startTiming('test_operation');
expect(requestId).toBeDefined();
await new Promise(resolve => setTimeout(resolve, 50));
const duration = monitor.endTiming(requestId, true, false);
expect(duration).toBeGreaterThanOrEqual(50);
expect(duration).toBeLessThan(100);
});
it('should handle successful operations', async () => {
const requestId = monitor.startTiming('success_test');
const duration = monitor.endTiming(requestId, true, false);
const metrics = monitor.getMetrics();
expect(metrics.totalRequests).toBe(1);
expect(metrics.errorRate).toBe(0);
});
it('should handle failed operations', async () => {
const requestId = monitor.startTiming('error_test');
monitor.endTiming(requestId, false, false, 'Test error');
const metrics = monitor.getMetrics();
expect(metrics.totalRequests).toBe(1);
expect(metrics.errorRate).toBe(100);
});
it('should track cached operations', async () => {
const requestId = monitor.startTiming('cache_test');
monitor.endTiming(requestId, true, true);
const metrics = monitor.getMetrics();
expect(metrics.cacheHitRate).toBe(100);
});
});
describe('Async Operation Timing', () => {
it('should time async operations successfully', async () => {
const result = await monitor.timeOperation('async_test', async () => {
await new Promise(resolve => setTimeout(resolve, 50));
return 'success';
});
expect(result).toBe('success');
const metrics = monitor.getMetrics();
expect(metrics.totalRequests).toBe(1);
expect(metrics.operationStats['async_test']).toBeDefined();
expect(metrics.operationStats['async_test'].count).toBe(1);
});
it('should handle async operation errors', async () => {
await expect(
monitor.timeOperation('error_test', async () => {
throw new Error('Test error');
})
).rejects.toThrow('Test error');
const metrics = monitor.getMetrics();
expect(metrics.operationStats['error_test'].errorCount).toBe(1);
});
it('should track cached async operations', async () => {
await monitor.timeOperation('cached_test', async () => {
return 'cached_result';
}, true);
const metrics = monitor.getMetrics();
expect(metrics.cacheHitRate).toBe(100);
});
});
describe('Metrics and Statistics', () => {
beforeEach(async () => {
// Setup some test data
await monitor.timeOperation('search', async () => 'result1');
await monitor.timeOperation('search', async () => 'result2', true); // cached
await monitor.timeOperation('get_page', async () => 'page');
try {
await monitor.timeOperation('error_op', async () => {
throw new Error('Test error');
});
} catch (e) {
// Expected error
}
});
it('should calculate correct overall metrics', () => {
const metrics = monitor.getMetrics();
expect(metrics.totalRequests).toBe(4);
expect(metrics.cacheHitRate).toBe(25); // 1 cached out of 4
expect(metrics.errorRate).toBe(25); // 1 error out of 4
});
it('should provide per-operation statistics', () => {
const metrics = monitor.getMetrics();
expect(metrics.operationStats['search']).toBeDefined();
expect(metrics.operationStats['search'].count).toBe(2);
expect(metrics.operationStats['search'].cacheHits).toBe(1);
expect(metrics.operationStats['search'].errorCount).toBe(0);
expect(metrics.operationStats['error_op']).toBeDefined();
expect(metrics.operationStats['error_op'].errorCount).toBe(1);
});
it('should calculate average response times', async () => {
// Add a timing to ensure we have data
await monitor.timeOperation('search', async () => {
await new Promise(resolve => setTimeout(resolve, 10));
return 'result';
});
const metrics = monitor.getMetrics();
expect(metrics.averageResponseTime).toBeGreaterThan(0);
expect(metrics.operationStats['search'].averageTime).toBeGreaterThan(0);
});
});
describe('Performance Summary', () => {
beforeEach(async () => {
// Add test data with different characteristics
await monitor.timeOperation('fast_op', async () => {
await new Promise(resolve => setTimeout(resolve, 10));
return 'fast';
});
await monitor.timeOperation('slow_op', async () => {
await new Promise(resolve => setTimeout(resolve, 1100)); // Slow operation
return 'slow';
});
await monitor.timeOperation('cached_op', async () => 'cached', true);
});
it('should generate comprehensive performance summary', () => {
const summary = monitor.getPerformanceSummary();
expect(summary.overview).toBeDefined();
expect(summary.overview.totalRequests).toBe(3);
expect(summary.overview.slowRequests).toBe(1); // slow_op > 1000ms
expect(summary.topOperations).toBeDefined();
expect(summary.recentActivity).toBeDefined();
expect(summary.alerts).toBeDefined();
});
it('should identify slow requests', () => {
const summary = monitor.getPerformanceSummary();
expect(summary.overview.slowRequests).toBeGreaterThan(0);
});
it('should generate performance alerts', () => {
const summary = monitor.getPerformanceSummary();
// Should have alerts for slow operations
expect(summary.alerts.length).toBeGreaterThan(0);
expect(summary.alerts.some(alert =>
alert.includes('slow') || alert.includes('response time')
)).toBe(true);
});
});
describe('Timing History', () => {
beforeEach(async () => {
for (let i = 0; i < 5; i++) {
await monitor.timeOperation('test_op', async () => `result${i}`);
}
});
it('should maintain timing history', () => {
const history = monitor.getTimingHistory();
expect(history).toHaveLength(5);
history.forEach(timing => {
expect(timing).toHaveProperty('operation', 'test_op');
expect(timing).toHaveProperty('duration');
expect(timing).toHaveProperty('success', true);
});
});
it('should filter history by operation', () => {
const history = monitor.getTimingHistory('test_op');
expect(history).toHaveLength(5);
const nonExistentHistory = monitor.getTimingHistory('nonexistent');
expect(nonExistentHistory).toHaveLength(0);
});
it('should limit history results', () => {
const limitedHistory = monitor.getTimingHistory(undefined, 3);
expect(limitedHistory).toHaveLength(3);
});
});
describe('Active Timings', () => {
it('should track active operations', () => {
const requestId1 = monitor.startTiming('active_op1');
const requestId2 = monitor.startTiming('active_op2');
const activeTimings = monitor.getActiveTimings();
expect(activeTimings).toHaveLength(2);
expect(activeTimings[0]).toHaveProperty('requestId');
expect(activeTimings[0]).toHaveProperty('operation');
expect(activeTimings[0]).toHaveProperty('elapsed');
// Clean up
monitor.endTiming(requestId1);
monitor.endTiming(requestId2);
});
it('should clear active timings when operations complete', () => {
const requestId = monitor.startTiming('test_op');
expect(monitor.getActiveTimings()).toHaveLength(1);
monitor.endTiming(requestId);
expect(monitor.getActiveTimings()).toHaveLength(0);
});
});
describe('Reset Functionality', () => {
beforeEach(async () => {
await monitor.timeOperation('test', async () => 'result');
});
it('should reset all metrics and history', () => {
expect(monitor.getMetrics().totalRequests).toBe(1);
monitor.reset();
expect(monitor.getMetrics().totalRequests).toBe(0);
expect(monitor.getTimingHistory()).toHaveLength(0);
});
});
});