createBookingDtoWithUnavailableSelection(); $bookingSessionService = $this->createMock(BookingSessionManager::class); $bookingSessionService->expects($this->once()) ->method('getOrCreateBookingCreateDto') ->with($request) ->willReturn($bookingDto); $bookingSessionService->expects($this->once()) ->method('getOrCreateBaselineSnapshot') ->with($request, $bookingDto) ->willReturn([[2, 1]]); $bookingSessionService->expects($this->once()) ->method('refreshCreateAvailability') ->with($request, $bookingDto) ->willReturn(new BookingAvailabilityRefreshResult( availabilityRefreshed: true, roomSelectionQuantityReset: true, roomSelectionSnapshotChanged: true, hasAvailableRooms: true, )); $bookingSessionService->expects($this->once()) ->method('saveBaselineSnapshot') ->with($request, $bookingDto); $bookingService = $this->createMock(BookingConfigurator::class); $form = $this->createMock(FormInterface::class); $form->expects($this->once()) ->method('handleRequest') ->with($request) ->willReturnSelf(); $form->expects($this->once()) ->method('isSubmitted') ->willReturn(false); $createContextFactory = $this->createMock(BookingCreateContextFactory::class); $createContextFactory->expects($this->once()) ->method('create') ->with($bookingDto, $this->anything()) ->willReturn(new BookingCreateContext( bookingDto: $bookingDto, summaryData: $this->createMock(BookingSummaryDto::class), groupedRooms: new RoomGroupsDto([], []), )); $controller = new TestableStep1Controller( $bookingService, $bookingSessionService, $createContextFactory, $form, ); $response = $controller->index($request); $this->assertInstanceOf(Response::class, $response); $this->assertSame(['warning', 'Mindestens eine Zimmerauswahl ist nicht mehr verfügbar und wurde zurückgesetzt.'], $controller->flashes[0]); } public function testIndexUsesPrunedBaselineBeforeSubmittingUnchangedSelections(): void { $request = Request::create('/bookings/create/rooms', 'POST'); $bookingDto = $this->createBookingDtoWithUnavailableSelection(); $bookingDto = $this->getMockBuilder(BookingDto::class) ->setConstructorArgs([$bookingDto->travel, $bookingDto->hotelId]) ->onlyMethods(['hasRoomSelectionChanged', 'resetParticipantAssignments']) ->getMock(); $bookingDto->roomSelections = $this->createBookingDtoWithUnavailableSelection()->roomSelections; $bookingDto->currentStep = 1; $bookingSessionService = $this->createMock(BookingSessionManager::class); $bookingSessionService->expects($this->once()) ->method('getOrCreateBookingCreateDto') ->with($request) ->willReturn($bookingDto); $bookingSessionService->expects($this->once()) ->method('getOrCreateBaselineSnapshot') ->with($request, $bookingDto) ->willReturn([[1, 0], [2, 1]]); $bookingSessionService->expects($this->once()) ->method('refreshCreateAvailability') ->with($request, $bookingDto) ->willReturn(new BookingAvailabilityRefreshResult( availabilityRefreshed: true, roomSelectionQuantityReset: true, roomSelectionSnapshotChanged: true, hasAvailableRooms: true, )); $bookingSessionService->expects($this->once()) ->method('saveBookingDto') ->with($request, $bookingDto, BookingDto::MODE_CREATE); $bookingSessionService->expects($this->once()) ->method('saveBaselineSnapshot') ->with($request, $bookingDto) ->willReturn([[1, 0]]); $bookingSessionService->expects($this->once()) ->method('clearBaselineSnapshot') ->with($request); $bookingService = $this->createMock(BookingConfigurator::class); $bookingService->expects($this->once()) ->method('updateBookingStatusFromRoomSelection') ->with($bookingDto); $bookingDto->expects($this->never()) ->method('resetParticipantAssignments'); $bookingDto->expects($this->once()) ->method('hasRoomSelectionChanged') ->with([[1, 0]]) ->willReturn(false); $form = $this->createMock(FormInterface::class); $form->expects($this->once()) ->method('handleRequest') ->with($request) ->willReturnSelf(); $form->expects($this->once()) ->method('isSubmitted') ->willReturn(true); $form->expects($this->once()) ->method('isValid') ->willReturn(true); $createContextFactory = $this->createMock(BookingCreateContextFactory::class); $createContextFactory->expects($this->never()) ->method('create'); $controller = new TestableStep1Controller( $bookingService, $bookingSessionService, $createContextFactory, $form, ); $response = $controller->index($request); $this->assertInstanceOf(Response::class, $response); } private function createBookingDtoWithUnavailableSelection(): BookingDto { $travel = new Travel(); $travel->id = 1234; $availableRoom = new Room(); $availableRoom->id = 1; $availableRoom->label = 'Doppelzimmer'; $availableRoom->available = 2; $availableRoom->status = 'Frei'; $travel->rooms = [$availableRoom]; $bookingDto = new BookingDto($travel, 77); $selection = new RoomSelectionDto(); $selection->id = 2; $selection->quantity = 1; $bookingDto->roomSelections = [$selection]; return $bookingDto; } } final class TestableStep1Controller extends Step1Controller { /** * @var array */ public array $flashes = []; /** * @param FormInterface $form */ public function __construct( BookingConfigurator $bookingService, BookingSessionManager $bookingSessionService, BookingCreateContextFactory $createContextFactory, private readonly FormInterface $form, ) { parent::__construct($bookingService, $bookingSessionService, $createContextFactory); } /** * @return FormInterface */ protected function createForm(string $type, mixed $data = null, array $options = []): FormInterface { return $this->form; } protected function addFlash(string $type, mixed $message): void { $this->flashes[] = [$type, $message]; } /** * @param array $parameters */ protected function render(string $view, array $parameters = [], ?Response $response = null): Response { return new Response('rendered:'.$view); } protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse { return new RedirectResponse('redirect:'.$route, $status); } protected function generateUrl(string $route, array $parameters = [], int $referenceType = 0): string { return $route; } }