getMetrics(CacheType::STATE); $slotMetrics = $collector->getMetrics(CacheType::SLOT); $templateMetrics = $collector->getMetrics(CacheType::TEMPLATE); expect($stateMetrics->hits)->toBe(0) ->and($slotMetrics->hits)->toBe(0) ->and($templateMetrics->hits)->toBe(0); }); it('records hits for specific cache type', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $collector->recordHit(CacheType::STATE, 0.6); $metrics = $collector->getMetrics(CacheType::STATE); expect($metrics->hits)->toBe(2) ->and($metrics->misses)->toBe(0); }); it('records misses for specific cache type', function () { $collector = new CacheMetricsCollector(); $collector->recordMiss(CacheType::SLOT, 1.0); $collector->recordMiss(CacheType::SLOT, 1.2); $metrics = $collector->getMetrics(CacheType::SLOT); expect($metrics->hits)->toBe(0) ->and($metrics->misses)->toBe(2); }); it('records invalidations for specific cache type', function () { $collector = new CacheMetricsCollector(); $collector->recordInvalidation(CacheType::TEMPLATE); $collector->recordInvalidation(CacheType::TEMPLATE); $metrics = $collector->getMetrics(CacheType::TEMPLATE); expect($metrics->invalidations)->toBe(2); }); it('updates cache size for specific cache type', function () { $collector = new CacheMetricsCollector(); $collector->updateSize(CacheType::STATE, 150); $metrics = $collector->getMetrics(CacheType::STATE); expect($metrics->totalSize)->toBe(150); }); it('maintains separate metrics for different cache types', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $collector->recordMiss(CacheType::SLOT, 1.0); $collector->recordInvalidation(CacheType::TEMPLATE); expect($collector->getMetrics(CacheType::STATE)->hits)->toBe(1) ->and($collector->getMetrics(CacheType::SLOT)->misses)->toBe(1) ->and($collector->getMetrics(CacheType::TEMPLATE)->invalidations)->toBe(1); }); it('returns all metrics', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $collector->recordHit(CacheType::SLOT, 0.6); $allMetrics = $collector->getAllMetrics(); expect($allMetrics)->toBeArray() ->and($allMetrics)->toHaveKey('state') ->and($allMetrics)->toHaveKey('slot') ->and($allMetrics)->toHaveKey('template'); }); it('calculates aggregate metrics across all caches', function () { $collector = new CacheMetricsCollector(); // State: 2 hits $collector->recordHit(CacheType::STATE, 0.5); $collector->recordHit(CacheType::STATE, 0.6); // Slot: 1 hit, 1 miss $collector->recordHit(CacheType::SLOT, 0.7); $collector->recordMiss(CacheType::SLOT, 1.0); // Template: 1 miss $collector->recordMiss(CacheType::TEMPLATE, 1.2); $aggregate = $collector->getAggregateMetrics(); expect($aggregate->cacheType)->toBe(CacheType::MERGED) ->and($aggregate->hits)->toBe(3) ->and($aggregate->misses)->toBe(2) ->and($aggregate->getTotalOperations())->toBe(5); }); it('generates comprehensive summary', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $collector->recordMiss(CacheType::SLOT, 1.0); $summary = $collector->getSummary(); expect($summary)->toHaveKey('overall') ->and($summary)->toHaveKey('by_type') ->and($summary)->toHaveKey('performance_assessment') ->and($summary['by_type'])->toHaveKey('state') ->and($summary['by_type'])->toHaveKey('slot') ->and($summary['by_type'])->toHaveKey('template'); }); it('assesses performance against targets', function () { $collector = new CacheMetricsCollector(); // State: 80% hit rate (target: 70%) for ($i = 0; $i < 8; $i++) { $collector->recordHit(CacheType::STATE, 0.5); } for ($i = 0; $i < 2; $i++) { $collector->recordMiss(CacheType::STATE, 1.0); } $assessment = $collector->assessPerformance(); expect($assessment['state_cache']['meets_target'])->toBeTrue() ->and($assessment['state_cache']['grade'])->toBe('B'); }); it('detects performance issues when targets not met', function () { $collector = new CacheMetricsCollector(); // State: 50% hit rate (below 70% target) $collector->recordHit(CacheType::STATE, 0.5); $collector->recordMiss(CacheType::STATE, 1.0); expect($collector->hasPerformanceIssues())->toBeTrue(); }); it('generates performance warnings for underperforming caches', function () { $collector = new CacheMetricsCollector(); // State: 50% hit rate (below 70% target) $collector->recordHit(CacheType::STATE, 0.5); $collector->recordMiss(CacheType::STATE, 1.0); $warnings = $collector->getPerformanceWarnings(); expect($warnings)->toBeArray() ->and($warnings)->not->toBeEmpty() ->and($warnings[0])->toContain('State cache hit rate'); }); it('returns no warnings when all caches meet targets', function () { $collector = new CacheMetricsCollector(); // State: 90% hit rate (exceeds 70% target) for ($i = 0; $i < 9; $i++) { $collector->recordHit(CacheType::STATE, 0.5); } $collector->recordMiss(CacheType::STATE, 1.0); // Slot: 80% hit rate (exceeds 60% target) for ($i = 0; $i < 8; $i++) { $collector->recordHit(CacheType::SLOT, 0.5); } for ($i = 0; $i < 2; $i++) { $collector->recordMiss(CacheType::SLOT, 1.0); } // Template: 90% hit rate (exceeds 80% target) for ($i = 0; $i < 9; $i++) { $collector->recordHit(CacheType::TEMPLATE, 0.5); } $collector->recordMiss(CacheType::TEMPLATE, 1.0); expect($collector->hasPerformanceIssues())->toBeFalse() ->and($collector->getPerformanceWarnings())->toBeEmpty(); }); it('exports metrics with timestamp', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $export = $collector->export(); expect($export)->toHaveKey('timestamp') ->and($export)->toHaveKey('metrics') ->and($export['timestamp'])->toBeInt(); }); it('resets all metrics', function () { $collector = new CacheMetricsCollector(); $collector->recordHit(CacheType::STATE, 0.5); $collector->recordHit(CacheType::SLOT, 0.6); $collector->recordHit(CacheType::TEMPLATE, 0.7); $collector->reset(); expect($collector->getMetrics(CacheType::STATE)->hits)->toBe(0) ->and($collector->getMetrics(CacheType::SLOT)->hits)->toBe(0) ->and($collector->getMetrics(CacheType::TEMPLATE)->hits)->toBe(0); }); });