The Teamer app groups hotels by cost unit and renders a flag per destination, and cannot derive either from what /api/hotels returns today. costUnit is the raw kostenstelle and stays a string: only 281 of 1931 hotels carry one and some hold a space-separated list such as "84 85". countryCode goes through CountryCodeMapper, whose table is part of the API contract: BusPro's land column mixes ISO-2, ISO-3 and European vehicle registration codes for the same country, and a wrong entry mislabels a destination's flag silently. An unmappable token is null and warns on the bpn channel, never a guess. country itself is untouched -- it remains the join key into /api/countries[].token. Co-Authored-By: Claude Opus 5 <[email protected]>
96 lines
3.0 KiB
PHP
96 lines
3.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\BusProNet\XmlLoader;
|
|
|
|
use App\BusProNet\Model\Travel;
|
|
use App\BusProNet\Utility\CountryCodeMapper;
|
|
use App\BusProNet\XmlCrawlerFactory;
|
|
use App\BusProNet\XmlLoader\HotelLoader;
|
|
use League\Flysystem\FilesystemOperator;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\NullLogger;
|
|
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
|
use Symfony\Component\DomCrawler\Crawler;
|
|
|
|
class HotelLoaderTest extends TestCase
|
|
{
|
|
public function testPatchHotelDetailsSkipsWhenTravelHasNoHotelId(): void
|
|
{
|
|
$filesystem = $this->createMock(FilesystemOperator::class);
|
|
$filesystem
|
|
->expects($this->never())
|
|
->method('read');
|
|
|
|
$loader = $this->createLoader($filesystem);
|
|
$travel = new Travel();
|
|
$travel->hotelId = null;
|
|
$travel->hotel = null;
|
|
|
|
$loader->patchHotelDetails($travel);
|
|
|
|
$this->assertNull($travel->hotel);
|
|
}
|
|
|
|
public function testParseXmlMapsCostUnitAndCountryCode(): void
|
|
{
|
|
$hotel = $this->createLoader()->parseXml($this->hotelNode(
|
|
'<kostenstelle>83</kostenstelle><land>CH</land>'
|
|
));
|
|
|
|
$this->assertSame('83', $hotel->costUnit);
|
|
$this->assertSame('CH', $hotel->country, 'the raw token is the join key and must not be rewritten');
|
|
$this->assertSame('ch', $hotel->countryCode);
|
|
}
|
|
|
|
public function testParseXmlKeepsAListOfCostUnitsAsAString(): void
|
|
{
|
|
$hotel = $this->createLoader()->parseXml($this->hotelNode(
|
|
'<kostenstelle>84 85</kostenstelle><land>D</land>'
|
|
));
|
|
|
|
$this->assertSame('84 85', $hotel->costUnit);
|
|
}
|
|
|
|
public function testParseXmlLeavesCostUnitNullWhenAbsent(): void
|
|
{
|
|
$hotel = $this->createLoader()->parseXml($this->hotelNode('<land>D</land>'));
|
|
|
|
$this->assertNull($hotel->costUnit);
|
|
$this->assertSame('de', $hotel->countryCode);
|
|
}
|
|
|
|
public function testParseXmlLeavesBothCountryFieldsNullWhenLandIsAbsent(): void
|
|
{
|
|
$hotel = $this->createLoader()->parseXml($this->hotelNode('<name>Ohne Land</name>'));
|
|
|
|
$this->assertNull($hotel->country);
|
|
$this->assertNull($hotel->countryCode);
|
|
}
|
|
|
|
public function testParseXmlLeavesCountryCodeNullForAnUnmappableToken(): void
|
|
{
|
|
$hotel = $this->createLoader()->parseXml($this->hotelNode('<land>XX</land>'));
|
|
|
|
$this->assertSame('XX', $hotel->country);
|
|
$this->assertNull($hotel->countryCode, 'an unknown token must never be guessed into a code');
|
|
}
|
|
|
|
private function hotelNode(string $children): Crawler
|
|
{
|
|
$xml = sprintf('<hotels><hotel idbuspro="1" code="TEST">%s</hotel></hotels>', $children);
|
|
|
|
return XmlCrawlerFactory::create($xml)->filterXPath('//hotels/hotel');
|
|
}
|
|
|
|
private function createLoader(?FilesystemOperator $filesystem = null): HotelLoader
|
|
{
|
|
return new HotelLoader(
|
|
new CountryCodeMapper(new NullLogger()),
|
|
new ArrayAdapter(),
|
|
$filesystem ?? $this->createStub(FilesystemOperator::class),
|
|
);
|
|
}
|
|
}
|