dayRepository = $this->createStub(ContingentDayRepository::class); $this->syncStateRepository = $this->createStub(ContingentSyncStateRepository::class); } public function testReturnsNullWhenNothingHasEverSynced(): void { $this->dayRepository = $this->createMock(ContingentDayRepository::class); $this->syncStateRepository->method('findOneByHotelCode')->willReturn(null); $this->dayRepository->expects(self::never())->method('findByHotelCodeAndDateRange'); self::assertNull($this->read()); } public function testReturnsNullWhenTheSnapshotIsStale(): void { $this->dayRepository = $this->createMock(ContingentDayRepository::class); $this->syncStateRepository->method('findOneByHotelCode')->willReturn($this->state('-7 hours')); $this->dayRepository->expects(self::never())->method('findByHotelCodeAndDateRange'); self::assertNull($this->read()); } public function testReturnsStatusesWhenRecentlySynced(): void { $this->syncStateRepository->method('findOneByHotelCode')->willReturn($this->state('-10 minutes')); $this->dayRepository->method('findByHotelCodeAndDateRange')->willReturn([ '2026-09-01' => $this->day(ContingentStatus::Ok), '2026-09-02' => $this->day(ContingentStatus::OnRequest), ]); self::assertSame([ '2026-09-01' => ContingentStatus::Ok, '2026-09-02' => ContingentStatus::OnRequest, ], $this->read()); } public function testStillUsableJustInsideTheStalenessWindow(): void { $this->syncStateRepository->method('findOneByHotelCode')->willReturn($this->state('-5 hours')); $this->dayRepository->method('findByHotelCodeAndDateRange')->willReturn([]); self::assertSame([], $this->read()); } public function testReturnsNullForAnEmptyHotelCode(): void { $this->syncStateRepository = $this->createMock(ContingentSyncStateRepository::class); $this->syncStateRepository->expects(self::never())->method('findOneByHotelCode'); self::assertNull($this->read('')); } public function testIsCurrentMatchesWhatStatusesForServes(): void { $reader = new ContingentSnapshotReader( $this->dayRepository, $this->syncStateRepository, $this->createStub(LoggerInterface::class), ); self::assertFalse($reader->isCurrent(null)); self::assertFalse($reader->isCurrent(new ContingentSyncState('HOTEL1'))); self::assertFalse($reader->isCurrent($this->state('-7 hours'))); self::assertTrue($reader->isCurrent($this->state('-5 hours'))); } /** * @return array|null */ private function read(string $hotelCode = 'HOTEL1'): ?array { $reader = new ContingentSnapshotReader( $this->dayRepository, $this->syncStateRepository, $this->createStub(LoggerInterface::class), ); return $reader->statusesFor( $hotelCode, new \DateTimeImmutable('2026-09-01'), new \DateTimeImmutable('2026-09-03'), ); } private function state(string $syncedAgo): ContingentSyncState { return (new ContingentSyncState())->setSyncedAt(CarbonImmutable::now()->modify($syncedAgo)->toDateTimeImmutable()); } private function day(ContingentStatus $status): ContingentDay { return (new ContingentDay())->setStatus($status); } }