tracker = new NestedPerformanceTracker( new SystemClock(), new SystemHighResolutionClock(), new MemoryMonitor() ); $this->profiler = new ActionProfiler($this->tracker); $this->componentId = ComponentId::create('test-component', 'instance-1'); }); it('profiles CSRF validation', function () { $this->profiler->profileCsrfValidation( $this->componentId, 'testAction', function () { usleep(1000); // 1ms return true; } ); $timeline = $this->tracker->generateTimeline(); expect($timeline)->not->toBeEmpty(); $csrfEvents = array_filter($timeline, fn($e) => str_contains($e['name'], 'csrf')); expect($csrfEvents)->not->toBeEmpty(); $event = array_values($csrfEvents)[0]; expect($event['duration_ms'])->toBeGreaterThan(0.5); expect($event)->toHaveKey('context'); expect($event['context'])->toHaveKey('component'); expect($event['context']['component'])->toBe('test-component'); }); it('profiles authorization check', function () { $this->profiler->profileAuthorizationCheck( $this->componentId, 'testAction', function () { usleep(500); // 0.5ms return true; } ); $timeline = $this->tracker->generateTimeline(); $authEvents = array_filter($timeline, fn($e) => str_contains($e['name'], 'authorization')); expect($authEvents)->not->toBeEmpty(); $event = array_values($authEvents)[0]; expect($event['context'])->toHaveKey('action'); expect($event['context']['action'])->toBe('testAction'); }); it('profiles rate limit check', function () { $this->profiler->profileRateLimitCheck( $this->componentId, 'testAction', function () { usleep(300); // 0.3ms return true; } ); $timeline = $this->tracker->generateTimeline(); $rateLimitEvents = array_filter($timeline, fn($e) => str_contains($e['name'], 'rateLimit')); expect($rateLimitEvents)->not->toBeEmpty(); }); it('profiles parameter binding', function () { $this->profiler->profileParameterBinding( $this->componentId, 'testAction', function () { usleep(2000); // 2ms - reflection overhead return ['param1' => 'value1']; } ); $timeline = $this->tracker->generateTimeline(); $bindingEvents = array_filter($timeline, fn($e) => str_contains($e['name'], 'binding')); expect($bindingEvents)->not->toBeEmpty(); $event = array_values($bindingEvents)[0]; expect($event['duration_ms'])->toBeGreaterThan(1); }); it('profiles idempotency check', function () { $this->profiler->profileIdempotencyCheck( $this->componentId, 'testAction', function () { usleep(400); // 0.4ms - cache lookup return null; // No cached result } ); $timeline = $this->tracker->generateTimeline(); $idempotencyEvents = array_filter($timeline, fn($e) => str_contains($e['name'], 'idempotency')); expect($idempotencyEvents)->not->toBeEmpty(); }); it('aggregates action metrics across multiple executions', function () { $component1 = ComponentId::create('counter', 'inst-1'); // Execute multiple actions WITHOUT reset to accumulate metrics for ($i = 0; $i < 3; $i++) { $this->profiler->profileCsrfValidation($component1, 'increment', fn() => usleep(100)); $this->profiler->profileAuthorizationCheck($component1, 'increment', fn() => usleep(50)); } // Get metrics for specific action $counterMetrics = $this->profiler->getActionMetrics('counter', 'increment'); expect($counterMetrics)->toHaveKey('component'); expect($counterMetrics)->toHaveKey('action'); expect($counterMetrics)->toHaveKey('phase_timings'); expect($counterMetrics['component'])->toBe('counter'); expect($counterMetrics['action'])->toBe('increment'); // Verify we tracked multiple phases expect($counterMetrics['phase_timings'])->not->toBeEmpty(); }); it('provides component-level performance summary', function () { $component = ComponentId::create('user-profile', 'user-123'); // Multiple actions on same component WITHOUT reset $this->profiler->profileCsrfValidation($component, 'update', fn() => usleep(100)); $this->profiler->profileParameterBinding($component, 'update', fn() => usleep(200)); $this->profiler->profileCsrfValidation($component, 'delete', fn() => usleep(100)); $this->profiler->profileAuthorizationCheck($component, 'delete', fn() => usleep(150)); $summary = $this->profiler->getComponentSummary('user-profile'); expect($summary)->toHaveKey('component'); expect($summary)->toHaveKey('total_operations'); expect($summary)->toHaveKey('total_time_ms'); expect($summary)->toHaveKey('operations'); expect($summary['component'])->toBe('user-profile'); expect($summary['total_operations'])->toBeGreaterThan(0); expect($summary['operations'])->toBeArray(); }); it('generates system-wide performance report', function () { $comp1 = ComponentId::create('component-a', 'a-1'); $comp2 = ComponentId::create('component-b', 'b-1'); $comp3 = ComponentId::create('component-c', 'c-1'); // Simulate multiple components and actions WITHOUT reset $this->profiler->profileCsrfValidation($comp1, 'action1', fn() => usleep(100)); $this->profiler->profileAuthorizationCheck($comp2, 'action2', fn() => usleep(150)); $this->profiler->profileRateLimitCheck($comp3, 'action3', fn() => usleep(75)); $report = $this->profiler->generatePerformanceReport(); expect($report)->toHaveKey('generated_at'); expect($report)->toHaveKey('total_components'); expect($report)->toHaveKey('total_operations'); expect($report)->toHaveKey('components'); expect($report['total_components'])->toBe(3); expect($report['components'])->toHaveKey('component-a'); expect($report['components'])->toHaveKey('component-b'); expect($report['components'])->toHaveKey('component-c'); }); it('tracks execution times for action phases', function () { $component = ComponentId::create('fast-component', 'fast-1'); // Execute action multiple times for ($i = 0; $i < 3; $i++) { $this->profiler->profileCsrfValidation($component, 'quickAction', fn() => usleep(100)); } $metrics = $this->profiler->getActionMetrics('fast-component', 'quickAction'); expect($metrics)->toHaveKey('phase_timings'); expect($metrics['phase_timings'])->not->toBeEmpty(); // Verify phase timing structure $firstPhase = array_values($metrics['phase_timings'])[0]; expect($firstPhase)->toHaveKey('count'); expect($firstPhase)->toHaveKey('total_ms'); expect($firstPhase)->toHaveKey('avg_ms'); expect($firstPhase['count'])->toBe(3); }); it('calculates average execution time for phases', function () { $component = ComponentId::create('slow-component', 'slow-1'); // Execute multiple times for ($i = 0; $i < 3; $i++) { $this->profiler->profileParameterBinding($component, 'complexAction', fn() => usleep(1000)); } $metrics = $this->profiler->getActionMetrics('slow-component', 'complexAction'); expect($metrics)->toHaveKey('phase_timings'); expect($metrics['phase_timings'])->not->toBeEmpty(); // Verify average is calculated correctly $firstPhase = array_values($metrics['phase_timings'])[0]; expect($firstPhase['avg_ms'])->toBeGreaterThan(0); expect($firstPhase['avg_ms'])->toBe($firstPhase['total_ms'] / $firstPhase['count']); }); it('tracks total execution count per phase', function () { $component = ComponentId::create('counted-component', 'count-1'); // Execute 5 times WITHOUT reset for ($i = 0; $i < 5; $i++) { $this->profiler->profileCsrfValidation($component, 'countedAction', fn() => usleep(100)); } $metrics = $this->profiler->getActionMetrics('counted-component', 'countedAction'); expect($metrics)->toHaveKey('phase_timings'); expect($metrics['phase_timings'])->not->toBeEmpty(); // Verify count tracking $firstPhase = array_values($metrics['phase_timings'])[0]; expect($firstPhase['count'])->toBe(5); }); it('handles components with no profiled actions gracefully', function () { $summary = $this->profiler->getComponentSummary('non-existent-component'); expect($summary['component'])->toBe('non-existent-component'); expect($summary['total_operations'])->toBe(0); expect($summary['total_time_ms'])->toBe(0); expect($summary['operations'])->toBe([]); }); it('handles actions with no profiled phases gracefully', function () { $metrics = $this->profiler->getActionMetrics('non-existent', 'action'); expect($metrics['component'])->toBe('non-existent'); expect($metrics['action'])->toBe('action'); expect($metrics['executions'])->toBe(0); expect($metrics['metrics'])->toBe([]); }); it('measures overhead of profiling system', function () { $component = ComponentId::create('overhead-test', 'ov-1'); // Measure baseline (no profiling) - execute noop lambda $baselineStart = microtime(true); for ($i = 0; $i < 100; $i++) { $noop = function () {}; // Minimal work $noop(); } $baselineDuration = (microtime(true) - $baselineStart) * 1000; // Measure with profiling $this->tracker->reset(); $profiledStart = microtime(true); for ($i = 0; $i < 100; $i++) { $this->profiler->profileCsrfValidation($component, 'noop', fn() => null); } $profiledDuration = (microtime(true) - $profiledStart) * 1000; // Overhead should be reasonable (<5ms for 100 operations) $overhead = $profiledDuration - $baselineDuration; expect($overhead)->toBeLessThan(10); // Maximum 10ms overhead for 100 operations }); it('includes context data in all profiled operations', function () { $component = ComponentId::create('context-test', 'ctx-1'); $this->profiler->profileCsrfValidation($component, 'contextAction', fn() => usleep(100)); $timeline = $this->tracker->generateTimeline(); foreach ($timeline as $event) { expect($event)->toHaveKey('context'); expect($event['context'])->toHaveKey('component'); expect($event['context'])->toHaveKey('action'); } }); });