Tik op een dag in de verwachtingsstrook voor een uuroverzicht van
temperatuur, regen en wind (richting + snelheid), om de 2 uur, 12
punten per dag. Sluit na 20 seconden vanzelf of bij een tik op de
overlay zelf.
Backend: Open-Meteo levert hourly-data in dezelfde call als daily
(geen extra verzoek, dezelfde 60min-cache). Nieuwe HourlyForecastMapper
groepeert per kalenderdatum en bemonstert om het andere uur, robuust
voor de zomertijddag (23/25 uur). OpenMeteoClient::dailyForecast()
hernoemd naar forecast(), geeft nu {daily, hourly} terug. Elke dag in
de forecast-array krijgt een hourly-lijst.
Frontend: temperatuurlijn hergebruikt sparklinePath (nu ook als
sparklinePoints voor exacte uitlijning met de 12-koloms grid), regen
als kleine staafjes, windpijl (nieuw icoon) wijst waarheen de wind
waait. 20s-balk via CSS transition met reflow-restart. Servicewerker
naar v5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
56 lines
2.0 KiB
PHP
56 lines
2.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Weerscherm\Tests;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Weerscherm\OpenMeteoClient;
|
|
use Weerscherm\UpstreamException;
|
|
|
|
final class OpenMeteoClientTest extends TestCase
|
|
{
|
|
public function testRequestsSixDaysWithDailyAndHourlyVariables(): void
|
|
{
|
|
$http = new FakeHttp();
|
|
$http->queue(200, json_decode((string) file_get_contents(__DIR__ . '/fixtures/forecast.json'), true));
|
|
|
|
$result = (new OpenMeteoClient($http))->forecast(51.82, 6.55, 'Europe/Berlin');
|
|
|
|
self::assertCount(6, $result['daily']['time']);
|
|
self::assertCount(144, $result['hourly']['time']);
|
|
self::assertSame('https://api.open-meteo.com/v1/forecast', $http->requests[0]['url']);
|
|
self::assertSame([
|
|
'latitude' => 51.82,
|
|
'longitude' => 6.55,
|
|
'daily' => 'weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max',
|
|
'hourly' => 'temperature_2m,precipitation,wind_speed_10m,wind_direction_10m',
|
|
'timezone' => 'Europe/Berlin',
|
|
'forecast_days' => 6,
|
|
], $http->requests[0]['params']);
|
|
}
|
|
|
|
public function testErrorStatusIsUpstreamException(): void
|
|
{
|
|
$http = (new FakeHttp())->queue(429, ['reason' => 'rate limit']);
|
|
$this->expectException(UpstreamException::class);
|
|
(new OpenMeteoClient($http))->forecast(1, 2, 'UTC');
|
|
}
|
|
|
|
public function testMissingDailyIsUpstreamException(): void
|
|
{
|
|
$http = (new FakeHttp())->queue(200, ['hourly' => []]);
|
|
$this->expectException(UpstreamException::class);
|
|
(new OpenMeteoClient($http))->forecast(1, 2, 'UTC');
|
|
}
|
|
|
|
public function testMissingHourlyGivesEmptyHourlyBlockInsteadOfError(): void
|
|
{
|
|
$http = (new FakeHttp())->queue(200, ['daily' => ['time' => ['2026-09-11']]]);
|
|
|
|
$result = (new OpenMeteoClient($http))->forecast(1, 2, 'UTC');
|
|
|
|
self::assertSame([], $result['hourly']);
|
|
}
|
|
}
|