cacheType)->toBe(CacheType::STATE) ->and($metrics->hits)->toBe(0) ->and($metrics->misses)->toBe(0) ->and($metrics->hitRate->getValue())->toBe(0.0) ->and($metrics->missRate->getValue())->toBe(0.0) ->and($metrics->getTotalOperations())->toBe(0); }); it('records cache hit and updates metrics', function () { $metrics = CacheMetrics::empty(CacheType::STATE); $updated = $metrics->withHit(0.5); expect($updated->hits)->toBe(1) ->and($updated->misses)->toBe(0) ->and($updated->hitRate->getValue())->toBe(100.0) ->and($updated->missRate->getValue())->toBe(0.0) ->and($updated->averageLookupTimeMs)->toBe(0.5); }); it('records cache miss and updates metrics', function () { $metrics = CacheMetrics::empty(CacheType::STATE); $updated = $metrics->withMiss(1.2); expect($updated->hits)->toBe(0) ->and($updated->misses)->toBe(1) ->and($updated->hitRate->getValue())->toBe(0.0) ->and($updated->missRate->getValue())->toBe(100.0) ->and($updated->averageLookupTimeMs)->toBe(1.2); }); it('calculates correct hit rate with mixed hits and misses', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withHit(0.5) ->withHit(0.6) ->withHit(0.4) ->withMiss(1.0); expect($metrics->hits)->toBe(3) ->and($metrics->misses)->toBe(1) ->and($metrics->hitRate->getValue())->toBe(75.0) ->and($metrics->missRate->getValue())->toBe(25.0) ->and($metrics->getTotalOperations())->toBe(4); }); it('calculates weighted average lookup time', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withHit(0.5) // Avg: 0.5 ->withHit(1.5); // Avg: (0.5 + 1.5) / 2 = 1.0 expect($metrics->averageLookupTimeMs)->toBe(1.0); }); it('records cache invalidations', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withInvalidation() ->withInvalidation(); expect($metrics->invalidations)->toBe(2); }); it('updates cache size', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withSize(150); expect($metrics->totalSize)->toBe(150); }); it('checks performance target with Percentage', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withHit(0.5) ->withHit(0.6) ->withHit(0.7) ->withMiss(1.0); // 75% hit rate $target = Percentage::from(70.0); expect($metrics->meetsPerformanceTarget($target))->toBeTrue(); }); it('returns correct performance grade', function () { expect( CacheMetrics::empty(CacheType::STATE) ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5) ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5) ->withHit(0.5)->withMiss(1.0) // 90% hit rate ->getPerformanceGrade() )->toBe('A'); expect( CacheMetrics::empty(CacheType::STATE) ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5) ->withMiss(1.0)->withMiss(1.0) // 66.67% hit rate ->getPerformanceGrade() )->toBe('D'); }); it('converts to array for reporting', function () { $metrics = CacheMetrics::empty(CacheType::STATE) ->withHit(0.5) ->withMiss(1.0); $array = $metrics->toArray(); expect($array)->toHaveKey('cache_type') ->and($array)->toHaveKey('hits') ->and($array)->toHaveKey('misses') ->and($array)->toHaveKey('hit_rate') ->and($array)->toHaveKey('miss_rate') ->and($array)->toHaveKey('average_lookup_time_ms') ->and($array)->toHaveKey('performance_grade') ->and($array['cache_type'])->toBe('state') ->and($array['hits'])->toBe(1) ->and($array['misses'])->toBe(1); }); it('merges metrics from multiple sources', function () { $metrics1 = CacheMetrics::empty(CacheType::STATE) ->withHit(0.5) ->withHit(0.6); $metrics2 = CacheMetrics::empty(CacheType::SLOT) ->withHit(0.7) ->withMiss(1.0); $merged = CacheMetrics::merge($metrics1, $metrics2); expect($merged->cacheType)->toBe(CacheType::MERGED) ->and($merged->hits)->toBe(3) ->and($merged->misses)->toBe(1) ->and($merged->hitRate->getValue())->toBe(75.0); }); it('handles empty merge gracefully', function () { $merged = CacheMetrics::merge(); expect($merged->cacheType)->toBe(CacheType::MERGED) ->and($merged->hits)->toBe(0) ->and($merged->misses)->toBe(0); }); it('preserves immutability when recording operations', function () { $original = CacheMetrics::empty(CacheType::STATE); $updated = $original->withHit(0.5); expect($original->hits)->toBe(0) ->and($updated->hits)->toBe(1) ->and($original)->not->toBe($updated); }); });