createMock(AccommodationBookingService::class); $bookingService->expects(self::never())->method('removePeriod'); $controller = new TestableRemovePeriodController($bookingService, $this->createStub(LoggerInterface::class)); $controller->index($this->period(), $this->request()); self::assertSame('admin/accommodation_booking/modal_remove_period.html.twig', $controller->renderedView); } public function testConfirmingRemovesThePeriodAndReturnsToTheEditForm(): void { $period = $this->period(); $bookingService = $this->createMock(AccommodationBookingService::class); $bookingService->expects(self::once())->method('removePeriod') ->with($period->getBooking(), $period) ->willReturn(new ConstraintViolationList()); $controller = new TestableRemovePeriodController($bookingService, $this->createStub(LoggerInterface::class)); $response = $controller->index($period, $this->request('POST')); self::assertStringStartsWith('/app_admin_accommodationbooking_edit', (string) $response->headers->get('HX-Redirect')); self::assertSame('success', $controller->flashes[0]['type']); } public function testAnInvalidTokenRemovesNothing(): void { $bookingService = $this->createMock(AccommodationBookingService::class); $bookingService->expects(self::never())->method('removePeriod'); $controller = new TestableRemovePeriodController($bookingService, $this->createStub(LoggerInterface::class)); $controller->tokenValid = false; $this->expectException(AccessDeniedException::class); $controller->index($this->period(), $this->request('POST')); } public function testAPeriodWithoutABookingIsNotFound(): void { $controller = new TestableRemovePeriodController($this->createStub(AccommodationBookingService::class), $this->createStub(LoggerInterface::class)); $this->expectException(NotFoundHttpException::class); $controller->index( new AccommodationBookingPeriod(AccommodationBookingPeriodPosition::Leading, new \DateTimeImmutable('2026-07-01'), new \DateTimeImmutable('2026-07-02')), $this->request(), ); } private function period(): AccommodationBookingPeriod { $booking = (new AccommodationBooking()) ->setDateFrom(new \DateTimeImmutable('2026-07-01')) ->setDateTo(new \DateTimeImmutable('2026-07-08')); return $booking->splitOffPeriod(AccommodationBookingPeriodPosition::Leading, 1); } private function request(string $method = 'GET'): Request { return Request::create('/admin/accommodation-booking-period/1/remove', $method, server: ['HTTP_HX-Request' => 'true']); } } final class TestableRemovePeriodController extends RemovePeriodController { public ?string $renderedView = null; public bool $tokenValid = true; /** @var list */ public array $flashes = []; protected function isCsrfTokenValid(string $id, #[\SensitiveParameter] ?string $token): bool { return $this->tokenValid; } /** * @param array $parameters */ protected function render(string $view, array $parameters = [], ?Response $response = null): Response { $this->renderedView = $view; return new Response(); } protected function addFlash(string $type, mixed $message): void { $this->flashes[] = ['type' => $type, 'message' => $message]; } /** * @param array $parameters */ protected function generateUrl(string $route, array $parameters = [], int $referenceType = 1): string { return '/'.$route.'?'.http_build_query($parameters); } }