91 lines
3.7 KiB
PHP
91 lines
3.7 KiB
PHP
<?php
|
|
|
|
app_require('routes/moduleWeatherAPIRoute.php');
|
|
|
|
use routes\moduleWeatherAPIRoute;
|
|
|
|
function weather_timeline_range_invoke_private(moduleWeatherAPIRoute $route, string $method, array $args = []): mixed
|
|
{
|
|
$reflection = new ReflectionClass($route);
|
|
$target = $reflection->getMethod($method);
|
|
$target->setAccessible(true);
|
|
|
|
return $target->invokeArgs($route, $args);
|
|
}
|
|
|
|
beforeEach(function (): void {
|
|
$_SERVER['REQUEST_URI'] = '/departments/weather';
|
|
});
|
|
|
|
it('builds a timeline range from start of yesterday to end of today', function (): void {
|
|
$route = new moduleWeatherAPIRoute();
|
|
$range = weather_timeline_range_invoke_private($route, 'getDepartmentWeatherTimelineRange');
|
|
|
|
$expectedStart = new DateTime(date('Y-m-d 00:00:00'));
|
|
$expectedStart->sub(new DateInterval('P1D'));
|
|
|
|
$expectedEndExclusive = new DateTime(date('Y-m-d 00:00:00'));
|
|
$expectedEndExclusive->add(new DateInterval('P1D'));
|
|
|
|
expect($range['start'] instanceof DateTime)->toBeTrue();
|
|
expect($range['endExclusive'] instanceof DateTime)->toBeTrue();
|
|
expect($range['start']->format('Y-m-d H:i:s'))->toBe($expectedStart->format('Y-m-d H:i:s'));
|
|
expect($range['endExclusive']->format('Y-m-d H:i:s'))->toBe($expectedEndExclusive->format('Y-m-d H:i:s'));
|
|
|
|
$hourCount = (int)(($range['endExclusive']->getTimestamp() - $range['start']->getTimestamp()) / 3600);
|
|
expect($hourCount)->toBe(48);
|
|
});
|
|
|
|
it('builds a timeline range relative to explicit date_from and date_to override', function (): void {
|
|
$route = new moduleWeatherAPIRoute();
|
|
$range = weather_timeline_range_invoke_private($route, 'getDepartmentWeatherTimelineRange', ['2026-03-08', '2026-03-10']);
|
|
|
|
expect($range['start']->format('Y-m-d H:i:s'))->toBe('2026-03-08 00:00:00');
|
|
expect($range['endExclusive']->format('Y-m-d H:i:s'))->toBe('2026-03-11 00:00:00');
|
|
|
|
$hourCount = (int)(($range['endExclusive']->getTimestamp() - $range['start']->getTimestamp()) / 3600);
|
|
expect($hourCount)->toBe(72);
|
|
});
|
|
|
|
it('resolves forecast day count for a given timeline range with sane limits', function (): void {
|
|
$route = new moduleWeatherAPIRoute();
|
|
|
|
$start = new DateTime(date('Y-m-d 00:00:00'));
|
|
$end = new DateTime(date('Y-m-d 00:00:00'));
|
|
$end->add(new DateInterval('P3D'));
|
|
|
|
$days = weather_timeline_range_invoke_private($route, 'resolveForecastDaysForTimelineRange', [$start, $end]);
|
|
|
|
expect($days)->toBe(3);
|
|
|
|
$farEnd = new DateTime(date('Y-m-d 00:00:00'));
|
|
$farEnd->add(new DateInterval('P40D'));
|
|
$clamped = weather_timeline_range_invoke_private($route, 'resolveForecastDaysForTimelineRange', [$start, $farEnd]);
|
|
expect($clamped)->toBe(14);
|
|
|
|
$pastStart = new DateTime(date('Y-m-d 00:00:00'));
|
|
$pastStart->sub(new DateInterval('P10D'));
|
|
$pastEnd = new DateTime(date('Y-m-d 00:00:00'));
|
|
$pastEnd->sub(new DateInterval('P5D'));
|
|
$pastDays = weather_timeline_range_invoke_private($route, 'resolveForecastDaysForTimelineRange', [$pastStart, $pastEnd]);
|
|
expect($pastDays)->toBe(1);
|
|
});
|
|
|
|
it('marks non-started slots as unknown regardless of wash-hour ratio', function (): void {
|
|
$route = new moduleWeatherAPIRoute();
|
|
|
|
$targets = [
|
|
'degraded_threshold' => 1.0,
|
|
'healthy_threshold' => 1.3,
|
|
];
|
|
|
|
$futureStatus = weather_timeline_range_invoke_private($route, 'calculateStatus', [10, 2.0, false, $targets]);
|
|
expect($futureStatus)->toBe('unknown');
|
|
|
|
$missingTargetStatus = weather_timeline_range_invoke_private($route, 'calculateStatus', [10, 2.0, true]);
|
|
expect($missingTargetStatus)->toBe('unknown');
|
|
|
|
$startedStatus = weather_timeline_range_invoke_private($route, 'calculateStatus', [10, 2.0, true, $targets]);
|
|
expect($startedStatus)->toBe('healthy');
|
|
});
|