import { performanceService, PERFORMANCE_THRESHOLDS } from '../performance'; describe('PerformanceService', () => { beforeEach(() => { performanceService.clear(); }); describe('PERFORMANCE_THRESHOLDS', () => { it('should have correct threshold values', () => { expect(PERFORMANCE_THRESHOLDS.appStartup).toBe(3000); expect(PERFORMANCE_THRESHOLDS.bleOperation).toBe(10000); expect(PERFORMANCE_THRESHOLDS.apiCall).toBe(2000); }); }); describe('App Startup Tracking', () => { it('should track app startup duration', () => { performanceService.markAppStart(); // Simulate some delay const startTime = Date.now(); while (Date.now() - startTime < 50) { // Wait 50ms } const duration = performanceService.markAppReady(); expect(duration).toBeGreaterThanOrEqual(50); expect(duration).toBeLessThan(200); }); it('should return startup duration via getAppStartupDuration', () => { performanceService.markAppStart(); performanceService.markAppReady(); const duration = performanceService.getAppStartupDuration(); expect(duration).not.toBeNull(); expect(duration).toBeGreaterThanOrEqual(0); }); it('should return null if app not started', () => { expect(performanceService.getAppStartupDuration()).toBeNull(); }); it('should record startup in metrics', () => { performanceService.markAppStart(); performanceService.markAppReady(); const metrics = performanceService.getMetricsByName('app_startup'); expect(metrics).toHaveLength(1); expect(metrics[0].name).toBe('app_startup'); }); it('should mark startup as success when within threshold', () => { performanceService.markAppStart(); const duration = performanceService.markAppReady(); const metrics = performanceService.getMetricsByName('app_startup'); // Test starts almost immediately, so should be within threshold expect(metrics[0].success).toBe(true); expect(metrics[0].metadata?.withinTarget).toBe(true); }); }); describe('Timer Operations', () => { it('should track operation duration with timer', () => { performanceService.startTimer('test_operation'); const startTime = Date.now(); while (Date.now() - startTime < 50) { // Wait 50ms } const duration = performanceService.endTimer('test_operation'); expect(duration).toBeGreaterThanOrEqual(50); expect(duration).toBeLessThan(200); }); it('should return 0 for non-existent timer', () => { const duration = performanceService.endTimer('non_existent'); expect(duration).toBe(0); }); it('should record timer metrics', () => { performanceService.startTimer('my_op'); performanceService.endTimer('my_op', true, { custom: 'data' }); const metrics = performanceService.getMetrics(); expect(metrics).toHaveLength(1); expect(metrics[0].name).toBe('my_op'); expect(metrics[0].success).toBe(true); expect(metrics[0].metadata?.custom).toBe('data'); }); it('should record failed operations', () => { performanceService.startTimer('failed_op'); performanceService.endTimer('failed_op', false); const metrics = performanceService.getMetrics(); expect(metrics[0].success).toBe(false); }); }); describe('BLE Operation Tracking', () => { it('should track BLE operations', () => { performanceService.trackBleOperation('scan', 5000, true); performanceService.trackBleOperation('connect', 2000, true); performanceService.trackBleOperation('command', 500, true); const metrics = performanceService.getMetricsByName(/^ble_/); expect(metrics).toHaveLength(3); }); it('should mark BLE operation as within target when under 10s', () => { performanceService.trackBleOperation('scan', 5000, true); const metrics = performanceService.getMetricsByName('ble_scan'); expect(metrics[0].metadata?.withinTarget).toBe(true); }); it('should mark BLE operation as over target when over 10s', () => { performanceService.trackBleOperation('wifi_config', 12000, true); const metrics = performanceService.getMetricsByName('ble_wifi_config'); expect(metrics[0].metadata?.withinTarget).toBe(false); }); it('should track failed BLE operations', () => { performanceService.trackBleOperation('connect', 5000, false, { deviceId: 'test' }); const metrics = performanceService.getMetricsByName('ble_connect'); expect(metrics[0].success).toBe(false); expect(metrics[0].metadata?.deviceId).toBe('test'); }); }); describe('API Call Tracking', () => { it('should track API calls', () => { performanceService.trackApiCall('auth/me', 500, true); performanceService.trackApiCall('beneficiaries', 800, true); const metrics = performanceService.getMetricsByName(/^api_/); expect(metrics).toHaveLength(2); }); it('should mark API call as within target when under 2s', () => { performanceService.trackApiCall('auth/me', 500, true); const metrics = performanceService.getMetricsByName('api_auth/me'); expect(metrics[0].metadata?.withinTarget).toBe(true); }); it('should mark API call as over target when over 2s', () => { performanceService.trackApiCall('slow_endpoint', 3000, true); const metrics = performanceService.getMetricsByName('api_slow_endpoint'); expect(metrics[0].metadata?.withinTarget).toBe(false); }); }); describe('Metrics Aggregation', () => { it('should calculate average duration', () => { performanceService.trackBleOperation('connect', 1000, true); performanceService.trackBleOperation('connect', 2000, true); performanceService.trackBleOperation('connect', 3000, true); const avg = performanceService.getAverageDuration('ble_connect'); expect(avg).toBe(2000); }); it('should return null for non-existent metrics', () => { const avg = performanceService.getAverageDuration('non_existent'); expect(avg).toBeNull(); }); it('should get summary stats', () => { performanceService.markAppStart(); performanceService.markAppReady(); performanceService.trackBleOperation('scan', 5000, true); performanceService.trackBleOperation('connect', 2000, false); performanceService.trackApiCall('auth/me', 500, true); const summary = performanceService.getSummary(); expect(summary.appStartup.duration).toBeGreaterThanOrEqual(0); expect(summary.appStartup.withinTarget).toBe(true); expect(summary.bleOperations.count).toBe(2); expect(summary.bleOperations.avgDuration).toBe(3500); expect(summary.bleOperations.successRate).toBe(0.5); expect(summary.apiCalls.count).toBe(1); expect(summary.apiCalls.avgDuration).toBe(500); expect(summary.apiCalls.successRate).toBe(1); }); }); describe('Memory Management', () => { it('should keep only last 100 metrics', () => { for (let i = 0; i < 150; i++) { performanceService.trackApiCall(`endpoint_${i}`, 100, true); } const metrics = performanceService.getMetrics(); expect(metrics).toHaveLength(100); // Should have the most recent metrics expect(metrics[99].name).toBe('api_endpoint_149'); }); it('should clear all metrics', () => { performanceService.markAppStart(); performanceService.markAppReady(); performanceService.trackBleOperation('scan', 5000, true); performanceService.clear(); expect(performanceService.getMetrics()).toHaveLength(0); expect(performanceService.getAppStartupDuration()).toBeNull(); }); }); describe('Metrics Filtering', () => { it('should filter by string pattern', () => { performanceService.trackBleOperation('scan', 5000, true); performanceService.trackBleOperation('connect', 2000, true); performanceService.trackApiCall('auth/me', 500, true); const bleMetrics = performanceService.getMetricsByName('ble_'); expect(bleMetrics).toHaveLength(2); }); it('should filter by regex pattern', () => { performanceService.trackBleOperation('scan', 5000, true); performanceService.trackBleOperation('connect', 2000, true); performanceService.trackApiCall('auth/me', 500, true); const bleMetrics = performanceService.getMetricsByName(/^ble_/); expect(bleMetrics).toHaveLength(2); const apiMetrics = performanceService.getMetricsByName(/^api_/); expect(apiMetrics).toHaveLength(1); }); }); }); describe('Performance Targets', () => { beforeEach(() => { performanceService.clear(); }); it('should verify app startup target is 3 seconds', () => { expect(PERFORMANCE_THRESHOLDS.appStartup).toBe(3000); }); it('should verify BLE operation target is 10 seconds', () => { expect(PERFORMANCE_THRESHOLDS.bleOperation).toBe(10000); }); it('should correctly identify performance violations', () => { // App startup violation performanceService.markAppStart(); // Simulate slow startup (for testing, we just track the metric directly) // In real scenario, markAppReady would measure actual time // BLE operation within target performanceService.trackBleOperation('scan', 8000, true); const scanMetrics = performanceService.getMetricsByName('ble_scan'); expect(scanMetrics[0].metadata?.withinTarget).toBe(true); // BLE operation over target performanceService.trackBleOperation('bulk_wifi', 15000, true); const bulkMetrics = performanceService.getMetricsByName('ble_bulk_wifi'); expect(bulkMetrics[0].metadata?.withinTarget).toBe(false); }); });