feat: api endpoints and xml sync for contingents data

This commit is contained in:
Björn Fromme
2026-04-22 11:23:52 +02:00
parent a1f68ec11d
commit 9b84893d88
50 changed files with 2567 additions and 221 deletions
+74 -29
View File
@@ -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([]);