Skip to main content
Glama
performance-monitor.test.js32 kB
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest'; import { PerformanceMonitor } from '../../lib/utils/performance-monitor.js'; describe('PerformanceMonitor', () => { let monitor; let originalProcess; beforeEach(() => { // Mock console methods to prevent test output noise vi.spyOn(console, 'warn').mockImplementation(() => {}); // Mock process.memoryUsage for memory testing originalProcess = global.process; global.process = { ...originalProcess, memoryUsage: vi.fn(() => ({ heapUsed: 100 * 1024 * 1024 // 100 MB })) }; // Mock Math.random for predictable IDs vi.spyOn(Math, 'random').mockReturnValue(0.123456789); // Mock Date.now for predictable timestamps vi.spyOn(Date, 'now') .mockReturnValueOnce(1000) // Constructor startTime .mockReturnValue(2000); // Default for other calls }); afterEach(() => { global.process = originalProcess; vi.clearAllMocks(); }); describe('Constructor and Configuration', () => { test('should initialize with default configuration', () => { monitor = new PerformanceMonitor(); expect(monitor.config).toEqual({ enabled: true, maxMetricsHistory: 1000, slowQueryThreshold: 5000, trackPoolMetrics: true, samplingRate: 1.0 }); expect(monitor.metrics.queries).toEqual([]); expect(monitor.metrics.connections).toEqual([]); expect(monitor.metrics.poolStats).toEqual({ totalConnections: 0, activeConnections: 0, idleConnections: 0, pendingRequests: 0, errors: 0 }); expect(monitor.startTime).toBe(1000); }); test('should initialize with custom configuration', () => { const config = { enabled: false, maxMetricsHistory: 500, slowQueryThreshold: 2000, trackPoolMetrics: false, samplingRate: 0.5, customOption: 'test' }; monitor = new PerformanceMonitor(config); expect(monitor.config).toEqual(config); }); test('should initialize aggregates with correct defaults', () => { monitor = new PerformanceMonitor(); expect(monitor.metrics.aggregates).toEqual({ totalQueries: 0, slowQueries: 0, avgQueryTime: 0, maxQueryTime: 0, minQueryTime: Number.MAX_SAFE_INTEGER, totalQueryTime: 0, errorRate: 0 }); }); }); describe('Query Tracking', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should start query tracking successfully', () => { const queryId = monitor.startQuery('execute_query', 'SELECT * FROM users', { database: 'testdb' }); expect(queryId).toMatch(/^q_\d+_[a-z0-9]+$/); expect(monitor.metrics.queries).toHaveLength(1); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric).toEqual({ id: expect.any(String), tool: 'execute_query', query: 'SELECT * FROM users', database: 'testdb', startTime: 2000, startMemory: 100, status: 'running' }); }); test('should truncate long queries', () => { const longQuery = 'SELECT * FROM users WHERE ' + 'condition AND '.repeat(50) + 'final_condition'; monitor.startQuery('execute_query', longQuery, {}); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric.query).toHaveLength(203); // 200 chars + '...' expect(queryMetric.query.endsWith('...')).toBe(true); }); test('should use default database when not specified', () => { monitor.startQuery('list_tables', 'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES'); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric.database).toBe('default'); }); test('should not track query when disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); const queryId = monitor.startQuery('execute_query', 'SELECT 1'); expect(queryId).toBeNull(); expect(monitor.metrics.queries).toHaveLength(0); }); test('should respect sampling rate', () => { monitor = new PerformanceMonitor({ samplingRate: 0.1 }); Math.random.mockReturnValue(0.5); // > 0.1, should not sample const queryId = monitor.startQuery('execute_query', 'SELECT 1'); expect(queryId).toBeNull(); expect(monitor.metrics.queries).toHaveLength(0); }); test('should sample when within sampling rate', () => { monitor = new PerformanceMonitor({ samplingRate: 0.5 }); Math.random.mockReturnValue(0.3); // < 0.5, should sample const queryId = monitor.startQuery('execute_query', 'SELECT 1'); expect(queryId).not.toBeNull(); expect(monitor.metrics.queries).toHaveLength(1); }); }); describe('Query Completion', () => { beforeEach(() => { monitor = new PerformanceMonitor(); Date.now .mockReturnValueOnce(1000) // Constructor .mockReturnValueOnce(2000) // startQuery .mockReturnValue(3500); // endQuery - 1500ms duration }); test('should complete query successfully', () => { const queryId = monitor.startQuery('execute_query', 'SELECT * FROM users'); const result = { recordset: [{ id: 1 }, { id: 2 }, { id: 3 }], rowsAffected: [3] }; monitor.endQuery(queryId, result); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric).toEqual({ id: queryId, tool: 'execute_query', query: 'SELECT * FROM users', database: 'default', startTime: 2000, startMemory: 100, status: 'completed', endTime: 3500, duration: 1500, endMemory: 100, memoryDelta: 0, error: null, rowsAffected: [3], rowCount: 3, streaming: false }); }); test('should handle query with error', () => { const queryId = monitor.startQuery('execute_query', 'INVALID SQL'); const error = new Error('Syntax error near INVALID'); monitor.endQuery(queryId, {}, error); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric.status).toBe('error'); expect(queryMetric.error).toBe('Syntax error near INVALID'); expect(queryMetric.rowsAffected).toBe(0); expect(queryMetric.rowCount).toBe(0); }); test('should handle streaming result', () => { const queryId = monitor.startQuery('export_table_csv', 'SELECT * FROM large_table'); const result = { streaming: true, rowsAffected: [10000] }; monitor.endQuery(queryId, result); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric.streaming).toBe(true); expect(queryMetric.rowsAffected).toEqual([10000]); }); test('should not complete query when disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); monitor.endQuery('fake-id', {}); expect(monitor.metrics.queries).toHaveLength(0); }); test('should not complete query with invalid ID', () => { const _queryId = monitor.startQuery('execute_query', 'SELECT 1'); monitor.endQuery('invalid-id', {}); const queryMetric = monitor.metrics.queries[0]; expect(queryMetric.status).toBe('running'); // Should remain running }); // Note: Slow query logging functionality is tested indirectly through // aggregate statistics and other tests }); describe('Aggregate Statistics', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should update aggregates for successful query', () => { const queryId = monitor.startQuery('execute_query', 'SELECT 1'); Date.now.mockReturnValue(3000); // 1000ms duration monitor.endQuery(queryId, { recordset: [] }); const agg = monitor.metrics.aggregates; expect(agg.totalQueries).toBe(1); expect(agg.totalQueryTime).toBe(1000); expect(agg.avgQueryTime).toBe(1000); expect(agg.maxQueryTime).toBe(1000); expect(agg.minQueryTime).toBe(1000); expect(agg.errorRate).toBe(0); expect(agg.slowQueries).toBe(0); }); test('should update aggregates for error query', () => { const queryId = monitor.startQuery('execute_query', 'INVALID'); Date.now.mockReturnValue(2500); // 500ms duration monitor.endQuery(queryId, {}, new Error('SQL Error')); const agg = monitor.metrics.aggregates; expect(agg.totalQueries).toBe(1); expect(agg.errorRate).toBe(1); // 100% error rate }); test('should update aggregates for slow query', () => { monitor = new PerformanceMonitor({ slowQueryThreshold: 800 }); const queryId = monitor.startQuery('execute_query', 'SLOW QUERY'); Date.now.mockReturnValue(3000); // 1000ms duration > 800ms threshold monitor.endQuery(queryId, { recordset: [] }); const agg = monitor.metrics.aggregates; expect(agg.slowQueries).toBe(1); }); test('should calculate correct averages with multiple queries', () => { // First query: 1000ms const queryId1 = monitor.startQuery('execute_query', 'SELECT 1'); Date.now.mockReturnValue(3000); monitor.endQuery(queryId1, { recordset: [] }); // Second query: 2000ms Date.now.mockReturnValue(4000); const queryId2 = monitor.startQuery('list_tables', 'SHOW TABLES'); Date.now.mockReturnValue(6000); monitor.endQuery(queryId2, { recordset: [] }); const agg = monitor.metrics.aggregates; expect(agg.totalQueries).toBe(2); expect(agg.totalQueryTime).toBe(3000); // 1000 + 2000 expect(agg.avgQueryTime).toBe(1500); // 3000 / 2 expect(agg.maxQueryTime).toBe(2000); expect(agg.minQueryTime).toBe(1000); }); test('should calculate error rate correctly', () => { // Success const queryId1 = monitor.startQuery('execute_query', 'SELECT 1'); monitor.endQuery(queryId1, { recordset: [] }); // Error const queryId2 = monitor.startQuery('execute_query', 'INVALID'); monitor.endQuery(queryId2, {}, new Error('Error')); // Success const queryId3 = monitor.startQuery('execute_query', 'SELECT 2'); monitor.endQuery(queryId3, { recordset: [] }); const agg = monitor.metrics.aggregates; expect(agg.totalQueries).toBe(3); expect(Math.round(agg.errorRate * 100) / 100).toBe(0.33); // 1/3 ≈ 0.33 }); }); describe('Connection Pool Metrics', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should record pool metrics', () => { const poolStats = { totalConnections: 10, activeConnections: 3, idleConnections: 7, pendingRequests: 2, errors: 1 }; monitor.recordPoolMetrics(poolStats); expect(monitor.metrics.poolStats).toEqual({ ...poolStats, timestamp: 2000, uptime: 1000 // 2000 - 1000 (startTime) }); expect(monitor.metrics.connections).toHaveLength(1); expect(monitor.metrics.connections[0]).toEqual({ timestamp: 2000, ...poolStats }); }); test('should not record pool metrics when disabled', () => { monitor = new PerformanceMonitor({ trackPoolMetrics: false }); monitor.recordPoolMetrics({ totalConnections: 5 }); expect(monitor.metrics.connections).toHaveLength(0); }); test('should not record pool metrics when monitor disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); monitor.recordPoolMetrics({ totalConnections: 5 }); expect(monitor.metrics.connections).toHaveLength(0); }); }); describe('Connection Events', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should record connection event', () => { monitor.recordConnectionEvent('connect', { host: 'localhost', port: 1433, duration: 150 }); expect(monitor.metrics.connections).toHaveLength(1); expect(monitor.metrics.connections[0]).toEqual({ id: expect.any(String), event: 'connect', timestamp: 2000, host: 'localhost', port: 1433, duration: 150 }); // Should update pool stats expect(monitor.metrics.poolStats.totalConnections).toBe(1); expect(monitor.metrics.poolStats.activeConnections).toBe(1); }); test('should handle disconnect event', () => { monitor.recordConnectionEvent('disconnect', { reason: 'timeout' }); expect(monitor.metrics.poolStats.activeConnections).toBe(-1); expect(monitor.metrics.connections[0]).toEqual({ id: expect.any(String), event: 'disconnect', timestamp: 2000, reason: 'timeout' }); }); test('should handle error event', () => { monitor.recordConnectionEvent('error', { message: 'Connection lost' }); expect(monitor.metrics.poolStats.errors).toBe(1); expect(monitor.metrics.connections[0]).toEqual({ id: expect.any(String), event: 'error', timestamp: 2000, message: 'Connection lost' }); }); test('should not record connection events when disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); monitor.recordConnectionEvent('connect'); expect(monitor.metrics.connections).toHaveLength(0); expect(monitor.metrics.poolStats.totalConnections).toBe(0); }); }); describe('Statistics Retrieval', () => { beforeEach(() => { monitor = new PerformanceMonitor(); // Add some test data const queryId = monitor.startQuery('execute_query', 'SELECT * FROM test'); Date.now.mockReturnValue(4000); // 2000ms duration monitor.endQuery(queryId, { recordset: [1, 2, 3] }); monitor.recordPoolMetrics({ totalConnections: 5, activeConnections: 2, idleConnections: 3, pendingRequests: 0, errors: 0 }); }); test('should get overall statistics', () => { const stats = monitor.getStats(); expect(stats).toEqual({ enabled: true, uptime: 3000, // Mock Date.now() - startTime overall: monitor.metrics.aggregates, recent: expect.objectContaining({ count: 1, avgDuration: 2000, maxDuration: 2000, minDuration: 2000, errorRate: 0, slowQueryRate: 0 }), pool: monitor.metrics.poolStats, monitoring: { totalQueriesTracked: 1, totalConnectionEvents: 1, samplingRate: 1.0, slowQueryThreshold: 5000 } }); }); test('should return disabled status when monitoring disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); const stats = monitor.getStats(); expect(stats).toEqual({ enabled: false }); }); test('should filter recent queries correctly', () => { // Add an old query (older than 5 minutes) Date.now.mockReturnValue(1000); // Very old timestamp const oldQueryId = monitor.startQuery('old_query', 'SELECT OLD'); Date.now.mockReturnValue(1100); monitor.endQuery(oldQueryId, { recordset: [] }); // Current time is much later Date.now.mockReturnValue(600000); // 10 minutes later const stats = monitor.getStats(); expect(stats.recent.count).toBe(0); // Old query should be filtered out }); }); describe('Query Statistics', () => { beforeEach(() => { monitor = new PerformanceMonitor(); // Add test queries Date.now.mockReturnValue(2000); const query1 = monitor.startQuery('execute_query', 'SELECT 1'); Date.now.mockReturnValue(3000); // 1000ms duration monitor.endQuery(query1, { recordset: [1] }); Date.now.mockReturnValue(4000); const query2 = monitor.startQuery('list_tables', 'SHOW TABLES'); Date.now.mockReturnValue(5500); // 1500ms duration monitor.endQuery(query2, { recordset: [1, 2] }); Date.now.mockReturnValue(6000); const query3 = monitor.startQuery('execute_query', 'INVALID SQL'); Date.now.mockReturnValue(6200); // 200ms duration monitor.endQuery(query3, {}, new Error('SQL Error')); }); test('should get query statistics', () => { const queryStats = monitor.getQueryStats(10); expect(queryStats.enabled).toBe(true); expect(queryStats.queries).toHaveLength(3); expect(queryStats.byTool).toEqual({ execute_query: { count: 2, totalTime: 1200, // 1000 + 200 errors: 1, slowQueries: 0, avgTime: 600, errorRate: 50, slowQueryRate: 0 }, list_tables: { count: 1, totalTime: 1500, errors: 0, slowQueries: 0, avgTime: 1500, errorRate: 0, slowQueryRate: 0 } }); }); test('should limit query results', () => { const queryStats = monitor.getQueryStats(1); expect(queryStats.queries).toHaveLength(1); // Should return the most recent query expect(queryStats.queries[0].tool).toBe('execute_query'); }); test('should identify slow queries', () => { monitor = new PerformanceMonitor({ slowQueryThreshold: 1200 }); // Re-run with slow query threshold Date.now.mockReturnValue(2000); const query1 = monitor.startQuery('execute_query', 'SELECT 1'); Date.now.mockReturnValue(4000); // 2000ms duration > 1200ms threshold monitor.endQuery(query1, { recordset: [1] }); const queryStats = monitor.getQueryStats(10); expect(queryStats.byTool.execute_query.slowQueries).toBe(1); expect(queryStats.byTool.execute_query.slowQueryRate).toBe(100); expect(queryStats.slowQueries).toHaveLength(1); }); test('should return disabled status when monitoring disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); const queryStats = monitor.getQueryStats(); expect(queryStats).toEqual({ enabled: false }); }); }); describe('Pool Health Assessment', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should assess healthy pool', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 3, idleConnections: 7, pendingRequests: 1, errors: 0 }; const health = monitor.assessPoolHealth(); expect(health.status).toBe('healthy'); expect(health.issues).toHaveLength(0); expect(health.score).toBe(100); }); test('should detect high error count', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 3, idleConnections: 7, pendingRequests: 1, errors: 15 }; const health = monitor.assessPoolHealth(); expect(health.status).toBe('warning'); expect(health.issues).toContain('High error count detected'); expect(health.score).toBeLessThan(100); }); test('should detect connection pool near capacity', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 10, // 100% utilization (>= 95% threshold) idleConnections: 0, pendingRequests: 1, errors: 0 }; const health = monitor.assessPoolHealth(); expect(health.status).toBe('warning'); expect(health.issues).toContain('Connection pool near capacity'); }); test('should detect high pending requests', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 5, idleConnections: 5, pendingRequests: 10, errors: 0 }; const health = monitor.assessPoolHealth(); expect(health.status).toBe('warning'); expect(health.issues).toContain('High number of pending requests'); }); test('should detect critical no active connections', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 0, idleConnections: 0, pendingRequests: 5, errors: 0 }; const health = monitor.assessPoolHealth(); expect(health.status).toBe('critical'); expect(health.issues).toContain('No active connections available'); }); test('should calculate health score correctly', () => { monitor.metrics.poolStats = { totalConnections: 10, activeConnections: 10, // 100% utilization (>= 95% threshold) idleConnections: 0, pendingRequests: 8, // High pending requests errors: 3 // Some errors }; const health = monitor.assessPoolHealth(); // Calculate the actual score with 95% threshold: // Starting at 100 // - 20 * 2 (issues: 'Connection pool near capacity' + 'High number of pending requests') = -40 // - 10 (utilization > 0.8) = -10 // - 10 (utilization > 0.9) = -10 // - min(3 * 2, 30) = -6 (errors) // Total: 100 - 40 - 10 - 10 - 6 = 34 expect(health.score).toBe(34); }); }); describe('Pool Statistics', () => { beforeEach(() => { monitor = new PerformanceMonitor(); // Add connection events Date.now.mockReturnValue(1000); monitor.recordConnectionEvent('connect'); Date.now.mockReturnValue(2000); monitor.recordConnectionEvent('error', { message: 'Connection timeout' }); Date.now.mockReturnValue(3000); monitor.recordConnectionEvent('retry'); Date.now.mockReturnValue(700000); // 10+ minutes later for filtering }); test('should get pool statistics', () => { const poolStats = monitor.getPoolStats(); expect(poolStats.enabled).toBe(true); expect(poolStats.current).toBeDefined(); expect(poolStats.recent).toEqual({ connectionRate: 0, // Events are from too long ago (700000 - 3000 > 10 minutes) errorRate: 0, retryRate: 0, totalEvents: 0 }); expect(poolStats.health).toBeDefined(); }); test('should filter old connection events', () => { // All events are older than 10 minutes, so should be filtered out const poolStats = monitor.getPoolStats(); expect(poolStats.recent.totalEvents).toBe(0); expect(poolStats.recent.connectionRate).toBe(0); }); test('should return disabled status when tracking disabled', () => { monitor = new PerformanceMonitor({ trackPoolMetrics: false }); const poolStats = monitor.getPoolStats(); expect(poolStats).toEqual({ enabled: false }); }); test('should return disabled status when monitoring disabled', () => { monitor = new PerformanceMonitor({ enabled: false }); const poolStats = monitor.getPoolStats(); expect(poolStats).toEqual({ enabled: false }); }); }); describe('Memory Management', () => { beforeEach(() => { monitor = new PerformanceMonitor({ maxMetricsHistory: 5 }); }); test('should trim metrics history when limit exceeded', () => { // Add 7 queries (exceeds limit of 5) for (let i = 0; i < 7; i++) { Date.now.mockReturnValue(1000 + i * 100); const queryId = monitor.startQuery('execute_query', `SELECT ${i}`); Date.now.mockReturnValue(1000 + i * 100 + 50); monitor.endQuery(queryId, { recordset: [] }); } expect(monitor.metrics.queries.length).toBeLessThanOrEqual(5); }); test('should preserve slow queries when trimming', () => { monitor = new PerformanceMonitor({ maxMetricsHistory: 3, slowQueryThreshold: 100 }); // Add regular queries for (let i = 0; i < 3; i++) { Date.now.mockReturnValue(1000 + i * 100); const queryId = monitor.startQuery('execute_query', `SELECT ${i}`); Date.now.mockReturnValue(1000 + i * 100 + 50); // 50ms - fast monitor.endQuery(queryId, { recordset: [] }); } // Add slow query Date.now.mockReturnValue(2000); const slowQueryId = monitor.startQuery('execute_query', 'SLOW SELECT'); Date.now.mockReturnValue(2500); // 500ms - slow monitor.endQuery(slowQueryId, { recordset: [] }); // Add more regular queries to trigger trimming for (let i = 0; i < 2; i++) { Date.now.mockReturnValue(3000 + i * 100); const queryId = monitor.startQuery('execute_query', `SELECT fast ${i}`); Date.now.mockReturnValue(3000 + i * 100 + 50); // 50ms - fast monitor.endQuery(queryId, { recordset: [] }); } expect(monitor.metrics.queries.length).toBeLessThanOrEqual(3); // Should preserve the slow query const slowQuery = monitor.metrics.queries.find(q => q.query === 'SLOW SELECT'); expect(slowQuery).toBeDefined(); }); test('should trim connection history when limit exceeded', () => { // Add connection events exceeding limit for (let i = 0; i < 7; i++) { monitor.recordConnectionEvent('connect', { attempt: i }); } expect(monitor.metrics.connections.length).toBeLessThanOrEqual(5); }); }); describe('Memory Usage Tracking', () => { test('should get memory usage when process is available', () => { const memory = monitor.getMemoryUsage(); expect(memory).toBe(100); // Mock returns 100MB }); test('should return 0 when process is not available', () => { global.process = undefined; monitor = new PerformanceMonitor(); const memory = monitor.getMemoryUsage(); expect(memory).toBe(0); }); }); describe('Configuration Management', () => { beforeEach(() => { monitor = new PerformanceMonitor({ enabled: true, slowQueryThreshold: 1000 }); }); test('should update configuration', () => { const newConfig = { slowQueryThreshold: 2000, samplingRate: 0.5, newOption: 'test' }; monitor.updateConfig(newConfig); expect(monitor.config).toEqual({ enabled: true, maxMetricsHistory: 1000, slowQueryThreshold: 2000, trackPoolMetrics: true, samplingRate: 0.5, newOption: 'test' }); }); test('should get current configuration', () => { const config = monitor.getConfig(); expect(config).toEqual(monitor.config); expect(config).not.toBe(monitor.config); // Should return a copy }); }); describe('Reset Functionality', () => { beforeEach(() => { monitor = new PerformanceMonitor(); // Add some data const queryId = monitor.startQuery('execute_query', 'SELECT 1'); monitor.endQuery(queryId, { recordset: [] }); monitor.recordConnectionEvent('connect'); }); test('should reset all metrics', () => { Date.now.mockReturnValue(5000); // New start time monitor.reset(); expect(monitor.metrics.queries).toHaveLength(0); expect(monitor.metrics.connections).toHaveLength(0); expect(monitor.metrics.poolStats).toEqual({ totalConnections: 0, activeConnections: 0, idleConnections: 0, pendingRequests: 0, errors: 0 }); expect(monitor.metrics.aggregates).toEqual({ totalQueries: 0, slowQueries: 0, avgQueryTime: 0, maxQueryTime: 0, minQueryTime: Number.MAX_SAFE_INTEGER, totalQueryTime: 0, errorRate: 0 }); expect(monitor.startTime).toBe(5000); }); }); describe('Report Generation', () => { beforeEach(() => { monitor = new PerformanceMonitor({ slowQueryThreshold: 1000 }); // Add test data Date.now.mockReturnValue(2000); const queryId = monitor.startQuery('execute_query', 'SELECT * FROM users'); Date.now.mockReturnValue(4000); // 2000ms duration (slow) monitor.endQuery(queryId, { recordset: [1, 2, 3] }); monitor.recordPoolMetrics({ totalConnections: 10, activeConnections: 8, errors: 2 }); }); test('should generate comprehensive report', () => { const report = monitor.generateReport(); expect(report).toEqual({ timestamp: 4000, uptime: 3000, summary: { totalQueries: 1, avgQueryTime: 2000, slowQueries: 1, errorRate: 0, poolHealth: 'healthy' // Pool health calculation is different }, detailed: { overall: monitor.metrics.aggregates, recent: expect.any(Object), pool: expect.any(Object), queries: expect.any(Object) }, recommendations: expect.arrayContaining([ expect.objectContaining({ type: 'performance', priority: 'high', message: 'Average query time is high. Consider query optimization or indexing.' }) ]) }); }); test('should generate performance recommendations', () => { // Test high error rate Date.now.mockReturnValue(3000); const errorQuery = monitor.startQuery('execute_query', 'INVALID'); monitor.endQuery(errorQuery, {}, new Error('SQL Error')); const report = monitor.generateReport(); expect(report.recommendations).toEqual( expect.arrayContaining([ expect.objectContaining({ type: 'performance', priority: 'high', message: 'Average query time is high. Consider query optimization or indexing.' }) ]) ); // Pool health might not be warning if error count is low }); test('should generate tool-specific recommendations', () => { // Add a slow tool Date.now.mockReturnValue(5000); const slowToolQuery = monitor.startQuery('export_table_csv', 'SELECT * FROM huge_table'); Date.now.mockReturnValue(8000); // 3000ms duration monitor.endQuery(slowToolQuery, { recordset: [], streaming: true }); const report = monitor.generateReport(); expect(report.recommendations).toEqual( expect.arrayContaining([ expect.objectContaining({ type: 'optimization', priority: 'medium', message: "Tool 'export_table_csv' has high average execution time.", tool: 'export_table_csv' }) ]) ); }); }); describe('Edge Cases and Error Handling', () => { beforeEach(() => { monitor = new PerformanceMonitor(); }); test('should handle empty query statistics calculation', () => { const stats = monitor.calculateQueryStats([]); expect(stats).toEqual({ count: 0, avgDuration: 0, maxDuration: 0, minDuration: 0, errorRate: 0, slowQueryRate: 0 }); }); test('should handle queries without duration', () => { const queries = [ { duration: null, status: 'error' }, { duration: undefined, status: 'completed' } ]; const stats = monitor.calculateQueryStats(queries); expect(stats.count).toBe(2); expect(stats.avgDuration).toBe(0); expect(stats.errorRate).toBe(50); }); test('should generate unique query IDs', () => { Math.random.mockReturnValueOnce(0.123).mockReturnValueOnce(0.456).mockReturnValueOnce(0.789); const id1 = monitor.generateQueryId(); const id2 = monitor.generateQueryId(); expect(id1).not.toBe(id2); expect(id1).toMatch(/^q_\d+_[a-z0-9]+$/); }); test('should generate unique event IDs', () => { Math.random.mockReturnValueOnce(0.123).mockReturnValueOnce(0.456); const id1 = monitor.generateEventId(); const id2 = monitor.generateEventId(); expect(id1).not.toBe(id2); expect(id1).toMatch(/^e_\d+_[a-z0-9]+$/); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/egarcia74/warp-sql-server-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server