- 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.
162 lines
5.6 KiB
PHP
162 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Core\ValueObjects\Percentage;
|
|
use App\Framework\LiveComponents\Cache\CacheMetrics;
|
|
use App\Framework\LiveComponents\Cache\CacheType;
|
|
|
|
describe('CacheMetrics', function () {
|
|
it('creates empty metrics with zero values', function () {
|
|
$metrics = CacheMetrics::empty(CacheType::STATE);
|
|
|
|
expect($metrics->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);
|
|
});
|
|
});
|