getMethod($method); return $target->invokeArgs($route, $args); } beforeEach(function (): void { $_SERVER['REQUEST_URI'] = '/departments/weather'; }); it('evaluates healthy degraded and unhealthy statuses from configured department targets', function (): void { $route = new moduleWeatherAPIRoute(); $targets = [ 'degraded_threshold' => 1.0, 'healthy_threshold' => 1.3, ]; $healthy = weather_status_targets_invoke_private($route, 'calculateStatus', [13, 10.0, true, $targets]); $degraded = weather_status_targets_invoke_private($route, 'calculateStatus', [10, 10.0, true, $targets]); $unhealthy = weather_status_targets_invoke_private($route, 'calculateStatus', [9, 10.0, true, $targets]); expect($healthy)->toBe('healthy'); expect($degraded)->toBe('degraded'); expect($unhealthy)->toBe('unhealthy'); }); it('returns unknown when configured targets are missing for department status evaluation', function (): void { $route = new moduleWeatherAPIRoute(); $status = weather_status_targets_invoke_private($route, 'calculateStatus', [10, 10.0, true, null]); expect($status)->toBe('unknown'); }); it('aggregates multi-department slot statuses using worst severity when any department is unhealthy', function (): void { $route = new moduleWeatherAPIRoute(); $slotKey = '2026-03-24 10:00'; $targets = [ 1 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], 2 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], ]; $worst = weather_status_targets_invoke_private($route, 'calculateAggregatedDepartmentStatus', [ [1, 2], [ 1 => [$slotKey => 14], 2 => [$slotKey => 8], ], [ 1 => [$slotKey => 10.0], 2 => [$slotKey => 10.0], ], $slotKey, true, $targets, ]); expect($worst)->toBe('unhealthy'); }); it('aggregates all-unhealthy multi-department slot statuses as unhealthy', function (): void { $route = new moduleWeatherAPIRoute(); $slotKey = '2026-03-24 10:00'; $targets = [ 1 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], 2 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], ]; $worst = weather_status_targets_invoke_private($route, 'calculateAggregatedDepartmentStatus', [ [1, 2], [ 1 => [$slotKey => 3], 2 => [$slotKey => 4], ], [ 1 => [$slotKey => 10.0], 2 => [$slotKey => 10.0], ], $slotKey, true, $targets, ]); expect($worst)->toBe('unhealthy'); }); it('returns unknown when aggregated multi-department slot targets are missing', function (): void { $route = new moduleWeatherAPIRoute(); $slotKey = '2026-03-24 10:00'; $missingTargets = weather_status_targets_invoke_private($route, 'calculateAggregatedDepartmentStatus', [ [1, 2], [ 1 => [$slotKey => 14], 2 => [$slotKey => 8], ], [ 1 => [$slotKey => 10.0], 2 => [$slotKey => 10.0], ], $slotKey, true, [ 1 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], ], ]); expect($missingTargets)->toBe('unknown'); }); it('returns unknown when aggregated multi-department slot has no evaluable hours', function (): void { $route = new moduleWeatherAPIRoute(); $slotKey = '2026-03-24 10:00'; $targets = [ 1 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], 2 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], ]; $noEvaluableHours = weather_status_targets_invoke_private($route, 'calculateAggregatedDepartmentStatus', [ [1, 2], [ 1 => [$slotKey => 0], 2 => [$slotKey => 0], ], [ 1 => [$slotKey => 0.0], 2 => [$slotKey => 0.0], ], $slotKey, true, $targets, ]); expect($noEvaluableHours)->toBe('unknown'); }); it('returns unknown when aggregated multi-department slot has not started yet', function (): void { $route = new moduleWeatherAPIRoute(); $slotKey = '2026-03-24 10:00'; $targets = [ 1 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], 2 => ['degraded_threshold' => 1.0, 'healthy_threshold' => 1.3], ]; $futureSlot = weather_status_targets_invoke_private($route, 'calculateAggregatedDepartmentStatus', [ [1, 2], [ 1 => [$slotKey => 14], 2 => [$slotKey => 8], ], [ 1 => [$slotKey => 10.0], 2 => [$slotKey => 10.0], ], $slotKey, false, $targets, ]); expect($futureSlot)->toBe('unknown'); });