config = new TelemetryConfig( serviceName: 'test-service', serviceVersion: '1.0.0', environment: 'test', enabled: true ); // Create real instances for final classes $this->clock = new SystemClock(); $serializer = Mockery::mock(Serializer::class); $serializer->shouldReceive('serialize')->andReturnUsing(fn($val) => serialize($val)); $serializer->shouldReceive('unserialize')->andReturnUsing(fn($val) => unserialize($val)); $this->cache = new GeneralCache(new InMemoryCache(), $serializer); // Create CircuitBreaker with test dependencies $this->circuitBreaker = new CircuitBreaker($this->cache, $this->clock, null, null, null, 'test'); // Mock interfaces $this->performanceCollector = Mockery::mock(PerformanceCollectorInterface::class); // Configure mocks $this->performanceCollector->shouldReceive('startTiming')->andReturnNull(); $this->performanceCollector->shouldReceive('endTiming')->andReturnNull(); // Create simple logger for tests $this->logger = new DefaultLogger(); $this->telemetry = new UnifiedTelemetryService( $this->performanceCollector, $this->circuitBreaker, $this->logger, $this->clock, $this->config ); $this->tracer = new LiveComponentTracer($this->telemetry); }); afterEach(function () { Mockery::close(); }); describe('Resolve Operation Tracing', function () { it('creates trace span for component resolution', function () { $operation = $this->tracer->traceResolve('test-component', [ 'data_provider' => 'UserProvider', ]); expect($operation)->toBeInstanceOf(OperationHandle::class); // End the operation $operation->end('success'); }); it('includes component id in resolve attributes', function () { $operation = $this->tracer->traceResolve('user-list', [ 'initial_data' => ['page' => 1], ]); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('handles resolve operation errors gracefully', function () { $operation = $this->tracer->traceResolve('failing-component'); expect($operation)->toBeInstanceOf(OperationHandle::class); // End with error $operation->fail('Component resolution failed'); }); }); describe('Render Operation Tracing', function () { it('creates trace span for component rendering', function () { $operation = $this->tracer->traceRender('test-component', [ 'cached' => false, 'template' => 'components/user-card', ]); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('tracks render operation with view type', function () { $operation = $this->tracer->traceRender('product-list', [ 'items_count' => 50, ]); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('handles render failures with error status', function () { $operation = $this->tracer->traceRender('broken-template'); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->fail('Template rendering failed'); }); }); describe('Handle Operation Tracing', function () { it('creates trace span for action handling', function () { $operation = $this->tracer->traceHandle( 'form-component', 'submitForm', ['validation' => 'passed'] ); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('includes action name in handle attributes', function () { $operation = $this->tracer->traceHandle( 'cart-component', 'addItem', ['item_id' => 123] ); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('tracks action execution time', function () { $operation = $this->tracer->traceHandle('data-table', 'sortColumn'); expect($operation)->toBeInstanceOf(OperationHandle::class); // Simulate some work usleep(1000); // 1ms $operation->end('success'); }); }); describe('Upload Operation Tracing', function () { it('creates trace span for file upload', function () { $operation = $this->tracer->traceUpload( 'file-uploader', 'upload-session-123', ['chunk' => 1, 'total_chunks' => 5] ); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('includes upload session id in attributes', function () { $operation = $this->tracer->traceUpload( 'avatar-upload', 'session-abc-def', ['file_size' => 2048000] ); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->end('success'); }); it('handles upload failures', function () { $operation = $this->tracer->traceUpload('doc-uploader', 'session-xyz'); expect($operation)->toBeInstanceOf(OperationHandle::class); $operation->fail('Upload failed: file too large'); }); }); describe('Event Recording', function () { it('records component lifecycle events', function () { // Events are fire-and-forget, no return value $this->tracer->recordEvent('mounted', 'test-component', [ 'mount_time_ms' => 45.2, ]); // Should not throw expect(true)->toBeTrue(); }); it('records multiple events for component', function () { $componentId = 'lifecycle-test'; $this->tracer->recordEvent('mounted', $componentId); $this->tracer->recordEvent('updated', $componentId, ['updates' => 3]); $this->tracer->recordEvent('destroyed', $componentId); expect(true)->toBeTrue(); }); it('records custom component events', function () { $this->tracer->recordEvent('form_submitted', 'contact-form', [ 'fields_count' => 5, 'validation_passed' => true, ]); expect(true)->toBeTrue(); }); }); describe('Metric Recording', function () { it('records component metrics', function () { $this->tracer->recordMetric('render_duration', 25.5, 'ms', [ 'component_id' => 'test-component', ]); expect(true)->toBeTrue(); }); it('records multiple metrics with different units', function () { $this->tracer->recordMetric('cache_size', 1024, 'bytes'); $this->tracer->recordMetric('action_count', 15, 'count'); $this->tracer->recordMetric('response_time', 150.3, 'ms'); expect(true)->toBeTrue(); }); it('records metrics without unit', function () { $this->tracer->recordMetric('items_rendered', 50.0); expect(true)->toBeTrue(); }); }); describe('Traced Execution', function () { it('executes callback within traced operation', function () { $result = $this->tracer->trace( 'resolve', 'test-component', fn() => ['data' => 'loaded'], ['source' => 'database'] ); expect($result)->toBe(['data' => 'loaded']); }); it('executes render operation with trace', function () { $html = $this->tracer->trace( 'render', 'user-card', fn() => '