travelDataService = $this->createMock(TravelDataService::class); $priceCalculator = $this->createMock(BookingPriceCalculatorService::class); $participantEligibility = $this->createMock(ParticipantEligibilityService::class); $this->bookingService = new BookingService( $this->travelDataService, $priceCalculator, $participantEligibility ); } public function testStartFreshBookingWithFreiRooms(): void { $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_AVAILABLE, 'available' => 5], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $bookingDto = $this->bookingService->startFreshBooking($request, 123, 456); $this->assertSame('F', $bookingDto->bookingStatus); $this->assertCount(1, $bookingDto->roomSelections); } public function testStartFreshBookingWithFreiRoomsAndNoAvailabilityThrowsException(): void { $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_AVAILABLE, 'available' => 0], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $this->expectException(NoRoomsAvailableException::class); $this->bookingService->startFreshBooking($request, 123, 456); } public function testStartFreshBookingWithAnfrageRooms(): void { $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_ON_REQUEST, 'available' => 5], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $bookingDto = $this->bookingService->startFreshBooking($request, 123, 456); $this->assertSame('A', $bookingDto->bookingStatus); $this->assertCount(1, $bookingDto->roomSelections); } public function testStartFreshBookingWithAnfrageRoomsAndNoAvailabilityThrowsException(): void { $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_ON_REQUEST, 'available' => 0], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $this->expectException(NoRoomsAvailableException::class); $this->bookingService->startFreshBooking($request, 123, 456); } public function testStartFreshBookingWithMixedRoomsUsesFreeStatus(): void { // When both Frei and Anfrage rooms with availability exist, booking status should be 'F' $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_AVAILABLE, 'available' => 5], ['status' => Constants::STATUS_ON_REQUEST, 'available' => 3], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $bookingDto = $this->bookingService->startFreshBooking($request, 123, 456); $this->assertSame('F', $bookingDto->bookingStatus); $this->assertCount(2, $bookingDto->roomSelections); } public function testStartFreshBookingWithNoBookableRoomsThrowsException(): void { // Rooms exist but all have 0 availability $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_AVAILABLE, 'available' => 0], ['status' => Constants::STATUS_ON_REQUEST, 'available' => 0], ]); $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $this->expectException(NoRoomsAvailableException::class); $this->bookingService->startFreshBooking($request, 123, 456); } public function testStartFreshBookingWithApiRestrictionsForcesInquiry(): void { // Even with Frei rooms, if API says only 'A' allowed, booking status should be 'A' $travel = $this->createTravelWithRooms([ ['status' => Constants::STATUS_AVAILABLE, 'available' => 5], ]); $travel->allowedBookingStatus = ['A']; // API restricts to inquiry only $this->travelDataService ->method('getTravelData') ->willReturn($travel); $request = $this->createRequestWithSession(); $bookingDto = $this->bookingService->startFreshBooking($request, 123, 456); $this->assertSame('A', $bookingDto->bookingStatus); } /** * @param array $roomsConfig */ private function createTravelWithRooms(array $roomsConfig): Travel { $travel = new Travel(); $travel->id = 123; $travel->hotelId = 456; $travel->dateFrom = new \DateTimeImmutable('2030-01-01'); $travel->dateTo = new \DateTimeImmutable('2030-01-06'); $travel->allowedBookingStatus = []; // Empty means no API restrictions $rooms = []; foreach ($roomsConfig as $index => $config) { $room = new Room(); $room->id = $index + 1; $room->label = 'Test Room '.($index + 1); $room->price = 100.0; $room->available = $config['available']; $room->status = $config['status']; $room->minPax = 2; $room->category = 'A'; $room->boardId = 1; $rooms[$room->id] = $room; } $travel->rooms = $rooms; return $travel; } private function createRequestWithSession(): Request { $session = new Session(new MockArraySessionStorage()); $request = new Request(); $request->setSession($session); return $request; } }