- 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.
145 lines
4.7 KiB
PHP
145 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Application\LiveComponents\Dashboard\QueueStatsComponent;
|
|
use App\Application\LiveComponents\Dashboard\QueueStatsState;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
use App\Framework\Queue\Queue;
|
|
use App\Framework\Queue\Services\JobMetricsManagerInterface;
|
|
use App\Framework\Queue\ValueObjects\JobMetrics;
|
|
use App\Framework\Core\ValueObjects\Duration;
|
|
|
|
describe('QueueStatsComponent', function () {
|
|
beforeEach(function () {
|
|
// Mock Queue
|
|
$this->queue = Mockery::mock(Queue::class);
|
|
|
|
// Mock JobMetricsManager
|
|
$this->metricsManager = Mockery::mock(JobMetricsManagerInterface::class);
|
|
|
|
$this->componentId = ComponentId::create('queue-stats', 'test');
|
|
$this->initialState = QueueStatsState::empty();
|
|
});
|
|
|
|
afterEach(function () {
|
|
Mockery::close();
|
|
});
|
|
|
|
it('polls and updates state with queue statistics', function () {
|
|
// Mock queue stats
|
|
$this->queue->shouldReceive('getStats')
|
|
->once()
|
|
->andReturn([
|
|
'total_size' => 42,
|
|
'priority_breakdown' => ['high' => 10, 'medium' => 20, 'low' => 12],
|
|
]);
|
|
|
|
$this->queue->shouldReceive('size')
|
|
->once()
|
|
->andReturn(42);
|
|
|
|
// Mock metrics
|
|
$jobMetrics = new JobMetrics(
|
|
totalJobs: 1000,
|
|
successfulJobs: 950,
|
|
failedJobs: 50,
|
|
avgExecutionTime: Duration::fromMilliseconds(123.45),
|
|
successRate: 95.0
|
|
);
|
|
|
|
$this->metricsManager->shouldReceive('getAllQueueMetrics')
|
|
->once()
|
|
->with('1 hour')
|
|
->andReturn([$jobMetrics]);
|
|
|
|
$component = new QueueStatsComponent(
|
|
id: $this->componentId,
|
|
state: $this->initialState,
|
|
queue: $this->queue,
|
|
metricsManager: $this->metricsManager
|
|
);
|
|
|
|
$newState = $component->poll();
|
|
|
|
expect($newState)->toBeInstanceOf(QueueStatsState::class);
|
|
expect($newState->currentQueueSize)->toBe(42);
|
|
expect($newState->totalJobs)->toBe(1000);
|
|
expect($newState->successfulJobs)->toBe(950);
|
|
expect($newState->failedJobs)->toBe(50);
|
|
expect($newState->successRate)->toBe(95.0);
|
|
expect($newState->avgExecutionTimeMs)->toBe(123.45);
|
|
expect($newState->lastUpdated)->not->toBe($this->initialState->lastUpdated);
|
|
});
|
|
|
|
it('has correct poll interval', function () {
|
|
$component = new QueueStatsComponent(
|
|
id: $this->componentId,
|
|
state: $this->initialState,
|
|
queue: $this->queue,
|
|
metricsManager: $this->metricsManager
|
|
);
|
|
|
|
expect($component->getPollInterval())->toBe(5000);
|
|
});
|
|
|
|
it('returns correct render data', function () {
|
|
$state = new QueueStatsState(
|
|
currentQueueSize: 10,
|
|
totalJobs: 100,
|
|
successfulJobs: 90,
|
|
failedJobs: 10,
|
|
successRate: 90.0,
|
|
avgExecutionTimeMs: 50.0,
|
|
lastUpdated: '2024-01-15 12:00:00'
|
|
);
|
|
|
|
$component = new QueueStatsComponent(
|
|
id: $this->componentId,
|
|
state: $state,
|
|
queue: $this->queue,
|
|
metricsManager: $this->metricsManager
|
|
);
|
|
|
|
$renderData = $component->getRenderData();
|
|
|
|
expect($renderData->templatePath)->toBe('livecomponent-queue-stats');
|
|
expect($renderData->data)->toHaveKey('componentId');
|
|
expect($renderData->data)->toHaveKey('stateJson');
|
|
expect($renderData->data)->toHaveKey('pollInterval');
|
|
expect($renderData->data['pollInterval'])->toBe(5000);
|
|
expect($renderData->data['currentQueueSize'])->toBe(10);
|
|
expect($renderData->data['totalJobs'])->toBe(100);
|
|
expect($renderData->data['successRate'])->toBe(90.0);
|
|
});
|
|
|
|
it('handles empty metrics gracefully', function () {
|
|
$this->queue->shouldReceive('getStats')
|
|
->once()
|
|
->andReturn(['total_size' => 0]);
|
|
|
|
$this->queue->shouldReceive('size')
|
|
->once()
|
|
->andReturn(0);
|
|
|
|
$this->metricsManager->shouldReceive('getAllQueueMetrics')
|
|
->once()
|
|
->with('1 hour')
|
|
->andReturn([]);
|
|
|
|
$component = new QueueStatsComponent(
|
|
id: $this->componentId,
|
|
state: $this->initialState,
|
|
queue: $this->queue,
|
|
metricsManager: $this->metricsManager
|
|
);
|
|
|
|
$newState = $component->poll();
|
|
|
|
expect($newState->currentQueueSize)->toBe(0);
|
|
expect($newState->totalJobs)->toBe(0);
|
|
expect($newState->successfulJobs)->toBe(0);
|
|
expect($newState->failedJobs)->toBe(0);
|
|
});
|
|
});
|