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); } }