import { beforeEach, describe, expect, it, vi } from 'vitest'; // Mock PerformanceMonitor class for testing class MockPerformanceMonitor { constructor() { this.startTime = Date.now(); this.marks = new Map(); } /** * Mark a performance measurement * @param {string} name - Name of the mark */ mark(name) { if (this.marks.has(name)) { console.warn(`Mark with name ${name} already exists.`); } this.marks.set(name, Date.now() - this.startTime); } getMetrics() { return Object.fromEntries(this.marks.entries()); } } describe('Performance Monitoring', () => { /** @type {MockPerformanceMonitor} */ let monitor; beforeEach(() => { monitor = new MockPerformanceMonitor(); }); describe('PerformanceMonitor Class', () => { it('should initialize with start time', () => { expect(monitor.startTime).toBeDefined(); expect(typeof monitor.startTime).toBe('number'); }); it('should create timing marks', () => { monitor.mark('test-mark'); const metrics = monitor.getMetrics(); expect(metrics).toHaveProperty('test-mark'); expect(typeof metrics['test-mark']).toBe('number'); }); it('should handle multiple marks', () => { monitor.mark('mark1'); monitor.mark('mark2'); monitor.mark('mark3'); const metrics = monitor.getMetrics(); expect(Object.keys(metrics)).toHaveLength(3); expect(metrics).toHaveProperty('mark1'); expect(metrics).toHaveProperty('mark2'); expect(metrics).toHaveProperty('mark3'); }); it('should warn on duplicate mark names', () => { // Mock console.warn for this test const originalWarn = console.warn; const mockWarn = vi.fn(); console.warn = mockWarn; monitor.mark('duplicate'); monitor.mark('duplicate'); expect(mockWarn).toHaveBeenCalledWith('Mark with name duplicate already exists.'); // Restore original console.warn console.warn = originalWarn; }); it('should return metrics as plain object', () => { monitor.mark('test'); const metrics = monitor.getMetrics(); expect(metrics).toBeTypeOf('object'); expect(Array.isArray(metrics)).toBe(false); }); it('should track elapsed time correctly', async () => { monitor.mark('start'); // Wait a small amount of time await new Promise(resolve => setTimeout(resolve, 10)); monitor.mark('end'); const metrics = monitor.getMetrics(); expect(metrics.end).toBeGreaterThan(metrics.start); }); }); describe('Performance Metrics Validation', () => { it('should produce serializable metrics', () => { monitor.mark('request-start'); monitor.mark('proxy-start'); monitor.mark('proxy-end'); monitor.mark('request-end'); const metrics = monitor.getMetrics(); expect(() => JSON.stringify(metrics)).not.toThrow(); }); it('should have reasonable timing values', () => { monitor.mark('test-mark'); const metrics = monitor.getMetrics(); const timing = metrics['test-mark']; // Should be a positive number and reasonable (less than 1 second for this test) expect(timing).toBeGreaterThanOrEqual(0); expect(timing).toBeLessThan(1000); }); it('should maintain chronological order', async () => { monitor.mark('first'); await new Promise(resolve => setTimeout(resolve, 5)); monitor.mark('second'); await new Promise(resolve => setTimeout(resolve, 5)); monitor.mark('third'); const metrics = monitor.getMetrics(); expect(metrics.first).toBeLessThan(metrics.second); expect(metrics.second).toBeLessThan(metrics.third); }); }); describe('Common Performance Scenarios', () => { it('should track request lifecycle', () => { // Simulate typical request flow monitor.mark('request-received'); monitor.mark('validation-complete'); monitor.mark('proxy-start'); monitor.mark('proxy-response'); monitor.mark('response-sent'); const metrics = monitor.getMetrics(); expect(metrics).toHaveProperty('request-received'); expect(metrics).toHaveProperty('validation-complete'); expect(metrics).toHaveProperty('proxy-start'); expect(metrics).toHaveProperty('proxy-response'); expect(metrics).toHaveProperty('response-sent'); }); it('should track cache operations', () => { monitor.mark('cache-check-start'); monitor.mark('cache-miss'); monitor.mark('upstream-request'); monitor.mark('cache-store'); const metrics = monitor.getMetrics(); expect(metrics).toHaveProperty('cache-check-start'); expect(metrics).toHaveProperty('cache-miss'); expect(metrics).toHaveProperty('upstream-request'); expect(metrics).toHaveProperty('cache-store'); }); it('should track error scenarios', () => { monitor.mark('request-start'); monitor.mark('error-occurred'); monitor.mark('error-handled'); const metrics = monitor.getMetrics(); expect(metrics).toHaveProperty('request-start'); expect(metrics).toHaveProperty('error-occurred'); expect(metrics).toHaveProperty('error-handled'); }); }); describe('Performance Thresholds', () => { it('should identify slow operations', () => { monitor.mark('operation-start'); // Simulate slow operation const slowTiming = 5000; // 5 seconds monitor.marks.set('operation-end', slowTiming); const metrics = monitor.getMetrics(); const operationTime = metrics['operation-end'] - (metrics['operation-start'] || 0); // Should identify as slow (> 1 second) expect(operationTime).toBeGreaterThan(1000); }); it('should identify fast operations', () => { monitor.mark('fast-operation'); const metrics = monitor.getMetrics(); const timing = metrics['fast-operation']; // Should be fast (< 100ms for this test) expect(timing).toBeLessThan(100); }); }); describe('Memory and Resource Usage', () => { it('should not leak memory with many marks', () => { const initialSize = monitor.marks.size; // Add many marks for (let i = 0; i < 1000; i++) { monitor.mark(`mark-${i}`); } expect(monitor.marks.size).toBe(initialSize + 1000); // Clear marks (if such method existed) monitor.marks.clear(); expect(monitor.marks.size).toBe(0); }); it('should handle concurrent mark operations', () => { const promises = []; for (let i = 0; i < 10; i++) { promises.push( new Promise((/** @type {(value?: unknown) => void} */ resolve) => { setTimeout(() => { monitor.mark(`concurrent-${i}`); resolve(); }, Math.random() * 10); }) ); } return Promise.all(promises).then(() => { const metrics = monitor.getMetrics(); expect(Object.keys(metrics)).toHaveLength(10); }); }); }); });