298 lines
9.9 KiB
PHP
298 lines
9.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Service;
|
|
|
|
use App\BusProNet\XmlLoader\HotelLoader;
|
|
use App\BusProNet\XmlLoader\TravelLoader;
|
|
use App\Service\TravelIndex;
|
|
use App\Service\TravelSnapshotManager;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class TravelIndexTest extends TestCase
|
|
{
|
|
private TravelIndex $service;
|
|
private TravelLoader $travelLoader;
|
|
private HotelLoader $hotelLoader;
|
|
private TravelSnapshotManager $travelSnapshotService;
|
|
private LoggerInterface $logger;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->travelLoader = $this->createMock(TravelLoader::class);
|
|
$this->hotelLoader = $this->createMock(HotelLoader::class);
|
|
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
|
|
$this->logger = $this->createMock(LoggerInterface::class);
|
|
|
|
$this->service = new TravelIndex(
|
|
$this->travelLoader,
|
|
$this->hotelLoader,
|
|
$this->travelSnapshotService,
|
|
$this->logger,
|
|
);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// generateFilesMap
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testGenerateFilesMapMergesXmlAndSnapshotMappings(): void
|
|
{
|
|
$dateId = 100;
|
|
$hotelId = 200;
|
|
$snapshotOnlyDateId = 300;
|
|
|
|
$xmlMapping = [
|
|
$dateId => ['id' => $dateId, 'code' => 'ABC', 'hotels' => [
|
|
$hotelId => ['id' => $hotelId],
|
|
]],
|
|
];
|
|
$snapshotMapping = [
|
|
$dateId => ['id' => $dateId, 'code' => 'ABC', 'hotels' => [
|
|
999 => ['id' => 999], // extra hotel only in snapshot
|
|
]],
|
|
$snapshotOnlyDateId => ['id' => $snapshotOnlyDateId, 'code' => 'XYZ', 'hotels' => []],
|
|
];
|
|
|
|
$this->travelLoader->method('generateFilesMap')->willReturn($xmlMapping);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn($snapshotMapping);
|
|
|
|
$result = $this->service->generateFilesMap();
|
|
|
|
// XML entry is preserved
|
|
$this->assertArrayHasKey($dateId, $result);
|
|
// Snapshot-only date is merged in
|
|
$this->assertArrayHasKey($snapshotOnlyDateId, $result);
|
|
// Extra hotel from snapshot is added to the XML date entry
|
|
$this->assertArrayHasKey(999, $result[$dateId]['hotels']);
|
|
// Original XML hotel is still present
|
|
$this->assertArrayHasKey($hotelId, $result[$dateId]['hotels']);
|
|
}
|
|
|
|
public function testGenerateFilesMapIsMemoizedWithinRequest(): void
|
|
{
|
|
$mapping = [100 => ['id' => 100, 'code' => 'A', 'hotels' => []]];
|
|
|
|
$this->travelLoader
|
|
->expects($this->once())
|
|
->method('generateFilesMap')
|
|
->willReturn($mapping);
|
|
|
|
$this->travelSnapshotService
|
|
->expects($this->once())
|
|
->method('generateMapping')
|
|
->willReturn([]);
|
|
|
|
// Two calls — loaders invoked only once
|
|
$this->service->generateFilesMap();
|
|
$result = $this->service->generateFilesMap();
|
|
|
|
$this->assertSame($mapping, $result);
|
|
}
|
|
|
|
public function testGenerateFilesMapFallsBackToSnapshotsOnXmlFailure(): void
|
|
{
|
|
$snapshotMapping = [100 => ['id' => 100, 'code' => 'A', 'hotels' => []]];
|
|
|
|
$this->travelLoader
|
|
->method('generateFilesMap')
|
|
->willThrowException(new \RuntimeException('Filesystem error'));
|
|
|
|
$this->travelSnapshotService
|
|
->method('generateMapping')
|
|
->willReturn($snapshotMapping);
|
|
|
|
$this->logger
|
|
->expects($this->once())
|
|
->method('warning')
|
|
->with('Failed to generate XML files mapping, fallback to snapshots only', $this->arrayHasKey('error'));
|
|
|
|
$result = $this->service->generateFilesMap();
|
|
|
|
$this->assertSame($snapshotMapping, $result);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// mapDateCodeToId
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMapDateCodeToIdReturnsMatchingId(): void
|
|
{
|
|
$this->travelLoader->method('generateFilesMap')->willReturn([
|
|
42 => ['id' => 42, 'code' => 'WI25', 'hotels' => []],
|
|
]);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
|
|
$result = $this->service->mapDateCodeToId('WI25');
|
|
|
|
$this->assertSame(42, $result);
|
|
}
|
|
|
|
public function testMapDateCodeToIdReturnsNullWhenNotFound(): void
|
|
{
|
|
$this->travelLoader->method('generateFilesMap')->willReturn([]);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
|
|
$result = $this->service->mapDateCodeToId('UNKNOWN');
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// mapHotelCodeToId
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMapHotelCodeToIdDelegatesToHotelLoader(): void
|
|
{
|
|
$this->hotelLoader
|
|
->expects($this->once())
|
|
->method('mapCodeToId')
|
|
->with('HTL001')
|
|
->willReturn(77);
|
|
|
|
$result = $this->service->mapHotelCodeToId('HTL001');
|
|
|
|
$this->assertSame(77, $result);
|
|
}
|
|
|
|
public function testMapHotelCodeToIdReturnsNullOnException(): void
|
|
{
|
|
$this->hotelLoader
|
|
->method('mapCodeToId')
|
|
->willThrowException(new \RuntimeException('Not found'));
|
|
|
|
$result = $this->service->mapHotelCodeToId('MISSING');
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// mapDateIdToProductId
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMapDateIdToProductIdPrefersSnapshot(): void
|
|
{
|
|
$this->travelSnapshotService
|
|
->expects($this->once())
|
|
->method('findProductIdByDateId')
|
|
->with(100)
|
|
->willReturn(999);
|
|
|
|
$this->travelLoader
|
|
->expects($this->never())
|
|
->method('mapDateIdToProductId');
|
|
|
|
$result = $this->service->mapDateIdToProductId(100);
|
|
|
|
$this->assertSame(999, $result);
|
|
}
|
|
|
|
public function testMapDateIdToProductIdFallsBackToLoaderWhenSnapshotReturnsNull(): void
|
|
{
|
|
$this->travelSnapshotService
|
|
->expects($this->once())
|
|
->method('findProductIdByDateId')
|
|
->with(100)
|
|
->willReturn(null);
|
|
|
|
$this->travelLoader
|
|
->expects($this->once())
|
|
->method('mapDateIdToProductId')
|
|
->with(100)
|
|
->willReturn(42);
|
|
|
|
$result = $this->service->mapDateIdToProductId(100);
|
|
|
|
$this->assertSame(42, $result);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// existsLocally
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testExistsLocallyTrueFromSnapshot(): void
|
|
{
|
|
$dateId = 12345;
|
|
$hotelId = 67890;
|
|
|
|
$this->travelSnapshotService
|
|
->expects($this->once())
|
|
->method('exists')
|
|
->with($dateId, $hotelId)
|
|
->willReturn(true);
|
|
|
|
$this->travelLoader
|
|
->expects($this->never())
|
|
->method('generateFilesMap');
|
|
|
|
$this->assertTrue($this->service->existsLocally($dateId, $hotelId));
|
|
}
|
|
|
|
public function testExistsLocallyTrueFromXmlMap(): void
|
|
{
|
|
$dateId = 12345;
|
|
$hotelId = 67890;
|
|
$mapping = [
|
|
$dateId => [
|
|
'id' => $dateId,
|
|
'hotels' => [$hotelId => ['id' => $hotelId]],
|
|
],
|
|
];
|
|
|
|
$this->travelSnapshotService->method('exists')->willReturn(false);
|
|
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
|
|
$this->assertTrue($this->service->existsLocally($dateId, $hotelId));
|
|
}
|
|
|
|
public function testExistsLocallyFalseNoTravel(): void
|
|
{
|
|
$this->travelSnapshotService->method('exists')->willReturn(false);
|
|
$this->travelLoader->method('generateFilesMap')->willReturn([]);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
|
|
$this->assertFalse($this->service->existsLocally(12345, 67890));
|
|
}
|
|
|
|
public function testExistsLocallyFalseNoHotel(): void
|
|
{
|
|
$dateId = 12345;
|
|
$hotelId = 67890;
|
|
$mapping = [
|
|
$dateId => ['id' => $dateId, 'hotels' => []],
|
|
];
|
|
|
|
$this->travelSnapshotService->method('exists')->willReturn(false);
|
|
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
|
|
$this->assertFalse($this->service->existsLocally($dateId, $hotelId));
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// getAvailableSources
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testGetAvailableSourcesReturnsBothFlags(): void
|
|
{
|
|
$dateId = 12345;
|
|
$hotelId = 67890;
|
|
$mapping = [
|
|
$dateId => ['id' => $dateId, 'hotels' => [$hotelId => []]],
|
|
];
|
|
|
|
$this->travelSnapshotService->method('exists')->willReturn(false);
|
|
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
|
|
$this->travelSnapshotService->method('findProductIdByDateId')->willReturn(null);
|
|
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
|
|
$this->travelLoader->method('mapDateIdToProductId')->with($dateId)->willReturn(555);
|
|
|
|
$result = $this->service->getAvailableSources($dateId, $hotelId);
|
|
|
|
$this->assertEquals(['local' => true, 'remote' => true], $result);
|
|
}
|
|
}
|