feat: api endpoints and xml sync for contingents data
This commit is contained in:
@@ -7,6 +7,7 @@ use App\BusProNet\XmlLoader\HotelLoader;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||
|
||||
class HotelDataLoaderTest extends TestCase
|
||||
{
|
||||
@@ -32,7 +33,7 @@ class HotelDataLoaderTest extends TestCase
|
||||
->with('hotel.xml')
|
||||
->willReturn($xmlContent);
|
||||
|
||||
$cache = new ArrayAdapter();
|
||||
$cache = new TagAwareAdapter(new ArrayAdapter());
|
||||
$loader = new HotelLoader($cache, $filesystem);
|
||||
|
||||
$hotels = $loader->loadAll();
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\BusProNet\XmlLoader\PickupLoader;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||
|
||||
class PickupDataLoaderTest extends TestCase
|
||||
{
|
||||
@@ -32,7 +33,7 @@ class PickupDataLoaderTest extends TestCase
|
||||
->with('zustiege.xml')
|
||||
->willReturn($xmlContent);
|
||||
|
||||
$cache = new ArrayAdapter();
|
||||
$cache = new TagAwareAdapter(new ArrayAdapter());
|
||||
$loader = new PickupLoader($cache, $filesystem);
|
||||
|
||||
$pickup = $loader->loadById(1);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BusProNet\Utility;
|
||||
|
||||
use App\BusProNet\Utility\BookingUrlUtility;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BookingUrlUtilityTest extends TestCase
|
||||
{
|
||||
public function testBuildUsesConfiguredDefaultBaseUrlAsAbsoluteFallback(): void
|
||||
{
|
||||
$utility = new BookingUrlUtility('https://my.ep-reisen.de');
|
||||
|
||||
$url = $utility->build(157047, 11606);
|
||||
|
||||
$this->assertSame(
|
||||
'https://my.ep-reisen.de/bookings/create?date_id=11606&hotel_id=157047',
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildUsesCustomBaseUrlWhenProvided(): void
|
||||
{
|
||||
$utility = new BookingUrlUtility('https://my.ep-reisen.de');
|
||||
|
||||
$url = $utility->build(157047, 11606, 'https://my.example.test/');
|
||||
|
||||
$this->assertSame(
|
||||
'https://my.example.test/bookings/create?date_id=11606&hotel_id=157047',
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildFallsBackToConfiguredDefaultWhenCustomBaseIsBlank(): void
|
||||
{
|
||||
$utility = new BookingUrlUtility('https://fallback.test');
|
||||
|
||||
$url = $utility->build(157047, 11606, ' ');
|
||||
|
||||
$this->assertSame(
|
||||
'https://fallback.test/bookings/create?date_id=11606&hotel_id=157047',
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildReturnsRelativeUrlWhenNoBaseUrlAvailable(): void
|
||||
{
|
||||
$utility = new BookingUrlUtility('');
|
||||
|
||||
$url = $utility->build(157047, 11606);
|
||||
|
||||
$this->assertSame('/bookings/create?date_id=11606&hotel_id=157047', $url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\BusProNet\XmlLoader;
|
||||
|
||||
use App\BusProNet\XmlLoader\ContingentLoader;
|
||||
use App\BusProNet\XmlParser\ContingentParser;
|
||||
use League\Flysystem\DirectoryListing;
|
||||
use League\Flysystem\FileAttributes;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
|
||||
class ContingentLoaderTest extends TestCase
|
||||
{
|
||||
public function testGenerateFilesMapBuildsMappingAndTagsCache(): void
|
||||
{
|
||||
$cache = $this->createMock(TagAwareCacheInterface::class);
|
||||
$filesystem = $this->createMock(FilesystemOperator::class);
|
||||
$parser = $this->createMock(ContingentParser::class);
|
||||
|
||||
$filesystem->method('listContents')->willReturn(new DirectoryListing([
|
||||
new FileAttributes('HotelZimmer_100.xml', 10),
|
||||
new FileAttributes('HotelZimmer_200.xml', 10),
|
||||
new FileAttributes('other.xml', 10),
|
||||
]));
|
||||
|
||||
$item = $this->createMock(ItemInterface::class);
|
||||
$item->expects($this->once())->method('expiresAfter')->with(3 * 60 * 60);
|
||||
$item->expects($this->once())->method('tag')->with(['xml-sync']);
|
||||
|
||||
$cache->expects($this->once())
|
||||
->method('get')
|
||||
->with('bpn_contingent_files', $this->isType('callable'))
|
||||
->willReturnCallback(fn (string $key, callable $callback) => $callback($item));
|
||||
|
||||
$loader = new ContingentLoader($parser, $cache, $filesystem);
|
||||
$map = $loader->generateFilesMap();
|
||||
|
||||
$this->assertSame([
|
||||
100 => 'HotelZimmer_100.xml',
|
||||
200 => 'HotelZimmer_200.xml',
|
||||
], $map);
|
||||
}
|
||||
|
||||
public function testLoadByHotelIdUsesRootLinkOneHopAndTagsCache(): void
|
||||
{
|
||||
$cache = $this->createMock(TagAwareCacheInterface::class);
|
||||
$filesystem = $this->createMock(FilesystemOperator::class);
|
||||
$parser = $this->createMock(ContingentParser::class);
|
||||
|
||||
$originalXml = '<hotelzimmer idbuspro_kontingent_aus="200"><unterbringungen /></hotelzimmer>';
|
||||
$linkedXml = '<hotelzimmer><unterbringungen /></hotelzimmer>';
|
||||
$parsed = ['roomTypes' => [], 'rows' => [['roomCode' => 'DZ']]];
|
||||
|
||||
$filesystem->method('listContents')->willReturn(new DirectoryListing([
|
||||
new FileAttributes('HotelZimmer_100.xml', 10),
|
||||
new FileAttributes('HotelZimmer_200.xml', 10),
|
||||
]));
|
||||
$filesystem->method('read')->willReturnMap([
|
||||
['HotelZimmer_100.xml', $originalXml],
|
||||
['HotelZimmer_200.xml', $linkedXml],
|
||||
]);
|
||||
|
||||
$mapItem = $this->createMock(ItemInterface::class);
|
||||
$mapItem->method('expiresAfter')->with(3 * 60 * 60);
|
||||
$mapItem->method('tag')->with(['xml-sync']);
|
||||
|
||||
$hotelItem = $this->createMock(ItemInterface::class);
|
||||
$hotelItem->method('expiresAfter')->with(3600);
|
||||
$hotelItem->method('tag')->with(['xml-sync']);
|
||||
|
||||
$cache->method('get')->willReturnCallback(
|
||||
function (string $key, callable $callback) use ($mapItem, $hotelItem) {
|
||||
if ('bpn_contingent_files' === $key) {
|
||||
return $callback($mapItem);
|
||||
}
|
||||
if ('contingent_hotel_100' === $key) {
|
||||
return $callback($hotelItem);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
$parser->expects($this->once())->method('parse')->with($linkedXml)->willReturn($parsed);
|
||||
|
||||
$loader = new ContingentLoader($parser, $cache, $filesystem);
|
||||
$result = $loader->loadByHotelId(100);
|
||||
|
||||
$this->assertSame($parsed, $result);
|
||||
}
|
||||
|
||||
public function testLoadByHotelIdReturnsEmptyWhenLinkedTargetIsMissing(): void
|
||||
{
|
||||
$cache = $this->createMock(TagAwareCacheInterface::class);
|
||||
$filesystem = $this->createMock(FilesystemOperator::class);
|
||||
$parser = $this->createMock(ContingentParser::class);
|
||||
|
||||
$xml = '<hotelzimmer idbuspro_kontingent_aus="999"><unterbringungen /></hotelzimmer>';
|
||||
|
||||
$filesystem->method('listContents')->willReturn(new DirectoryListing([
|
||||
new FileAttributes('HotelZimmer_100.xml', 10),
|
||||
]));
|
||||
$filesystem->method('read')->willReturn($xml);
|
||||
|
||||
$mapItem = $this->createMock(ItemInterface::class);
|
||||
$mapItem->method('expiresAfter')->with(3 * 60 * 60);
|
||||
$mapItem->method('tag')->with(['xml-sync']);
|
||||
|
||||
$hotelItem = $this->createMock(ItemInterface::class);
|
||||
$hotelItem->method('expiresAfter')->with(3600);
|
||||
$hotelItem->method('tag')->with(['xml-sync']);
|
||||
|
||||
$cache->method('get')->willReturnCallback(
|
||||
function (string $key, callable $callback) use ($mapItem, $hotelItem) {
|
||||
if ('bpn_contingent_files' === $key) {
|
||||
return $callback($mapItem);
|
||||
}
|
||||
if ('contingent_hotel_100' === $key) {
|
||||
return $callback($hotelItem);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
$parser->expects($this->never())->method('parse');
|
||||
|
||||
$loader = new ContingentLoader($parser, $cache, $filesystem);
|
||||
$result = $loader->loadByHotelId(100);
|
||||
|
||||
$this->assertSame(['roomTypes' => [], 'rows' => []], $result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\BusProNet\XmlParser;
|
||||
|
||||
use App\BusProNet\XmlParser\ContingentParser;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ContingentParserTest extends TestCase
|
||||
{
|
||||
private ContingentParser $parser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->parser = new ContingentParser();
|
||||
}
|
||||
|
||||
public function testParseBuildsRoomTypesAndRowsWithLinksAndControlRoom(): void
|
||||
{
|
||||
$xml = <<<'XML'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<hotelzimmer>
|
||||
<unterbringungen>
|
||||
<unterbringung idbuspro="10" code="DZ" zimmerbezeichnung="Double Room" pax_max="2" />
|
||||
<unterbringung idbuspro="11" code="BelKal" zimmerbezeichnung="Control" pax_max="1" />
|
||||
<unterbringung idbuspro="12" code="PDGS" zimmerbezeichnung="Pseudo" pax_max="1" />
|
||||
<unterbringung idbuspro="13" code="EZ" zimmerbezeichnung="Single Room" pax_max="1" idbuspro_kontingent_aus="10" />
|
||||
</unterbringungen>
|
||||
<kapazitaeten>
|
||||
<kapazitaet termin="24.02.2026">
|
||||
<zimmerliste>
|
||||
<zimmer idbuspro="10" kontingent="3" frei="1" status="" abpreis="100,00" abpreis_naechte="2" abpreis_verlaengerung="15,00" abpreis_verlaengerung_naechte="1" />
|
||||
<zimmer idbuspro="11" kontingent="1" frei="1" status="A" abpreis="0" abpreis_naechte="0" abpreis_verlaengerung="0" abpreis_verlaengerung_naechte="0" />
|
||||
</zimmerliste>
|
||||
</kapazitaet>
|
||||
</kapazitaeten>
|
||||
</hotelzimmer>
|
||||
XML;
|
||||
|
||||
$result = $this->parser->parse($xml);
|
||||
|
||||
$this->assertArrayHasKey('roomTypes', $result);
|
||||
$this->assertArrayHasKey('rows', $result);
|
||||
$this->assertCount(3, $result['roomTypes']); // PDGS skipped
|
||||
$this->assertCount(3, $result['rows']);
|
||||
|
||||
$rowsByCode = [];
|
||||
foreach ($result['rows'] as $row) {
|
||||
$rowsByCode[$row['roomCode']] = $row;
|
||||
}
|
||||
|
||||
$this->assertSame('OK', $rowsByCode['DZ']['status']);
|
||||
$this->assertSame(6, $rowsByCode['DZ']['pax']);
|
||||
$this->assertSame(2, $rowsByCode['DZ']['available']);
|
||||
$this->assertSame(100.0, $rowsByCode['DZ']['minPrice']);
|
||||
$this->assertSame(2, $rowsByCode['DZ']['minNights']);
|
||||
$this->assertSame(15.0, $rowsByCode['DZ']['additionalNightMinPrice']);
|
||||
$this->assertSame(1, $rowsByCode['DZ']['additionalNightMinNights']);
|
||||
$this->assertFalse($rowsByCode['DZ']['isControlRoom']);
|
||||
|
||||
// Linked room uses DZ contingent node but its own pax
|
||||
$this->assertSame('Single Room', $rowsByCode['EZ']['roomLabel']);
|
||||
$this->assertSame(3, $rowsByCode['EZ']['pax']);
|
||||
$this->assertSame(1, $rowsByCode['EZ']['available']);
|
||||
|
||||
$this->assertSame('ON_REQUEST', $rowsByCode['BelKal']['status']);
|
||||
$this->assertSame(0, $rowsByCode['BelKal']['pax']);
|
||||
$this->assertSame(0, $rowsByCode['BelKal']['available']);
|
||||
$this->assertTrue($rowsByCode['BelKal']['isControlRoom']);
|
||||
}
|
||||
|
||||
public function testParseHandlesZeroValuesAndBlockedStatus(): void
|
||||
{
|
||||
$xml = <<<'XML'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<hotelzimmer>
|
||||
<unterbringungen>
|
||||
<unterbringung idbuspro="20" code="DZ" zimmerbezeichnung="Double Room" pax_max="2" />
|
||||
</unterbringungen>
|
||||
<kapazitaeten>
|
||||
<kapazitaet termin="25.02.2026">
|
||||
<zimmerliste>
|
||||
<zimmer idbuspro="20" kontingent="0" frei="0" status="S" abpreis="0" abpreis_naechte="0" abpreis_verlaengerung="0" abpreis_verlaengerung_naechte="0" />
|
||||
</zimmerliste>
|
||||
</kapazitaet>
|
||||
</kapazitaeten>
|
||||
</hotelzimmer>
|
||||
XML;
|
||||
|
||||
$result = $this->parser->parse($xml);
|
||||
$row = $result['rows'][0];
|
||||
|
||||
$this->assertSame('BLOCKED', $row['status']);
|
||||
$this->assertSame(0, $row['pax']);
|
||||
$this->assertSame(0, $row['available']);
|
||||
$this->assertSame(0.0, $row['minPrice']);
|
||||
$this->assertSame(0, $row['minNights']);
|
||||
$this->assertSame(0.0, $row['additionalNightMinPrice']);
|
||||
$this->assertSame(0, $row['additionalNightMinNights']);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ namespace App\Tests\Command;
|
||||
|
||||
use App\BusProNet\XmlLoader\TravelLoader;
|
||||
use App\Command\BpnXmlSyncCommand;
|
||||
use App\Service\BpnXmlSyncManager;
|
||||
use App\Service\BpnXmlSnapshotRefreshManager;
|
||||
use App\Service\TravelDataProvider;
|
||||
use App\Service\TravelSnapshotManager;
|
||||
use League\Flysystem\DirectoryListing;
|
||||
@@ -14,17 +16,21 @@ use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
|
||||
class BpnXmlSyncCommandTest extends TestCase
|
||||
{
|
||||
private FilesystemOperator $xmlSource;
|
||||
private FilesystemOperator $xmlExport;
|
||||
private CacheInterface $cache;
|
||||
private FilesystemOperator $xmlSourceContingents;
|
||||
private FilesystemOperator $xmlExportContingents;
|
||||
private TagAwareCacheInterface $cache;
|
||||
private LoggerInterface $logger;
|
||||
private TravelLoader $travelLoader;
|
||||
private TravelDataProvider $travelDataService;
|
||||
private TravelSnapshotManager $travelSnapshotService;
|
||||
private BpnXmlSnapshotRefreshManager $snapshotRefreshManager;
|
||||
private BpnXmlSyncManager $syncManager;
|
||||
private BpnXmlSyncCommand $command;
|
||||
|
||||
protected function tearDown(): void
|
||||
@@ -39,40 +45,55 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
{
|
||||
$this->xmlSource = $this->createMock(FilesystemOperator::class);
|
||||
$this->xmlExport = $this->createMock(FilesystemOperator::class);
|
||||
$this->cache = $this->createMock(CacheInterface::class);
|
||||
$this->xmlSourceContingents = $this->createMock(FilesystemOperator::class);
|
||||
$this->xmlExportContingents = $this->createMock(FilesystemOperator::class);
|
||||
$this->cache = $this->createMock(TagAwareCacheInterface::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->travelLoader = $this->createMock(TravelLoader::class);
|
||||
$this->travelDataService = $this->createMock(TravelDataProvider::class);
|
||||
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
|
||||
|
||||
$this->command = new BpnXmlSyncCommand(
|
||||
$this->xmlSource,
|
||||
$this->xmlExport,
|
||||
$this->cache,
|
||||
$this->logger,
|
||||
$this->snapshotRefreshManager = new BpnXmlSnapshotRefreshManager(
|
||||
$this->travelLoader,
|
||||
$this->travelDataService,
|
||||
$this->travelSnapshotService,
|
||||
$this->logger,
|
||||
);
|
||||
|
||||
$this->syncManager = new BpnXmlSyncManager(
|
||||
$this->xmlSource,
|
||||
$this->xmlExport,
|
||||
$this->xmlSourceContingents,
|
||||
$this->xmlExportContingents,
|
||||
$this->cache,
|
||||
$this->logger,
|
||||
);
|
||||
|
||||
$this->command = new BpnXmlSyncCommand(
|
||||
$this->syncManager,
|
||||
$this->snapshotRefreshManager,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote newer than local → files downloaded → syncSnapshotsFromXml() called once.
|
||||
* Remote newer than local -> files downloaded -> syncSnapshotsFromXml() called once.
|
||||
*/
|
||||
public function testSnapshotSyncIsTriggeredAfterSuccessfulFileDownload(): void
|
||||
{
|
||||
$this->xmlSource->method('read')
|
||||
->willReturn("24.03.2026 12:00:00\nExport\n3 Dateien\n");
|
||||
$newerTimestamp = "24.03.2026 12:00:00\nExport\n3 Dateien\n";
|
||||
$olderTimestamp = "24.03.2026 10:00:00\nExport\n3 Dateien\n";
|
||||
|
||||
$this->xmlSource->method('read')->willReturn($newerTimestamp);
|
||||
$this->xmlExport->method('fileExists')->willReturn(true);
|
||||
$this->xmlExport->method('read')
|
||||
->willReturn("24.03.2026 10:00:00\nExport\n3 Dateien\n");
|
||||
$this->xmlExport->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlSource->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExport->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
// No remote files to copy (keeps syncFiles() trivial while still completing).
|
||||
$this->xmlSource->method('listContents')
|
||||
->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExport->method('listContents')
|
||||
->willReturn(new DirectoryListing([]));
|
||||
$this->xmlSourceContingents->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlExportContingents->method('fileExists')->willReturn(true);
|
||||
$this->xmlExportContingents->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlSourceContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExportContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
$fileMap = [
|
||||
101 => ['hotels' => ['H1' => null, 'H2' => null]],
|
||||
@@ -87,6 +108,15 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
->with($fileMap, $this->isInstanceOf(\Closure::class))
|
||||
->willReturn(['processed' => 2, 'failed' => 0]);
|
||||
|
||||
$this->travelSnapshotService->expects($this->once())
|
||||
->method('purgeOrphanedFutureSnapshots')
|
||||
->with([101, 102])
|
||||
->willReturn(0);
|
||||
|
||||
$this->cache->expects($this->once())
|
||||
->method('invalidateTags')
|
||||
->with(['xml-sync']);
|
||||
|
||||
$tester = new CommandTester($this->command);
|
||||
$tester->execute([]);
|
||||
|
||||
@@ -94,7 +124,7 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Local already up to date → early return before snapshot code is reached.
|
||||
* Local already up to date -> early return before snapshot code is reached.
|
||||
*/
|
||||
public function testSnapshotSyncIsSkippedWhenLocalDataIsUpToDate(): void
|
||||
{
|
||||
@@ -102,9 +132,19 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
$this->xmlSource->method('read')->willReturn($sameTimestamp);
|
||||
$this->xmlExport->method('fileExists')->willReturn(true);
|
||||
$this->xmlExport->method('read')->willReturn($sameTimestamp);
|
||||
$this->xmlSource->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExport->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
$this->xmlSourceContingents->method('read')->willReturn($sameTimestamp);
|
||||
$this->xmlExportContingents->method('fileExists')->willReturn(true);
|
||||
$this->xmlExportContingents->method('read')->willReturn($sameTimestamp);
|
||||
$this->xmlSourceContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExportContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
$this->travelLoader->expects($this->never())->method('generateFilesMap');
|
||||
$this->travelDataService->expects($this->never())->method('syncSnapshotsFromXml');
|
||||
$this->travelSnapshotService->expects($this->never())->method('purgeOrphanedFutureSnapshots');
|
||||
$this->cache->expects($this->never())->method('invalidateTags');
|
||||
|
||||
$tester = new CommandTester($this->command);
|
||||
$tester->execute([]);
|
||||
@@ -113,22 +153,25 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* generateFilesMap() throws → warning logged, syncSnapshotsFromXml() never called,
|
||||
* generateFilesMap() throws -> warning logged, syncSnapshotsFromXml() never called,
|
||||
* command still returns SUCCESS.
|
||||
*/
|
||||
public function testSnapshotSyncIsSkippedAndWarningLoggedWhenFileMapGenerationFails(): void
|
||||
{
|
||||
$this->xmlSource->method('read')
|
||||
->willReturn("24.03.2026 12:00:00\nExport\n3 Dateien\n");
|
||||
$newerTimestamp = "24.03.2026 12:00:00\nExport\n3 Dateien\n";
|
||||
$olderTimestamp = "24.03.2026 10:00:00\nExport\n3 Dateien\n";
|
||||
|
||||
$this->xmlSource->method('read')->willReturn($newerTimestamp);
|
||||
$this->xmlExport->method('fileExists')->willReturn(true);
|
||||
$this->xmlExport->method('read')
|
||||
->willReturn("24.03.2026 10:00:00\nExport\n3 Dateien\n");
|
||||
$this->xmlExport->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlSource->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExport->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
$this->xmlSource->method('listContents')
|
||||
->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExport->method('listContents')
|
||||
->willReturn(new DirectoryListing([]));
|
||||
$this->xmlSourceContingents->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlExportContingents->method('fileExists')->willReturn(true);
|
||||
$this->xmlExportContingents->method('read')->willReturn($olderTimestamp);
|
||||
$this->xmlSourceContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
$this->xmlExportContingents->method('listContents')->willReturn(new DirectoryListing([]));
|
||||
|
||||
$this->travelLoader->expects($this->once())
|
||||
->method('generateFilesMap')
|
||||
@@ -142,6 +185,8 @@ class BpnXmlSyncCommandTest extends TestCase
|
||||
);
|
||||
|
||||
$this->travelDataService->expects($this->never())->method('syncSnapshotsFromXml');
|
||||
$this->travelSnapshotService->expects($this->never())->method('purgeOrphanedFutureSnapshots');
|
||||
$this->cache->expects($this->once())->method('invalidateTags')->with(['xml-sync']);
|
||||
|
||||
$tester = new CommandTester($this->command);
|
||||
$tester->execute([]);
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Controller\Api;
|
||||
|
||||
use App\Controller\Api\ContingentController;
|
||||
use App\Service\ContingentDataService;
|
||||
use App\Service\TravelDataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
class ContingentControllerTest extends TestCase
|
||||
{
|
||||
public function testByDateQueryResolvesCodeReferences(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->once())
|
||||
->method('mapHotelCodeToId')
|
||||
->with('SBW-HOTEL')
|
||||
->willReturn(157047);
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->once())
|
||||
->method('mapDateCodeToId')
|
||||
->with('SBW131225')
|
||||
->willReturn(11606);
|
||||
|
||||
$contingentDataService
|
||||
->expects($this->once())
|
||||
->method('getAvailableContingents')
|
||||
->with(157047, 11606)
|
||||
->willReturn([]);
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents', 'GET', [
|
||||
'hotelRef' => 'SBW-HOTEL',
|
||||
'dateRef' => 'sbw-13/12/25',
|
||||
]);
|
||||
|
||||
$response = $controller->byDate($request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testByDateQueryRequiresDateReference(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents', 'GET', [
|
||||
'hotelRef' => '157047',
|
||||
]);
|
||||
|
||||
$response = $controller->byDate($request);
|
||||
$payload = json_decode((string) $response->getContent(), true);
|
||||
|
||||
$this->assertSame(400, $response->getStatusCode());
|
||||
$this->assertSame('dateRef is required', $payload['error']);
|
||||
}
|
||||
|
||||
public function testByDateQueryReturnsNotFoundForUnknownDateReference(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->once())
|
||||
->method('mapHotelCodeToId')
|
||||
->with('SBW-HOTEL')
|
||||
->willReturn(157047);
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->once())
|
||||
->method('mapDateCodeToId')
|
||||
->with('UNKNOWN')
|
||||
->willReturn(null);
|
||||
|
||||
$contingentDataService
|
||||
->expects($this->never())
|
||||
->method('getAvailableContingents');
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents', 'GET', [
|
||||
'hotelRef' => 'SBW-HOTEL',
|
||||
'dateRef' => 'unknown',
|
||||
]);
|
||||
|
||||
$response = $controller->byDate($request);
|
||||
$payload = json_decode((string) $response->getContent(), true);
|
||||
|
||||
$this->assertSame(404, $response->getStatusCode());
|
||||
$this->assertSame('Not found', $payload['message']);
|
||||
}
|
||||
|
||||
public function testRoomsQueryRequiresDateRange(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents/rooms', 'GET', [
|
||||
'hotelRef' => '157047',
|
||||
'dateRef' => '11606',
|
||||
]);
|
||||
|
||||
$response = $controller->rooms($request);
|
||||
$payload = json_decode((string) $response->getContent(), true);
|
||||
|
||||
$this->assertSame(400, $response->getStatusCode());
|
||||
$this->assertSame('dateFrom and dateTo are required', $payload['error']);
|
||||
}
|
||||
|
||||
public function testByDateQueryUsesNumericReferencesAsIdentifiers(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->never())
|
||||
->method('mapHotelCodeToId');
|
||||
|
||||
$travelDataProvider
|
||||
->expects($this->never())
|
||||
->method('mapDateCodeToId');
|
||||
|
||||
$contingentDataService
|
||||
->expects($this->once())
|
||||
->method('getAvailableContingents')
|
||||
->with(157047, 11606)
|
||||
->willReturn([]);
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents', 'GET', [
|
||||
'hotelRef' => '157047',
|
||||
'dateRef' => '11606',
|
||||
]);
|
||||
$response = $controller->byDate($request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testRoomsQueryForwardsOptionalMyEpUrl(): void
|
||||
{
|
||||
$contingentDataService = $this->createMock(ContingentDataService::class);
|
||||
$travelDataProvider = $this->createMock(TravelDataProvider::class);
|
||||
|
||||
$contingentDataService
|
||||
->expects($this->once())
|
||||
->method('getAvailableRooms')
|
||||
->with('2026-03-01', '2026-03-08', 157047, 11606, 'https://my.ep-reisen.de')
|
||||
->willReturn([]);
|
||||
|
||||
$controller = $this->createController($contingentDataService, $travelDataProvider);
|
||||
$request = Request::create('/api/contingents/rooms', 'GET', [
|
||||
'hotelRef' => '157047',
|
||||
'dateRef' => '11606',
|
||||
'dateFrom' => '2026-03-01',
|
||||
'dateTo' => '2026-03-08',
|
||||
'my_ep_url' => 'https://my.ep-reisen.de',
|
||||
]);
|
||||
|
||||
$response = $controller->rooms($request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
private function createController(
|
||||
ContingentDataService $contingentDataService,
|
||||
TravelDataProvider $travelDataProvider,
|
||||
): ContingentController {
|
||||
$controller = new ContingentController($contingentDataService, $travelDataProvider);
|
||||
|
||||
$container = new Container();
|
||||
$container->set('serializer', new Serializer([new ObjectNormalizer()], [new JsonEncoder()]));
|
||||
$controller->setContainer($container);
|
||||
|
||||
return $controller;
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,7 @@ class ParticipantEditDtoTest extends TestCase
|
||||
$mockServiceAgeEvaluator = $this->createMock(ServiceAgeEvaluator::class);
|
||||
|
||||
// Create custom validator factory that can inject dependencies
|
||||
$validatorFactory = new class(
|
||||
$mockVoucherService,
|
||||
$mockPriceCalculatorService,
|
||||
$mockParticipantEligibilityChecker,
|
||||
$mockServiceAgeEvaluator
|
||||
) implements ConstraintValidatorFactoryInterface {
|
||||
$validatorFactory = new class($mockVoucherService, $mockPriceCalculatorService, $mockParticipantEligibilityChecker, $mockServiceAgeEvaluator) implements ConstraintValidatorFactoryInterface {
|
||||
public function __construct(
|
||||
private readonly VoucherValidator $voucherService,
|
||||
private readonly BookingPriceCalculator $priceCalculatorService,
|
||||
|
||||
@@ -13,8 +13,8 @@ use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantFieldOptionsProvider;
|
||||
use App\Service\BookingPriceCalculator;
|
||||
use App\Service\InsuranceManager;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantFieldOptionsProvider;
|
||||
use App\Service\BookingPriceCalculator;
|
||||
use App\Service\InsuranceManager;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
use App\Service\ServiceLabelFormatter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\Service\BookingStatusRuleRegistry;
|
||||
use App\BusProNet\XmlLoader\AgencyLoader;
|
||||
|
||||
@@ -8,13 +8,13 @@ use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingCreateContext;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Form\Model\ParticipantCardDataDto;
|
||||
use App\Form\Model\ParticipantCardPriceDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\ParticipantCardAssembler;
|
||||
use App\Service\BookingSummaryAssembler;
|
||||
use App\Service\BookingPriceCalculator;
|
||||
use App\Service\BookingSummaryAssembler;
|
||||
use App\Service\ParticipantCardAssembler;
|
||||
use App\Service\RoomPricingCalculator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\MutableData;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingMutabilityDto;
|
||||
use App\Form\Model\BookingEditContext;
|
||||
use App\Form\Model\BookingMutabilityDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Service\BookingEditContextFactory;
|
||||
use App\Service\BookingSummaryAssembler;
|
||||
|
||||
@@ -13,9 +13,9 @@ use App\Entity\User;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Repository\BookingEditDraftRepository;
|
||||
use App\Service\BookingChangeTracker;
|
||||
use App\Service\BookingEditDraftManager;
|
||||
use App\Service\BookingEditDraftMerger;
|
||||
use App\Service\BookingChangeTracker;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
@@ -422,11 +422,11 @@ class BookingEditSubmitterTest extends TestCase
|
||||
|
||||
private function createService(
|
||||
?\App\BusProNet\ApiClient $apiClient = null,
|
||||
?BookingEditDataLoader $dataLoader = null,
|
||||
?BookingEditDraftManager $draftService = null,
|
||||
?TravelDataProvider $travelDataService = null,
|
||||
?BookingEditSubmitGuard $submitGuard = null,
|
||||
?BookingSessionManager $bookingSessionService = null,
|
||||
?BookingEditDataLoader $dataLoader = null,
|
||||
?BookingEditDraftManager $draftService = null,
|
||||
?TravelDataProvider $travelDataService = null,
|
||||
?BookingEditSubmitGuard $submitGuard = null,
|
||||
?BookingSessionManager $bookingSessionService = null,
|
||||
): BookingEditSubmitter {
|
||||
return new BookingEditSubmitter(
|
||||
$apiClient ?? $this->createMock(\App\BusProNet\ApiClient::class),
|
||||
|
||||
@@ -11,8 +11,8 @@ use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingPricingAssembler;
|
||||
use App\Service\BookingPriceMismatchAnalyzer;
|
||||
use App\Service\BookingPricingAssembler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BookingPriceMismatchAnalyzerTest extends TestCase
|
||||
|
||||
@@ -4,9 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\DataProvider\CountryDataProvider;
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\XmlLoader\HotelLoader;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
@@ -15,8 +17,6 @@ use App\Service\BookingPriceCalculator;
|
||||
use App\Service\BookingPricingAssembler;
|
||||
use App\Service\BookingSummaryAssembler;
|
||||
use App\Service\CmsDataProvider;
|
||||
use App\BusProNet\DataProvider\CountryDataProvider;
|
||||
use App\BusProNet\XmlLoader\HotelLoader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\Utility\BookingUrlUtility;
|
||||
use App\BusProNet\XmlLoader\ContingentLoader;
|
||||
use App\BusProNet\XmlLoader\TravelLoader;
|
||||
use App\Service\ContingentDataService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ContingentDataServiceTest extends TestCase
|
||||
{
|
||||
private ContingentLoader $contingentLoader;
|
||||
private TravelLoader $travelLoader;
|
||||
private ContingentDataService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->contingentLoader = $this->createMock(ContingentLoader::class);
|
||||
$this->travelLoader = $this->createMock(TravelLoader::class);
|
||||
|
||||
$this->service = new ContingentDataService(
|
||||
$this->contingentLoader,
|
||||
$this->travelLoader,
|
||||
new BookingUrlUtility('https://my.ep-reisen.de'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetAvailableContingentsAggregatesAndAppliesBelKalOverride(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->dateFrom = new \DateTimeImmutable('2026-02-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2026-02-03');
|
||||
|
||||
$this->travelLoader->method('loadById')->with(123, 10)->willReturn($travel);
|
||||
$this->contingentLoader->method('loadByHotelId')->with(10)->willReturn([
|
||||
'rows' => [
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-01'),
|
||||
'isControlRoom' => false,
|
||||
'available' => 4,
|
||||
'pax' => 8,
|
||||
'minNights' => 3,
|
||||
'status' => 'OK',
|
||||
],
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-01'),
|
||||
'isControlRoom' => false,
|
||||
'available' => 2,
|
||||
'pax' => 4,
|
||||
'minNights' => 2,
|
||||
'status' => 'OK',
|
||||
],
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-01'),
|
||||
'isControlRoom' => true,
|
||||
'available' => 0,
|
||||
'pax' => 0,
|
||||
'minNights' => null,
|
||||
'status' => 'BLOCKED',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = $this->service->getAvailableContingents(10, 123);
|
||||
|
||||
$this->assertArrayHasKey('2026-02-01', $result);
|
||||
$summary = $result['2026-02-01'];
|
||||
$this->assertSame('2026-02-01', $summary->date);
|
||||
$this->assertSame(2, $summary->minNights);
|
||||
$this->assertSame(12, $summary->capacity);
|
||||
$this->assertSame(0, $summary->total); // BelKal override
|
||||
}
|
||||
|
||||
public function testGetAvailableRoomsRejectsRangeOutsideTravelPeriod(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->dateFrom = new \DateTimeImmutable('2026-02-10');
|
||||
$travel->dateTo = new \DateTimeImmutable('2026-02-20');
|
||||
|
||||
$this->travelLoader->method('loadById')->with(123, 10)->willReturn($travel);
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Requested range must be within the travel date range');
|
||||
|
||||
$this->service->getAvailableRooms('2026-02-01', '2026-02-05', 10, 123);
|
||||
}
|
||||
|
||||
public function testGetAvailableRoomsCalculatesPriceAndAppliesBelKalStatusOverride(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->dateFrom = new \DateTimeImmutable('2026-02-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2026-02-10');
|
||||
|
||||
$this->travelLoader->method('loadById')->with(123, 10)->willReturn($travel);
|
||||
$this->contingentLoader->method('loadByHotelId')->with(10)->willReturn([
|
||||
'rows' => [
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-03'),
|
||||
'isControlRoom' => true,
|
||||
'status' => 'ON_REQUEST',
|
||||
],
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-03'),
|
||||
'isControlRoom' => false,
|
||||
'roomCode' => 'DZ',
|
||||
'roomLabel' => 'Double',
|
||||
'pax' => 4,
|
||||
'available' => 2,
|
||||
'status' => 'OK',
|
||||
'minPrice' => 100.0,
|
||||
'minNights' => 2,
|
||||
'additionalNightMinPrice' => 30.0,
|
||||
'additionalNightMinNights' => 1,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = $this->service->getAvailableRooms('2026-02-03', '2026-02-06', 10, 123);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$room = $result[0];
|
||||
$this->assertSame('2026-02-03', $room->date);
|
||||
$this->assertSame('ON_REQUEST', $room->status);
|
||||
$this->assertSame(0, $room->available); // overridden
|
||||
$this->assertSame(130.0, $room->priceForSelection); // 100 + (3-2)*30
|
||||
$this->assertSame('https://my.ep-reisen.de/bookings/create?date_id=123&hotel_id=10', $room->bookingUrl);
|
||||
}
|
||||
|
||||
public function testGetCalendarEventsUsesWorstStatusAndExcludesControlRoomFromSums(): void
|
||||
{
|
||||
$this->contingentLoader->method('loadByHotelId')->with(10)->willReturn([
|
||||
'rows' => [
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-03'),
|
||||
'isControlRoom' => false,
|
||||
'status' => 'OK',
|
||||
'pax' => 6,
|
||||
'available' => 2,
|
||||
],
|
||||
[
|
||||
'date' => new \DateTimeImmutable('2026-02-03'),
|
||||
'isControlRoom' => true,
|
||||
'status' => 'BLOCKED',
|
||||
'pax' => 0,
|
||||
'available' => 0,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$events = $this->service->getCalendarEvents(10, '2026-02-01', '2026-02-10');
|
||||
|
||||
$this->assertCount(1, $events);
|
||||
$event = $events[0];
|
||||
$this->assertSame('2026-02-03', $event->date);
|
||||
$this->assertSame('BLOCKED', $event->status);
|
||||
$this->assertSame(6, $event->pax);
|
||||
$this->assertSame(2, $event->available);
|
||||
}
|
||||
}
|
||||
@@ -176,4 +176,3 @@ class MandatoryAdditionalServicesSelectedValidatorTest extends ConstraintValidat
|
||||
return $service;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
use App\Kernel;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__) . '/.env');
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
$kernel->boot();
|
||||
|
||||
Reference in New Issue
Block a user