Files
api/services/nginx/app/tests/Unit/Workfeed/DepartmentWeatherFallbackBehaviorTest.php
T

137 lines
5.3 KiB
PHP

<?php
app_require('routes/moduleWeatherAPIRoute.php');
use routes\moduleWeatherAPIRoute;
function department_weather_fallback_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);
}
function department_weather_coordinate_property(mixed $value): object
{
return new class($value) {
public function __construct(private mixed $value)
{
}
public function value(): mixed
{
return $this->value;
}
};
}
function department_weather_fake_department(mixed $latitude, mixed $longitude): object
{
return (object)[
'latitude' => department_weather_coordinate_property($latitude),
'longitude' => department_weather_coordinate_property($longitude),
];
}
beforeEach(function (): void {
$_SERVER['REQUEST_URI'] = '/departments/weather';
});
it('resolves weather coordinates from valid coordinates only', function (): void {
$route = new moduleWeatherAPIRoute();
$coordinates = department_weather_fallback_invoke_private($route, 'resolveWeatherCoordinates', [[
department_weather_fake_department('', ''),
department_weather_fake_department('0', '0'),
department_weather_fake_department('91.0', '12.0'),
department_weather_fake_department('55.6761', '181.0'),
department_weather_fake_department('55.6761', '12.5683'),
department_weather_fake_department(56.2639, 9.5018),
]]);
expect($coordinates)->toBeArray();
expect(round($coordinates['lat'], 4))->toBe(round((55.6761 + 56.2639) / 2, 4));
expect(round($coordinates['lon'], 4))->toBe(round((12.5683 + 9.5018) / 2, 4));
});
it('returns null weather coordinates when all department locations are invalid', function (): void {
$route = new moduleWeatherAPIRoute();
$coordinates = department_weather_fallback_invoke_private($route, 'resolveWeatherCoordinates', [[
department_weather_fake_department('', ''),
department_weather_fake_department('0', '0'),
department_weather_fake_department('95.0', '12.0'),
department_weather_fake_department('55.0', '-181.0'),
]]);
expect($coordinates)->toBeNull();
});
it('classifies weatherapi no matching location errors as location lookup failures', function (): void {
$route = new moduleWeatherAPIRoute();
$is_location_error = department_weather_fallback_invoke_private($route, 'isWeatherLocationLookupFailure', [
new Exception('WeatherAPI request failed with HTTP 400: No matching location found.'),
]);
expect($is_location_error)->toBeTrue();
});
it('does not classify non-location weatherapi errors as location lookup failures', function (): void {
$route = new moduleWeatherAPIRoute();
$is_location_error = department_weather_fallback_invoke_private($route, 'isWeatherLocationLookupFailure', [
new Exception('WeatherAPI request failed with HTTP 401: API key is invalid.'),
]);
expect($is_location_error)->toBeFalse();
});
it('returns an empty forecast fallback when coordinates are unavailable', function (): void {
$route = new moduleWeatherAPIRoute();
$result = department_weather_fallback_invoke_private($route, 'fetchDepartmentForecastOrFallback', [null, 2]);
expect($result['fallback_reason'])->toBe('invalid_department_coordinates');
expect($result['forecast'])->toBeObject();
expect($result['forecast']->forecast->forecastday ?? null)->toBeArray();
expect($result['forecast']->forecast->forecastday ?? [1])->toHaveCount(0);
});
it('builds timeline entries with mostly_clear weather when forecast payload is empty', function (): void {
$route = new moduleWeatherAPIRoute();
$start = new DateTime('2026-03-24 08:00:00');
$end_exclusive = new DateTime('2026-03-24 11:00:00');
$timeline = department_weather_fallback_invoke_private($route, 'buildDepartmentWeatherTimeline', [
[],
[],
(object)['forecast' => (object)['forecastday' => []]],
[
'start' => $start,
'endExclusive' => $end_exclusive,
],
]);
expect($timeline)->toHaveCount(3);
expect($timeline[0]['date'])->toBe('2026-03-24');
expect($timeline[0]['time'])->toBe('08:00');
expect($timeline[0]['weather'])->toBe('mostly_clear');
expect($timeline[0]['washes'])->toBe(0);
expect($timeline[0]['hours'])->toBe(0.0);
expect($timeline[0]['status'])->toBe('unknown');
expect($timeline[1]['weather'])->toBe('mostly_clear');
expect($timeline[2]['weather'])->toBe('mostly_clear');
});
it('wires departments weather route through the forecast fallback path', function (): void {
$routeFile = app_path('routes/moduleWeatherAPIRoute.php');
$content = (string)file_get_contents($routeFile);
expect($content)->toContain('fetchDepartmentForecastOrFallback');
expect($content)->toContain('$this->resolveForecastDaysForTimelineRange');
expect($content)->toContain('$this->buildDepartmentWeatherTimeline');
expect($content)->not->toContain('static function () use ($coordinates');
expect($content)->toContain('DEPARTMENTS_WEATHER_FALLBACK');
expect($content)->not->toContain('Selected department(s) do not have GPS coordinates configured');
});