Files
myep-team/tests/MyEp/Import/DestinationRowTest.php

223 lines
8.0 KiB
PHP

<?php
namespace App\Tests\MyEp\Import;
use App\BusProNet\Model\Pickup;
use App\MyEp\Import\DestinationRow;
use App\MyEp\Import\InvalidRowException;
use PHPUnit\Framework\TestCase;
class DestinationRowTest extends TestCase
{
public function testARowIsMapped(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$this->assertEquals(11886, $row->busProId);
$this->assertEquals('LPJPGJ040426', $row->code);
$this->assertEquals('Lenzerheide - Sportclub Jenatsch', $row->product);
$this->assertEquals('2026-04-04', $row->dateFrom->format('Y-m-d'));
$this->assertEquals('2026-04-11', $row->dateTo->format('Y-m-d'));
$this->assertEquals(163959, $row->hotelBusProId);
$this->assertEquals('LPJGR', $row->hotelCode);
$this->assertEquals('Sportclub Jenatsch', $row->hotelName);
$this->assertEquals('4711', $row->costUnit);
$this->assertEquals('ch', $row->country);
}
public function testTheIsoCountryCodeIsStoredRatherThanTheRawToken(): void
{
$row = DestinationRow::fromArray($this->getRowData([
'hotel' => ['busProId' => 1, 'country' => 'AUT', 'countryCode' => 'AT'],
]));
$this->assertEquals('at', $row->country);
}
public function testAnUnresolvedCountryCodeIsStoredAsNull(): void
{
$row = DestinationRow::fromArray($this->getRowData([
'hotel' => ['busProId' => 1, 'country' => 'SLO'],
]));
$this->assertNull($row->country);
}
public function testTheDatesAreReadAsWholeDays(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$this->assertEquals('00:00:00', $row->dateFrom->format('H:i:s'));
$this->assertEquals('00:00:00', $row->dateTo->format('H:i:s'));
}
public function testPickupsKeepTheShapeTheDestinationTableStores(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$this->assertEquals([[
'busProId' => 4,
'city' => 'Karlsruhe',
'street' => 'A5 - Autohof Bruchsal',
'time' => '23:59',
'datetime' => '2026-04-04T23:59:00+02:00',
]], $row->pickups);
}
/**
* MyE&P sends `time` as a bare HH:MM, so this is the only field that says which calendar day
* a boarding point belongs to. Dropping it left App\Service\Assignment\PickupDepartureOrder
* with no anchor and a naive clock sort, which puts a 00:30 stop before the 22:00 departure.
*/
public function testTheResolvedDepartureTimestampIsCarried(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$this->assertSame('2026-04-04T23:59:00+02:00', $row->pickups[0]['datetime']);
}
public function testAPickupWithoutAResolvedTimestampIsStillCarried(): void
{
$row = DestinationRow::fromArray($this->getRowData(['pickups' => [
['busProId' => 27, 'city' => 'Ulm', 'street' => 'Busparkplatz', 'time' => null],
]]));
$this->assertNull($row->pickups[0]['datetime']);
$this->assertCount(1, $row->pickups);
}
public function testStoredPickupsCanBeReadBackAsModels(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$pickup = Pickup::fromArray(json_decode(json_encode($row->pickups), true)[0]);
$this->assertEquals(4, $pickup->getId());
$this->assertEquals('Karlsruhe', $pickup->getCity());
$this->assertEquals('23:59', $pickup->getTime());
}
public function testPickupsWithoutAnIdAreDropped(): void
{
$row = DestinationRow::fromArray($this->getRowData(['pickups' => [
['city' => 'Karlsruhe'],
['busProId' => 0, 'city' => 'Köln'],
['busProId' => 27, 'city' => 'Bonn', 'street' => 'ZOB', 'time' => '02:15'],
]]));
$this->assertCount(1, $row->pickups);
$this->assertEquals(27, $row->pickups[0]['busProId']);
}
public function testARowWithoutPickupsIsAccepted(): void
{
$row = DestinationRow::fromArray($this->getRowData(['pickups' => []]));
$this->assertEquals([], $row->pickups);
}
public function testARowWithoutAHotelIsRejected(): void
{
$this->expectException(InvalidRowException::class);
DestinationRow::fromArray($this->getRowData(['hotel' => null]));
}
public function testARowWithoutIdsIsRejected(): void
{
$this->expectException(InvalidRowException::class);
DestinationRow::fromArray($this->getRowData(['busProId' => 0]));
}
public function testARowWithAnUnreadableDateIsRejected(): void
{
$this->expectException(InvalidRowException::class);
DestinationRow::fromArray($this->getRowData(['dateFrom' => '04.04.2026']));
}
public function testTheInsertPayloadCarriesEveryColumn(): void
{
$row = DestinationRow::fromArray($this->getRowData());
$payload = $row->toInsertPayload(new \DateTimeImmutable('2026-08-13 03:00:00'));
$this->assertEquals([
'code' => 'LPJPGJ040426',
'bus_pro_id' => 11886,
'date_from' => '2026-04-04',
'date_to' => '2026-04-11',
'product' => 'Lenzerheide - Sportclub Jenatsch',
'hotel' => 'Sportclub Jenatsch',
'hotel_code' => 'LPJGR',
'hotel_bus_pro_id' => 163959,
'pickups' => '[{"busProId":4,"city":"Karlsruhe","street":"A5 - Autohof Bruchsal","time":"23:59","datetime":"2026-04-04T23:59:00+02:00"}]',
'cost_unit' => '4711',
'country' => 'ch',
'product_code' => 'LPJPGJ',
'imported_at' => '2026-08-13 03:00:00',
'created_at' => '2026-08-13 03:00:00',
], $payload);
}
public function testTheUpdatePayloadRefreshesTheHotelName(): void
{
// a renamed house has to reach existing rows, or it drops out of the house matching
$row = DestinationRow::fromArray($this->getRowData());
$payload = $row->toUpdatePayload(new \DateTimeImmutable('2026-08-13 03:00:00'));
$this->assertEquals('Sportclub Jenatsch', $payload['hotel']);
$this->assertEquals('LPJGR', $payload['hotel_code']);
$this->assertEquals('2026-08-13 03:00:00', $payload['updated_at']);
$this->assertEquals('2026-08-13 03:00:00', $payload['imported_at']);
$this->assertEquals('LPJPGJ', $payload['product_code']);
$this->assertArrayNotHasKey('created_at', $payload);
$this->assertArrayNotHasKey('bus_pro_id', $payload);
$this->assertArrayNotHasKey('hotel_bus_pro_id', $payload);
}
public function testTheProductCodeIsTrimmed(): void
{
// upstream carries "DPWAST " with a trailing blank
$row = DestinationRow::fromArray($this->getRowData(['productCode' => 'DPWAST ']));
$this->assertSame('DPWAST', $row->productCode);
}
public function testAMissingOrEmptyProductCodeIsNull(): void
{
$this->assertNull(DestinationRow::fromArray($this->getRowData(['productCode' => null]))->productCode);
$this->assertNull(DestinationRow::fromArray($this->getRowData(['productCode' => ' ']))->productCode);
}
private function getRowData(array $overrides = []): array
{
return array_merge([
'busProId' => 11886,
'code' => 'LPJPGJ040426',
'productCode' => 'LPJPGJ',
'product' => 'Lenzerheide - Sportclub Jenatsch',
'dateFrom' => '2026-04-04',
'dateTo' => '2026-04-11',
'hotel' => [
'busProId' => 163959,
'code' => 'LPJGR',
'name' => 'Sportclub Jenatsch',
'costUnit' => '4711',
'country' => 'CH',
'countryCode' => 'ch',
],
'pickups' => [[
'busProId' => 4,
'code' => '298',
'city' => 'Karlsruhe',
'street' => 'A5 - Autohof Bruchsal',
'time' => '23:59',
'datetime' => '2026-04-04T23:59:00+02:00',
]],
], $overrides);
}
}