- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
211 lines
6.7 KiB
PHP
211 lines
6.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\LiveComponents\Observability\ComponentMetricsCollector;
|
|
|
|
describe('ComponentMetricsCollector - Basic Functionality', function () {
|
|
it('can be instantiated', function () {
|
|
$collector = new ComponentMetricsCollector();
|
|
|
|
expect($collector)->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);
|
|
});
|
|
});
|