setStatus(AccommodationBookingStatus::Open); $bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner->method('isValidLinkRequest')->willReturn(true); $linkSigner->expects(self::once())->method('authorizeSession')->with(self::anything(), $booking); $controller = new TestableOfferIndexController($bookingRepository, $linkSigner); $response = $controller->index($booking->getUuid(), Request::create('/groups/booking/offer/'.$booking->getUuid())); self::assertInstanceOf(RedirectResponse::class, $response); self::assertSame('/app_groups_offer_view?uuid='.$booking->getUuid(), $response->getTargetUrl()); } public function testRendersUnavailableForInvalidLink(): void { $booking = new AccommodationBooking(); $bookingRepository = $this->createStub(AccommodationBookingRepository::class); $bookingRepository->method('findOneBy')->willReturn($booking); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner->method('isValidLinkRequest')->willReturn(false); $linkSigner->expects(self::never())->method('authorizeSession'); $controller = new TestableOfferIndexController($bookingRepository, $linkSigner); $response = $controller->index($booking->getUuid(), Request::create('/groups/booking/offer/'.$booking->getUuid())); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame('groups/offer/unavailable.html.twig', $controller->renderedView); } public function testRendersUnavailableForUnknownUuid(): void { $bookingRepository = $this->createStub(AccommodationBookingRepository::class); $bookingRepository->method('findOneBy')->willReturn(null); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner->expects(self::never())->method('isValidLinkRequest'); $controller = new TestableOfferIndexController($bookingRepository, $linkSigner); $response = $controller->index('unknown-uuid', Request::create('/groups/booking/offer/unknown-uuid')); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame('groups/offer/unavailable.html.twig', $controller->renderedView); } #[DataProvider('statusesTheCustomerMustNotSee')] public function testRendersUnavailableForABookingThatIsNotOfferedYet(AccommodationBookingStatus $status): void { $booking = new AccommodationBooking(); $booking->setStatus($status); $bookingRepository = $this->createStub(AccommodationBookingRepository::class); $bookingRepository->method('findOneBy')->willReturn($booking); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner->method('isValidLinkRequest')->willReturn(true); $linkSigner->expects(self::never())->method('authorizeSession'); $controller = new TestableOfferIndexController($bookingRepository, $linkSigner); $response = $controller->index($booking->getUuid(), Request::create('/groups/booking/offer/'.$booking->getUuid())); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame('groups/offer/unavailable.html.twig', $controller->renderedView); } /** * @return iterable */ public static function statusesTheCustomerMustNotSee(): iterable { // A scratch record and an inquiry the office has not worked through yet are both // unfinished; a discarded one is over. All three behave like an expired link. yield 'draft' => [AccommodationBookingStatus::Draft]; yield 'requested' => [AccommodationBookingStatus::Requested]; yield 'discarded' => [AccommodationBookingStatus::Discarded]; } } final class TestableOfferIndexController extends IndexController { public ?string $renderedView = null; /** * @param array $parameters */ protected function generateUrl(string $route, array $parameters = [], int $referenceType = 1): string { return '/'.$route.'?'.http_build_query($parameters); } protected function render(string $view, array $parameters = [], ?Response $response = null): Response { $this->renderedView = $view; return $response ?? new Response('unavailable'); } }