toBeInstanceOf(ComponentMetricsCollector::class); }); it('starts with empty metrics', function () { $collector = new ComponentMetricsCollector(); $metrics = $collector->getMetrics(); expect($metrics)->toBeArray(); expect(count($metrics))->toBe(0); }); it('records render metrics', function () { $collector = new ComponentMetricsCollector(); $collector->recordRender('comp-123', 45.5, false); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records action execution', function () { $collector = new ComponentMetricsCollector(); $collector->recordAction('comp-123', 'handleClick', 25.5, true); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records cache hits', function () { $collector = new ComponentMetricsCollector(); $collector->recordCacheHit('comp-123', true); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records cache misses', function () { $collector = new ComponentMetricsCollector(); $collector->recordCacheHit('comp-123', false); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records event dispatching', function () { $collector = new ComponentMetricsCollector(); $collector->recordEventDispatched('comp-123', 'user:updated'); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records event receiving', function () { $collector = new ComponentMetricsCollector(); $collector->recordEventReceived('comp-123', 'data:loaded'); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records hydration time', function () { $collector = new ComponentMetricsCollector(); $collector->recordHydration('comp-123', 15.5); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records batch operations', function () { $collector = new ComponentMetricsCollector(); $collector->recordBatch(10, 125.5, 8, 2); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records fragment updates', function () { $collector = new ComponentMetricsCollector(); $collector->recordFragmentUpdate('comp-123', 3, 45.5); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records upload chunks', function () { $collector = new ComponentMetricsCollector(); $collector->recordUploadChunk('session-abc', 0, 125.5, true); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('records upload completion', function () { $collector = new ComponentMetricsCollector(); $collector->recordUploadComplete('session-abc', 1500.5, 5); $metrics = $collector->getMetrics(); expect(count($metrics))->toBeGreaterThan(0); }); it('generates summary statistics', function () { $collector = new ComponentMetricsCollector(); $collector->recordRender('comp-1', 45.5, false); $collector->recordAction('comp-1', 'handleClick', 25.5, true); $collector->recordCacheHit('comp-1', true); $summary = $collector->getSummary(); expect($summary)->toBeArray(); expect(isset($summary['total_renders']))->toBeTrue(); expect(isset($summary['total_actions']))->toBeTrue(); expect(isset($summary['cache_hits']))->toBeTrue(); expect(isset($summary['cache_misses']))->toBeTrue(); expect(isset($summary['cache_hit_rate']))->toBeTrue(); }); it('exports Prometheus format', function () { $collector = new ComponentMetricsCollector(); $collector->recordRender('comp-1', 45.5, false); $collector->recordAction('comp-1', 'handleClick', 25.5, true); $prometheus = $collector->exportPrometheus(); expect($prometheus)->toBeString(); expect(str_contains($prometheus, '# HELP'))->toBeTrue(); expect(str_contains($prometheus, 'livecomponent_'))->toBeTrue(); }); it('can reset metrics', function () { $collector = new ComponentMetricsCollector(); $collector->recordRender('comp-1', 45.5, false); expect(count($collector->getMetrics()))->toBeGreaterThan(0); $collector->reset(); expect(count($collector->getMetrics()))->toBe(0); }); it('calculates cache hit rate correctly', function () { $collector = new ComponentMetricsCollector(); // 2 hits, 1 miss = 66.7% hit rate $collector->recordCacheHit('comp-1', true); $collector->recordCacheHit('comp-2', true); $collector->recordCacheHit('comp-3', false); $summary = $collector->getSummary(); expect($summary['cache_hits'])->toBe(2); expect($summary['cache_misses'])->toBe(1); expect($summary['cache_hit_rate'])->toBeGreaterThan(60.0); expect($summary['cache_hit_rate'])->toBeLessThan(70.0); }); it('handles action errors separately', function () { $collector = new ComponentMetricsCollector(); $collector->recordAction('comp-1', 'handleClick', 25.5, false); $summary = $collector->getSummary(); expect($summary['action_errors'])->toBe(1); }); it('tracks multiple components', function () { $collector = new ComponentMetricsCollector(); $collector->recordRender('comp-1', 45.5, false); $collector->recordRender('comp-2', 30.2, true); $collector->recordAction('comp-1', 'handleClick', 25.5, true); $collector->recordAction('comp-2', 'handleSubmit', 35.8, true); $summary = $collector->getSummary(); expect($summary['total_renders'])->toBe(2); expect($summary['total_actions'])->toBe(2); }); it('handles zero operations gracefully', function () { $collector = new ComponentMetricsCollector(); $summary = $collector->getSummary(); expect($summary['total_renders'])->toBe(0); expect($summary['total_actions'])->toBe(0); expect($summary['cache_hit_rate'])->toBe(0.0); }); });