createMock(AccommodationBookingService::class); $bookingService->expects(self::once())->method('refreshPriceSnapshot'); $bookingService->expects(self::never())->method('issueAccessLink'); $bookingService->expects(self::never())->method('sendCustomerConfirmationEmail'); $bookingService->expects(self::never())->method('sendOffer'); $booking = new AccommodationBooking(); $booking->setStatus($status); $this->submitEdit($booking, $bookingService); self::assertSame($status, $booking->getStatus(), 'editing leaves the status where it was'); } /** * @return iterable */ public static function everyStatus(): iterable { foreach (AccommodationBookingStatus::cases() as $status) { yield $status->value => [$status]; } } private function submitEdit(AccommodationBooking $booking, AccommodationBookingService $bookingService): void { $form = $this->createStub(FormInterface::class); $form->method('handleRequest')->willReturn($form); $form->method('isSubmitted')->willReturn(true); $form->method('isValid')->willReturn(true); $form->method('has')->willReturn(false); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->expects(self::once())->method('flush'); $controller = new TestableEditController( $entityManager, $this->createStub(LoggerInterface::class), $this->createStub(BoardServiceRepository::class), $this->createStub(AdditionalServiceRepository::class), $this->createStub(UserRepository::class), $bookingService, $form, ); $response = $controller->index($booking, Request::create('/admin/accommodation-booking/1/edit', 'POST')); self::assertSame(Response::HTTP_FOUND, $response->getStatusCode()); } public function testManagerWithoutAdminRoleGetsNoBetreuerChoices(): void { $userRepo = $this->createMock(UserRepository::class); $userRepo->expects(self::never())->method('findGroupsStaff'); $controller = $this->buildController($userRepo, granted: false); $controller->index(new AccommodationBooking(), Request::create('/admin/accommodation-booking/1/edit')); self::assertSame([], $controller->capturedOptions['assignable_managers']); } public function testAdminGetsTheGroupsStaffAsBetreuerChoices(): void { $staff = [new User('manager@example.test')]; $userRepo = $this->createStub(UserRepository::class); $userRepo->method('findGroupsStaff')->willReturn($staff); $controller = $this->buildController($userRepo, granted: true); $controller->index(new AccommodationBooking(), Request::create('/admin/accommodation-booking/1/edit')); self::assertSame($staff, $controller->capturedOptions['assignable_managers']); } public function testAnAssignedUserWhoLostTheRoleStaysSelectable(): void { $formerManager = new User('former@example.test'); $staff = [new User('manager@example.test')]; $userRepo = $this->createStub(UserRepository::class); $userRepo->method('findGroupsStaff')->willReturn($staff); $booking = new AccommodationBooking(); $booking->setManagedBy($formerManager); $controller = $this->buildController($userRepo, granted: true); $controller->index($booking, Request::create('/admin/accommodation-booking/1/edit')); self::assertSame([$staff[0], $formerManager], $controller->capturedOptions['assignable_managers']); } private function buildController(UserRepository $userRepo, bool $granted): TestableEditController { // An unsubmitted form: index() falls through to render() and only the options // handed to createForm() are of interest here. $form = $this->createStub(FormInterface::class); $form->method('handleRequest')->willReturn($form); $form->method('isSubmitted')->willReturn(false); return new TestableEditController( $this->createStub(EntityManagerInterface::class), $this->createStub(LoggerInterface::class), $this->createStub(BoardServiceRepository::class), $this->createStub(AdditionalServiceRepository::class), $userRepo, $this->createStub(AccommodationBookingService::class), $form, $granted, ); } } final class TestableEditController extends EditController { /** @var array */ public array $capturedOptions = []; public function __construct( EntityManagerInterface $entityManager, LoggerInterface $logger, BoardServiceRepository $boardServiceRepo, AdditionalServiceRepository $additionalServiceRepo, UserRepository $userRepo, AccommodationBookingService $bookingService, private readonly FormInterface $form, private readonly bool $granted = false, ) { parent::__construct($entityManager, $logger, $boardServiceRepo, $additionalServiceRepo, $userRepo, $bookingService); } protected function createForm(string $type, mixed $data = null, array $options = []): FormInterface { $this->capturedOptions = $options; return $this->form; } protected function isGranted(mixed $attribute, mixed $subject = null): bool { return $this->granted; } /** * @param array $parameters */ protected function render(string $view, array $parameters = [], ?Response $response = null): Response { return new Response(); } protected function addFlash(string $type, mixed $message): void { } /** * @param array $parameters */ protected function generateUrl(string $route, array $parameters = [], int $referenceType = 1): string { return '/'.$route.'?'.http_build_query($parameters); } }