430 lines
19 KiB
PHP
430 lines
19 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Tests\BusProNet\XmlParser;
|
||
|
||
use App\BusProNet\XmlParser\TravelParser;
|
||
use PHPUnit\Framework\TestCase;
|
||
use Symfony\Component\DomCrawler\Crawler;
|
||
|
||
class TravelParserTest extends TestCase
|
||
{
|
||
private TravelParser $parser;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->parser = new TravelParser();
|
||
}
|
||
|
||
public function testParseServiceDescriptions(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR" erstellt_am="27.08.2025 09:09:40">
|
||
<termin id="1" idbuspro="11946" idprodukt="2187" termin="01.01.2030" bis="06.01.2030" code="SSTMR010130" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183658" unterart="SON" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="False" pflicht="False">
|
||
<text>Begleitperson (gemäß Gruppenregelung)</text>
|
||
<hinweis>Gilt für Betreuende, Lehrkräfte, Gruppenleitungen oder andere berechtigte Begleitpersonen laut Buchungsregelung. Bei Unklarheiten bitte zunächst ohne Angabe buchen – eine spätere Ergänzung ist möglich.</hinweis>
|
||
<preis>0,00</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
<leistung id="2" idbuspro="183659" unterart="SON" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="True" pflicht="False">
|
||
<text>Bettwäsche-Set inkl. Handtuch</text>
|
||
<hinweis>Das Bettwäsche-Set umfasst Spannbettlaken, Kissen- und Deckenbezug. Beim Handtuch handelt es sich um ein Duschtuch (ca. 70x140cm).</hinweis>
|
||
<preis>13,90</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
<leistung id="3" idbuspro="183656" unterart="SPA" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="False" pflicht="False">
|
||
<text>Skipass 6 Tage (Erwachsene) (Jahrgang 1926-2006) inkl. Anreisetag</text>
|
||
<hinweis_stamm>JG:1926-2006</hinweis_stamm>
|
||
<preis>59,00</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
<leistung id="4" idbuspro="183660" unterart="VPF" termin="01.01.2030" bis="01.01.2030" automatisch_buchen="False" pflicht="False">
|
||
<text>Frühstück am Anreisetag</text>
|
||
<preis>8,90</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<lei_befoerderung>
|
||
<leistung id="5" idbuspro="183649" unterart="BUS" termin="30.12.2029" bis="01.01.2030" automatisch_buchen="False" pflicht="True">
|
||
<text>Bus-Hinfahrt</text>
|
||
<hinweis>Bus fährt nur bei ausreichender Teilnehmerzahl</hinweis>
|
||
<preis>0,00</preis>
|
||
<status>Frei</status>
|
||
<richtung>HIN</richtung>
|
||
</leistung>
|
||
</lei_befoerderung>
|
||
<hotel id="1" code="SSTGR" idbuspro="157047" ankunft="01.01.2030" abreise="06.01.2030">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer Dusche/WC" MinPax="4" MaxPax="4" naechte="5" kat="GR" vpcode="Lt. Auss." idbuspro_vp="2" vptext="Laut Ausschreibung" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
// Test additional services descriptions
|
||
$additionalServices = $travel->additionalServices;
|
||
|
||
// Service with description (ID 183658 - Begleitperson)
|
||
$this->assertArrayHasKey(183658, $additionalServices);
|
||
$begleitpersonService = $additionalServices[183658];
|
||
$this->assertSame('Begleitperson (gemäß Gruppenregelung)', $begleitpersonService->label);
|
||
$this->assertSame(
|
||
'Gilt für Betreuende, Lehrkräfte, Gruppenleitungen oder andere berechtigte Begleitpersonen laut Buchungsregelung. Bei Unklarheiten bitte zunächst ohne Angabe buchen – eine spätere Ergänzung ist möglich.',
|
||
$begleitpersonService->description
|
||
);
|
||
|
||
// Service with description (ID 183659 - Bettwäsche)
|
||
$this->assertArrayHasKey(183659, $additionalServices);
|
||
$bettwascheService = $additionalServices[183659];
|
||
$this->assertSame('Bettwäsche-Set inkl. Handtuch', $bettwascheService->label);
|
||
$this->assertSame(
|
||
'Das Bettwäsche-Set umfasst Spannbettlaken, Kissen- und Deckenbezug. Beim Handtuch handelt es sich um ein Duschtuch (ca. 70x140cm).',
|
||
$bettwascheService->description
|
||
);
|
||
$this->assertTrue($bettwascheService->autoBook);
|
||
|
||
// Service without description (ID 183656 - Skipass, has hinweis_stamm but no hinweis)
|
||
$this->assertArrayHasKey(183656, $additionalServices);
|
||
$skipassService = $additionalServices[183656];
|
||
$this->assertSame('Skipass 6 Tage (Erwachsene) (Jahrgang 1926-2006) inkl. Anreisetag', $skipassService->label);
|
||
$this->assertNull($skipassService->description); // No hinweis node for this service
|
||
$this->assertSame('JG:1926-2006', $skipassService->rawAgeConstraintData); // But has age constraints
|
||
|
||
// Service without description (ID 183660 - Frühstück)
|
||
$this->assertArrayHasKey(183660, $additionalServices);
|
||
$fruehstueckService = $additionalServices[183660];
|
||
$this->assertSame('Frühstück am Anreisetag', $fruehstueckService->label);
|
||
$this->assertNull($fruehstueckService->description); // No hinweis node
|
||
|
||
// Test transportation services descriptions
|
||
$transportationServices = $travel->transportationServices;
|
||
$this->assertArrayHasKey(183649, $transportationServices);
|
||
$busService = $transportationServices[183649];
|
||
$this->assertSame('Bus-Hinfahrt', $busService->label);
|
||
$this->assertSame('Bus fährt nur bei ausreichender Teilnehmerzahl', $busService->description);
|
||
$this->assertFalse($busService->autoBook);
|
||
|
||
$this->assertSame(2187, $travel->productId);
|
||
}
|
||
|
||
public function testProductIdIsNullWhenAttributeAbsent(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<hotel id="1" idbuspro="157047">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$this->assertNull($travel->productId);
|
||
}
|
||
|
||
public function testParseServiceWithoutDescriptions(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183660" unterart="VPF">
|
||
<text>Service without description</text>
|
||
<preis>8,90</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<hotel id="1" idbuspro="157047">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$additionalServices = $travel->additionalServices;
|
||
$this->assertArrayHasKey(183660, $additionalServices);
|
||
$service = $additionalServices[183660];
|
||
$this->assertSame('Service without description', $service->label);
|
||
$this->assertNull($service->description); // No hinweis node should result in null description
|
||
}
|
||
|
||
public function testParseServiceWithLowerCaseAutoBookFlag(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183660" unterart="VPF" automatisch_buchen="true" pflicht="False">
|
||
<text>Service with auto-book</text>
|
||
<preis>8,90</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<hotel id="1" idbuspro="157047">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$additionalServices = $travel->additionalServices;
|
||
$this->assertArrayHasKey(183660, $additionalServices);
|
||
$this->assertTrue($additionalServices[183660]->autoBook);
|
||
}
|
||
|
||
public function testParseTravelWithoutHotelNode(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2293" code="">
|
||
<termin id="1" idbuspro="12162" idprodukt="2293" termin="28.03.2026" bis="04.04.2026" code="PGTSGI280326" reiseart="P">
|
||
<text>PG TSG Irlich</text>
|
||
<abpreis>-50,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="188858" unterart="SON" termin="28.03.2026" bis="04.04.2026" automatisch_buchen="False" pflicht="False">
|
||
<text>7 Übernachtungen im 3* Posthotel</text>
|
||
<preis>999,00</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<lei_befoerderung>
|
||
<leistung id="1" idbuspro="188850" unterart="BUS" termin="27.03.2026" bis="28.03.2026" automatisch_buchen="False" pflicht="False">
|
||
<text>Bus-Hinfahrt</text>
|
||
<preis>5,00</preis>
|
||
<status>Frei</status>
|
||
<richtung>HIN</richtung>
|
||
</leistung>
|
||
</lei_befoerderung>
|
||
<zustiege>
|
||
<zustieg id="1" idbuspro="873" zeit="27.03.2026 21:30:00" />
|
||
</zustiege>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$this->assertSame(12162, $travel->id);
|
||
$this->assertSame('PGTSGI280326', $travel->code);
|
||
$this->assertNull($travel->hotelId);
|
||
$this->assertSame([], $travel->rooms);
|
||
$this->assertArrayHasKey(188858, $travel->additionalServices);
|
||
$this->assertArrayHasKey(188850, $travel->transportationServices);
|
||
$this->assertArrayHasKey(873, $travel->pickups);
|
||
}
|
||
|
||
public function testParseAdditionalServicesUsesTerminLevelOnly(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183656" unterart="SPA" automatisch_buchen="True" pflicht="False">
|
||
<text>Skipass 6 Tage</text>
|
||
<preis>59,00</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<hotel id="1" idbuspro="157047">
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183656" unterart="SPA" automatisch_buchen="False" pflicht="False">
|
||
<text>Skipass 6 Tage Hotel</text>
|
||
<preis>59,00</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$additionalServices = $travel->additionalServices;
|
||
$this->assertArrayHasKey(183656, $additionalServices);
|
||
$this->assertTrue($additionalServices[183656]->autoBook);
|
||
$this->assertSame('Skipass 6 Tage', $additionalServices[183656]->label);
|
||
}
|
||
|
||
public function testParseServiceWithEmptyDescription(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<lei_sonstiges>
|
||
<leistung id="1" idbuspro="183660" unterart="VPF">
|
||
<text>Service with empty description</text>
|
||
<hinweis></hinweis>
|
||
<preis>8,90</preis>
|
||
<status>Frei</status>
|
||
</leistung>
|
||
</lei_sonstiges>
|
||
<hotel id="1" idbuspro="157047">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$additionalServices = $travel->additionalServices;
|
||
$this->assertArrayHasKey(183660, $additionalServices);
|
||
$service = $additionalServices[183660];
|
||
$this->assertSame('Service with empty description', $service->label);
|
||
$this->assertNull($service->description); // Empty hinweis should result in null description
|
||
}
|
||
|
||
public function testParseTravelWithoutStatus(): void
|
||
{
|
||
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
|
||
<reisen>
|
||
<reise id="1" idbuspro="2187" code="SSTMR">
|
||
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
|
||
<text>Test Travel</text>
|
||
<abpreis>659,00</abpreis>
|
||
<hotel id="1" idbuspro="157047">
|
||
<zimmer>
|
||
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
|
||
</zimmer>
|
||
</hotel>
|
||
</termin>
|
||
</reise>
|
||
</reisen>';
|
||
|
||
$crawler = new Crawler($xmlContent);
|
||
$travelNode = $crawler->filterXPath('//reise/termin')->first();
|
||
$travel = $this->parser->parse($travelNode);
|
||
|
||
$this->assertNull($travel->status); // No status_hin node should result in null
|
||
}
|
||
|
||
public function testGetPickupsAdjustsOvernightTimes(): void
|
||
{
|
||
$xml = '<?xml version="1.0" encoding="utf-8"?>
|
||
<zustiege>
|
||
<zustieg idbuspro="1" zeit="30.12.2029 18:00" preis="0,00" />
|
||
<zustieg idbuspro="2" zeit="30.12.2029 20:30" preis="0,00" />
|
||
<zustieg idbuspro="3" zeit="23:00" preis="0,00" />
|
||
<zustieg idbuspro="4" zeit="01:00" preis="0,00" />
|
||
</zustiege>';
|
||
|
||
$crawler = new Crawler($xml);
|
||
$defaultDate = new \DateTimeImmutable('2029-12-30');
|
||
|
||
$pickups = $this->parser->getPickups(
|
||
$crawler->filterXPath('//zustiege/zustieg'),
|
||
$defaultDate,
|
||
true
|
||
);
|
||
|
||
// Full-datetime entries keep their original date
|
||
$this->assertSame('2029-12-30 18:00', $pickups[1]->time->format('Y-m-d H:i'));
|
||
$this->assertSame('2029-12-30 20:30', $pickups[2]->time->format('Y-m-d H:i'));
|
||
|
||
// Time-only "23:00" is after latest full-datetime (20:30), so stays on same day
|
||
$this->assertSame('2029-12-30 23:00', $pickups[3]->time->format('Y-m-d H:i'));
|
||
|
||
// Time-only "01:00" is before latest full-datetime (20:30), so gets +1 day
|
||
$this->assertSame('2029-12-31 01:00', $pickups[4]->time->format('Y-m-d H:i'));
|
||
}
|
||
|
||
public function testGetPickupsDoesNotAdjustWhenAllEntriesAreFullDatetime(): void
|
||
{
|
||
$xml = '<?xml version="1.0" encoding="utf-8"?>
|
||
<zustiege>
|
||
<zustieg idbuspro="1" zeit="30.12.2029 18:00" preis="0,00" />
|
||
<zustieg idbuspro="2" zeit="30.12.2029 20:30" preis="0,00" />
|
||
</zustiege>';
|
||
|
||
$crawler = new Crawler($xml);
|
||
$defaultDate = new \DateTimeImmutable('2029-12-30');
|
||
|
||
$pickups = $this->parser->getPickups(
|
||
$crawler->filterXPath('//zustiege/zustieg'),
|
||
$defaultDate,
|
||
true
|
||
);
|
||
|
||
$this->assertSame('2029-12-30 18:00', $pickups[1]->time->format('Y-m-d H:i'));
|
||
$this->assertSame('2029-12-30 20:30', $pickups[2]->time->format('Y-m-d H:i'));
|
||
}
|
||
|
||
public function testGetPickupsDoesNotAdjustWhenAllEntriesAreTimeOnly(): void
|
||
{
|
||
$xml = '<?xml version="1.0" encoding="utf-8"?>
|
||
<zustiege>
|
||
<zustieg idbuspro="1" zeit="18:00" preis="0,00" />
|
||
<zustieg idbuspro="2" zeit="01:00" preis="0,00" />
|
||
</zustiege>';
|
||
|
||
$crawler = new Crawler($xml);
|
||
$defaultDate = new \DateTimeImmutable('2029-12-30');
|
||
|
||
$pickups = $this->parser->getPickups(
|
||
$crawler->filterXPath('//zustiege/zustieg'),
|
||
$defaultDate,
|
||
true
|
||
);
|
||
|
||
// No full-datetime reference entries, so no adjustment happens
|
||
$this->assertSame('2029-12-30 18:00', $pickups[1]->time->format('Y-m-d H:i'));
|
||
$this->assertSame('2029-12-30 01:00', $pickups[2]->time->format('Y-m-d H:i'));
|
||
}
|
||
}
|