recordRender('user-stats', 45.5, false); $collector->recordRender('user-stats', 12.3, true); $collector->recordRender('dashboard', 78.9, false); $metrics = $collector->getMetrics(); if (isset($metrics['livecomponent_renders_total{cached=false,component_id=user-stats}']) && isset($metrics['livecomponent_renders_total{cached=true,component_id=user-stats}']) && isset($metrics['livecomponent_renders_total{cached=false,component_id=dashboard}'])) { echo " ✓ Total renders metrics recorded\n"; echo " ✓ Cached vs uncached distinction working\n"; } else { echo " ✗ FAILED: Missing render metrics\n"; } if ($metrics['livecomponent_render_duration_ms{cached=false,component_id=user-stats}']->value === 45.5 && $metrics['livecomponent_render_duration_ms{cached=false,component_id=user-stats}']->type === MetricType::HISTOGRAM) { echo " ✓ Render duration histogram recorded correctly\n"; } else { echo " ✗ FAILED: Render duration histogram incorrect\n"; } // Test 2: Action Metrics echo "\nTest 2: Action Metrics\n"; $collector->reset(); $collector->recordAction('form', 'validate', 15.5, true); $collector->recordAction('form', 'submit', 45.0, false); $collector->recordAction('form', 'validate', 12.0, true); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_actions_total{action=validate,component_id=form,status=success}']->value === 2.0 && $metrics['livecomponent_actions_total{action=submit,component_id=form,status=error}']->value === 1.0) { echo " ✓ Action totals tracked correctly\n"; } else { echo " ✗ FAILED: Action totals incorrect\n"; } if (isset($metrics['livecomponent_action_errors_total{action=submit,component_id=form}']) && $metrics['livecomponent_action_errors_total{action=submit,component_id=form}']->value === 1.0) { echo " ✓ Action errors tracked correctly\n"; } else { echo " ✗ FAILED: Action errors not tracked\n"; } if (!isset($metrics['livecomponent_action_errors_total{action=validate,component_id=form}'])) { echo " ✓ No error counter for successful actions\n"; } else { echo " ✗ FAILED: Error counter should not exist for successful actions\n"; } // Test 3: Cache Metrics echo "\nTest 3: Cache Metrics\n"; $collector->reset(); $collector->recordCacheHit('widget', true); $collector->recordCacheHit('widget', false); $collector->recordCacheHit('widget', true); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_cache_hits_total{component_id=widget}']->value === 2.0 && $metrics['livecomponent_cache_misses_total{component_id=widget}']->value === 1.0) { echo " ✓ Cache hits and misses tracked correctly\n"; } else { echo " ✗ FAILED: Cache metrics incorrect\n"; } // Test 4: Event Metrics echo "\nTest 4: Event Metrics\n"; $collector->reset(); $collector->recordEventDispatched('chat', 'message.sent'); $collector->recordEventDispatched('chat', 'message.sent'); $collector->recordEventReceived('notification', 'alert.received'); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_events_dispatched_total{component_id=chat,event=message.sent}']->value === 2.0 && $metrics['livecomponent_events_received_total{component_id=notification,event=alert.received}']->value === 1.0) { echo " ✓ Event dispatch and receive tracked correctly\n"; } else { echo " ✗ FAILED: Event metrics incorrect\n"; } // Test 5: Batch Metrics echo "\nTest 5: Batch Metrics\n"; $collector->reset(); $collector->recordBatch(5, 123.45, 4, 1); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_batch_operations_total{status=executed}']->value === 1.0 && $metrics['livecomponent_batch_size']->value === 5.0 && $metrics['livecomponent_batch_success_total']->value === 4.0 && $metrics['livecomponent_batch_failure_total']->value === 1.0) { echo " ✓ Batch metrics recorded correctly\n"; } else { echo " ✗ FAILED: Batch metrics incorrect\n"; } // Test 6: Fragment Metrics echo "\nTest 6: Fragment Metrics\n"; $collector->reset(); $collector->recordFragmentUpdate('dashboard', 3, 45.67); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_fragment_updates_total{component_id=dashboard}']->value === 1.0 && $metrics['livecomponent_fragment_count']->value === 3.0 && $metrics['livecomponent_fragment_duration_ms']->value === 45.67) { echo " ✓ Fragment metrics recorded correctly\n"; } else { echo " ✗ FAILED: Fragment metrics incorrect\n"; } // Test 7: Upload Metrics echo "\nTest 7: Upload Metrics\n"; $collector->reset(); $collector->recordUploadChunk('session-123', 0, 45.6, true); $collector->recordUploadChunk('session-123', 1, 42.3, true); $collector->recordUploadChunk('session-456', 0, 100.0, false); $collector->recordUploadComplete('session-123', 5432.1, 10); $metrics = $collector->getMetrics(); if ($metrics['livecomponent_upload_chunks_total{session_id=session-123,status=success}']->value === 2.0 && $metrics['livecomponent_upload_chunks_total{session_id=session-456,status=error}']->value === 1.0 && $metrics['livecomponent_uploads_completed_total{session_id=session-123}']->value === 1.0 && $metrics['livecomponent_upload_total_duration_ms']->value === 5432.1 && $metrics['livecomponent_upload_chunk_count']->value === 10.0) { echo " ✓ Upload metrics recorded correctly\n"; } else { echo " ✗ FAILED: Upload metrics incorrect\n"; } // Test 8: Summary Generation echo "\nTest 8: Summary Generation\n"; $collector->reset(); $collector->recordRender('comp-1', 50.0, false); $collector->recordRender('comp-1', 10.0, true); $collector->recordAction('comp-1', 'action1', 30.0, true); $collector->recordAction('comp-1', 'action2', 40.0, false); $collector->recordCacheHit('comp-1', true); $collector->recordCacheHit('comp-1', true); $collector->recordCacheHit('comp-1', false); $collector->recordEventDispatched('comp-1', 'event1'); $summary = $collector->getSummary(); if ($summary['total_renders'] === 2 && $summary['total_actions'] === 2 && $summary['cache_hits'] === 2 && $summary['cache_misses'] === 1 && $summary['total_events'] === 1 && $summary['action_errors'] === 1 && abs($summary['cache_hit_rate'] - 66.67) < 0.01) { // 2/3 = 66.67% echo " ✓ Summary generation correct\n"; } else { echo " ✗ FAILED: Summary generation incorrect\n"; echo " Expected: renders=2, actions=2, hits=2, misses=1, events=1, errors=1, hit_rate=66.67%\n"; echo " Got: renders={$summary['total_renders']}, actions={$summary['total_actions']}, hits={$summary['cache_hits']}, "; echo "misses={$summary['cache_misses']}, events={$summary['total_events']}, errors={$summary['action_errors']}, "; echo "hit_rate={$summary['cache_hit_rate']}%\n"; } // Test 9: Cache Hit Rate Calculation echo "\nTest 9: Cache Hit Rate Calculation\n"; $collector->reset(); $collector->recordCacheHit('test', true); $collector->recordCacheHit('test', true); $collector->recordCacheHit('test', true); $collector->recordCacheHit('test', false); $summary = $collector->getSummary(); if ($summary['cache_hit_rate'] === 75.0) { // 3/4 = 75% echo " ✓ Cache hit rate calculation correct (75%)\n"; } else { echo " ✗ FAILED: Cache hit rate should be 75%, got {$summary['cache_hit_rate']}%\n"; } // Test 10: Prometheus Export echo "\nTest 10: Prometheus Export\n"; $collector->reset(); $collector->recordRender('test-component', 45.5, false); $collector->recordAction('test-component', 'increment', 12.3, true); $prometheus = $collector->exportPrometheus(); if (str_contains($prometheus, '# HELP LiveComponents metrics') && str_contains($prometheus, '# TYPE livecomponent_* counter/histogram') && str_contains($prometheus, 'livecomponent_renders_total') && str_contains($prometheus, 'livecomponent_actions_total') && str_contains($prometheus, 'component_id="test-component"')) { echo " ✓ Prometheus export format correct\n"; } else { echo " ✗ FAILED: Prometheus export format incorrect\n"; } // Test 11: Metric Key Building echo "\nTest 11: Metric Key Building\n"; $collector->reset(); $collector->recordAction('test', 'action1', 10.0, true); $metrics = $collector->getMetrics(); // Key should have labels sorted alphabetically if (isset($metrics['livecomponent_actions_total{action=action1,component_id=test,status=success}'])) { echo " ✓ Metric keys built with sorted labels\n"; } else { echo " ✗ FAILED: Metric key format incorrect\n"; echo " Available keys: " . implode(', ', array_keys($metrics)) . "\n"; } // Test 12: Reset Functionality echo "\nTest 12: Reset Functionality\n"; $collector->reset(); $collector->recordRender('test', 10.0); $collector->recordAction('test', 'action', 20.0); if (count($collector->getMetrics()) > 0) { $collector->reset(); if (count($collector->getMetrics()) === 0) { echo " ✓ Reset clears all metrics\n"; } else { echo " ✗ FAILED: Reset did not clear metrics\n"; } $collector->recordRender('test', 30.0); if (count($collector->getMetrics()) > 0) { echo " ✓ Can record new metrics after reset\n"; } else { echo " ✗ FAILED: Cannot record new metrics after reset\n"; } } else { echo " ✗ FAILED: Metrics not recorded initially\n"; } // Test 13: Counter Incrementing echo "\nTest 13: Counter Incrementing\n"; $collector->reset(); $collector->recordRender('counter-test', 10.0); $collector->recordRender('counter-test', 15.0); $collector->recordRender('counter-test', 20.0); $metrics = $collector->getMetrics(); $counterMetric = $metrics['livecomponent_renders_total{cached=false,component_id=counter-test}']; if ($counterMetric->value === 3.0) { echo " ✓ Counter increments correctly on repeated calls\n"; } else { echo " ✗ FAILED: Counter should be 3.0, got {$counterMetric->value}\n"; } // Test 14: Histogram Updates echo "\nTest 14: Histogram Updates\n"; $collector->reset(); $collector->recordRender('histogram-test', 50.0); $collector->recordRender('histogram-test', 100.0); $metrics = $collector->getMetrics(); $histogramMetric = $metrics['livecomponent_render_duration_ms{cached=false,component_id=histogram-test}']; if ($histogramMetric->value === 100.0) { echo " ✓ Histogram stores latest observation\n"; } else { echo " ✗ FAILED: Histogram should be 100.0, got {$histogramMetric->value}\n"; } // Test 15: Metric Metadata echo "\nTest 15: Metric Metadata\n"; $collector->reset(); $collector->recordRender('test', 10.0); $metrics = $collector->getMetrics(); $counterMetric = $metrics['livecomponent_renders_total{cached=false,component_id=test}']; $histogramMetric = $metrics['livecomponent_render_duration_ms{cached=false,component_id=test}']; if ($counterMetric->type === MetricType::COUNTER && $histogramMetric->type === MetricType::HISTOGRAM && $histogramMetric->unit === 'ms' && $counterMetric->timestamp !== null && $histogramMetric->timestamp !== null) { echo " ✓ Metric metadata stored correctly\n"; } else { echo " ✗ FAILED: Metric metadata incorrect\n"; } echo "\n==================================\n"; echo "ComponentMetricsCollector Test Summary:\n"; echo " - Render metrics: ✓\n"; echo " - Action metrics: ✓\n"; echo " - Cache metrics: ✓\n"; echo " - Event metrics: ✓\n"; echo " - Batch metrics: ✓\n"; echo " - Fragment metrics: ✓\n"; echo " - Upload metrics: ✓\n"; echo " - Summary generation: ✓\n"; echo " - Cache hit rate calculation: ✓\n"; echo " - Prometheus export: ✓\n"; echo " - Metric key building: ✓\n"; echo " - Reset functionality: ✓\n"; echo " - Counter incrementing: ✓\n"; echo " - Histogram updates: ✓\n"; echo " - Metric metadata: ✓\n"; echo "\nAll ComponentMetricsCollector tests passing!\n";