initital commit

This commit is contained in:
2025-01-26 21:17:23 +01:00
commit 2a7345ba56
72 changed files with 9458 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\DTO\Factory;
use App\DTO\Factory\GEOPointFactory;
use App\DTO\GEOPoint;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class GEOPointFactoryTest extends TestCase
{
public function testCreateFromStringWillAcceptGeopoints(): void
{
$service = $this->createService();
$geopoint = $service->createFromString('20.303', '30.22');
$this->assertInstanceOf(GEOPoint::class, $geopoint);
$this->assertEquals(20.303, $geopoint->latitude);
$this->assertEquals(30.22, $geopoint->longitude);
}
#[DataProvider('invalidGeoPointProvider')]
public function testCreateFromStringInvalidString(string $lat, string $lon): void
{
$service = $this->createService();
$geopoint = $service->createFromString($lat, $lon);
$this->assertEquals(null, $geopoint);
}
/**
* @return array<int|string, array<int|string, string>>
*/
public static function invalidGeoPointProvider(): array
{
return [
['foo', 'foo'],
['33.33', 'foo'],
['foo', '33.33'],
['foo3', 'foo3'],
['33.33', 'foo3'],
['foo3', '33.33'],
];
}
public function createService(): GEOPointFactory
{
return new GEOPointFactory();
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\DTO\Factory;
use App\DTO\Factory\WeatherAtTimePointFactory;
use DateTimeZone;
use PHPUnit\Framework\TestCase;
class WeatherAtTimePointFactoryTest extends TestCase
{
public function testDifferentTimeZone(): void
{
$oldTz = date_default_timezone_get();
// POSIX convention change +/- sign
date_default_timezone_set('Etc/GMT-2');
$service = $this->createService();
$weatherAtTimePoint = $service->createFromUntrustedDataWithTz(
new \DateTimeImmutable('2020-01-01 00:00:00', new DateTimeZone('Etc/GMT+0')),
0,
0,
0
);
$this->assertEquals('Etc/GMT-2', $weatherAtTimePoint->date->getTimezone()->getName());
$this->assertEquals('2020-01-01 02:00:00', $weatherAtTimePoint->date->format('Y-m-d H:i:s'));
date_default_timezone_set($oldTz);
}
public function testCreateWithYYYYmmddFormat(): void
{
$tz = date_default_timezone_get();
// POSIX convention change +/- sign
$service = $this->createService();
$weatherAtTimePoint = $service->createFromUntrustedWithDateYYYYmmdd(
"2020-01-01",
0,
0,
0
);
$this->assertEquals($tz, $weatherAtTimePoint->date->getTimezone()->getName());
$this->assertEquals('2020-01-01 00:00:00', $weatherAtTimePoint->date->format('Y-m-d H:i:s'));
}
private function createService(): WeatherAtTimePointFactory
{
return new WeatherAtTimePointFactory();
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Services;
use App\DTO\Factory\GEOPointFactory;
use App\DTO\GEOPoint;
use App\Services\GeoPointResolverService;
use PHPUnit\Framework\TestCase;
class GeoPointResolverServiceTest extends TestCase
{
public function testGetCityNameFromGeoPoint(): void
{
$this->assertEquals('Prague', $this->createService()->getCityNameFromGeoPoint(
new GEOPoint(50.073658, 14.418540)
));
$this->assertEquals('New York', $this->createService()->getCityNameFromGeoPoint(
new GEOPoint(40.730610, -73.935242)
));
}
public function testGetCityNameFromGeoPointInvalid(): void
{
$this->assertEquals(null, $this->createService()->getCityNameFromGeoPoint(
new GEOPoint(40.730610, -1)
));
}
public function testGetGeoPointFromCityName(): void
{
$resolvedGeoPoint = $this->createService()->getGeoPointFromCityName('Prague');
if ($resolvedGeoPoint === null) {
$this->fail('Geo point not found');
}
$this->assertEquals(50.073658, $resolvedGeoPoint->latitude);
$this->assertEquals(14.418540, $resolvedGeoPoint->longitude);
$resolvedGeoPoint = $this->createService()->getGeoPointFromCityName('New York');
if ($resolvedGeoPoint === null) {
$this->fail('Geo point not found');
}
$this->assertEquals(40.730610, $resolvedGeoPoint->latitude);
$this->assertEquals(-73.935242, $resolvedGeoPoint->longitude);
}
public function testGetGeoPointFromCityNameInvalid(): void
{
$resolvedGeoPoint = $this->createService()->getGeoPointFromCityName('Atlantis');
$this->assertNull($resolvedGeoPoint);
}
public function createService(): GeoPointResolverService
{
$geoPointFactory = \Mockery::mock(GEOPointFactory::class);
// @phpstan-ignore-next-line method.notFound
$geoPointFactory
->shouldReceive('createFromString')
->with('50.073658', '14.418540')
->andReturn(new GEOPoint(50.073658, 14.418540));
// @phpstan-ignore-next-line method.notFound
$geoPointFactory
->shouldReceive('createFromString')
->with('40.730610', '-73.935242')
->andReturn(new GEOPoint(40.730610, -73.935242));
return new GeoPointResolverService(
[
'Prague' => [
'lat' => '50.073658',
'lon' => '14.418540'
],
'New York' => [
'lat' => '40.730610',
'lon' => '-73.935242'
]
],
$geoPointFactory // @phpstan-ignore argument.type
);
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Services\Granularity;
use App\DTO\WeatherAtTimePoint;
use App\Services\Granularity\DayWeatherByDate;
use PHPUnit\Framework\TestCase;
class DayWeatherByDateTest extends TestCase
{
public function testCalculateFromMultipleRecordsInDay(): void
{
$data = [
new WeatherAtTimePoint(
new \DateTimeImmutable('2020-01-01 01:01:01'),
0.2,
5.2,
5
),
new WeatherAtTimePoint(
new \DateTimeImmutable('2020-01-01 01:01:10'),
-1.1,
6.2,
10.5
)
];
$service = $this->createService();
$calculatedData = $service->calculateFromMultipleRecords($data);
$this->assertEquals(1, count($calculatedData));
$this->assertEquals("2020-01-01 00:00:00", $calculatedData[0]->date->format('Y-m-d H:i:s'));
$this->assertEquals($calculatedData[0]->min, -1.1);
$this->assertEquals($calculatedData[0]->max, 6.2);
$temperatureValue = ($data[0]->value + $data[1]->value) / 2;
$this->assertEquals($calculatedData[0]->value, $temperatureValue);
}
public function testCalculateFromSingleRecordsInDay(): void
{
$data = [
new WeatherAtTimePoint(
new \DateTimeImmutable('2020-01-01 01:01:01'),
0.2,
5.2,
5
),
new WeatherAtTimePoint(
new \DateTimeImmutable('2020-01-02 01:01:10'),
-1.1,
6.2,
10.5
)
];
$service = $this->createService();
$calculatedData = $service->calculateFromMultipleRecords($data);
$this->assertEquals(2, count($calculatedData));
$this->assertEquals("2020-01-01 00:00:00", $calculatedData[0]->date->format('Y-m-d H:i:s'));
$this->assertEquals($calculatedData[0]->min, 0.2);
$this->assertEquals($calculatedData[0]->max, 5.2);
$this->assertEquals($calculatedData[0]->value, 5);
$this->assertEquals("2020-01-02 00:00:00", $calculatedData[1]->date->format('Y-m-d H:i:s'));
$this->assertEquals($calculatedData[1]->min, -1.1);
$this->assertEquals($calculatedData[1]->max, 6.2);
$this->assertEquals($calculatedData[1]->value, 10.5);
}
private function createService(): DayWeatherByDate
{
return new DayWeatherByDate();
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Services\Remote\Mapper;
use App\DTO\Factory\WeatherAtTimePointFactory;
use App\DTO\WeatherAtTimePoint;
use App\Services\Remote\Mapper\OpenMeteoMapper;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Validation;
class OpenMeteoMapperTest extends TestCase
{
public function testDailyData(): void
{
$data = '{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso8601","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3],"temperature_2m_min":[3.1,2.3]}}';
$json = json_decode($data, true);
$service = $this->createService();
$data = $service->mapResponseIntoTimePoints($json, 'daily');
$this->assertEquals("2025-01-26", $data[0]->date->format('Y-m-d'));
$this->assertEquals(3.1, $data[0]->min);
$this->assertEquals(9.1, $data[0]->max);
$this->assertEquals("2025-01-27", $data[1]->date->format('Y-m-d'));
$this->assertEquals(2.3, $data[1]->min);
$this->assertEquals(8.3, $data[1]->max);
}
#[DataProvider('invalidDataGenerator')]
public function testDailyInvalidDataTimeFormat(string $json): void
{
$this->expectException(\InvalidArgumentException::class);
$json = json_decode($json, true);
$service = $this->createService();
$service->mapResponseIntoTimePoints($json, 'daily');
}
/**
* @return array<int, array<int, string>>
*/
public static function invalidDataGenerator(): array
{
return [
// invalid date format (no iso)
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso86013","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3],"temperature_2m_min":[3.1,2.3]}}'],
// invalid temp format (from)
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso8601","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3],"temperature_2m_min":[3.1,"fff"]}}'],
// invalid temp format (to)
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso8601","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":["fff",8.3],"temperature_2m_min":[3.1,2.3]}}'],
// missing daily units
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3],"temperature_2m_min":[3.1,2.3]}}'],
// missing tempature min
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso8601","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3]}}'],
// different units
['{"latitude":50.08,"longitude":14.439999,"generationtime_ms":0.027298927307128906,"utc_offset_seconds":3600,"timezone":"Europe/Prague","timezone_abbreviation":"GMT+1","elevation":219.0,"daily_units":{"time":"iso8601","temperature_2m_max":"°K","temperature_2m_min":"°K"},"daily":{"time":["2025-01-26","2025-01-27"],"temperature_2m_max":[9.1,8.3],"temperature_2m_min":[3.1,2.3]}}']
];
}
private function createService(): OpenMeteoMapper
{
$mock = \Mockery::mock(WeatherAtTimePointFactory::class);
$firstDate = \DateTimeImmutable::createFromFormat('Y-m-d', '2025-01-26');
$secondDate = \DateTimeImmutable::createFromFormat('Y-m-d', '2025-01-27');
if ($firstDate === false || $secondDate === false) {
$this->fail('Can\'t create date');
}
// @phpstan-ignore-next-line method.notFound
$mock->shouldReceive('createFromUntrustedWithDateYYYYmmdd')->with('2025-01-26', '3.1', '9.1', null)->andReturn(
new WeatherAtTimePoint(
$firstDate,
3.1,
9.1,
null
)
);
// @phpstan-ignore-next-line method.notFound
$mock->shouldReceive('createFromUntrustedWithDateYYYYmmdd')->with('2025-01-27', '2.3', '8.3', null)->andReturn(
new WeatherAtTimePoint(
$secondDate,
2.3,
8.3,
null
)
);
return new OpenMeteoMapper(
$mock, // @phpstan-ignore argument.type
Validation::createValidator()
);
}
}