createController(); $controller->apiClient ->expects(self::once()) ->method('getPersonalData') ->with('customer@example.com', 'secret') ->willReturn($this->createPersonalData('Mia', 'Muster')); $controller->newsletterManager ->expects(self::once()) ->method('hasConfirmedOptIn') ->with('customer@example.com') ->willReturn(false); $controller->newsletterManager ->expects(self::once()) ->method('hasPendingConfirmation') ->with('customer@example.com') ->willReturn(false); $response = $controller->index(Request::create('/personal-data', 'GET')); self::assertSame(200, $response->getStatusCode()); self::assertSame('account/personal_data.html.twig', $controller->renderedView); self::assertSame(false, $controller->renderedParameters['newsletterSubscribed']); self::assertSame(false, $controller->renderedParameters['newsletterPendingConfirmation']); } public function testIndexStillTracksPendingNewsletterConfirmationWhenSubscribed(): void { $controller = $this->createController(); $controller->apiClient ->expects(self::once()) ->method('getPersonalData') ->with('customer@example.com', 'secret') ->willReturn($this->createPersonalData('Mia', 'Muster')); $controller->newsletterManager ->expects(self::once()) ->method('hasConfirmedOptIn') ->with('customer@example.com') ->willReturn(true); $controller->newsletterManager ->expects(self::once()) ->method('hasPendingConfirmation') ->with('customer@example.com') ->willReturn(false); $response = $controller->index(Request::create('/personal-data', 'GET')); self::assertSame(200, $response->getStatusCode()); self::assertSame(true, $controller->renderedParameters['newsletterSubscribed']); self::assertSame(false, $controller->renderedParameters['newsletterPendingConfirmation']); } public function testNewsletterActionReturnsHtmxRedirectAndFlash(): void { $controller = $this->createController(); $controller->apiClient ->expects(self::once()) ->method('getPersonalData') ->with('customer@example.com', 'secret') ->willReturn($this->createPersonalData('Mia', 'Muster')); $controller->newsletterManager ->expects(self::once()) ->method('hasPendingConfirmation') ->with('customer@example.com') ->willReturn(false); $controller->newsletterManager ->expects(self::once()) ->method('requestDefaultListSubscription') ->with('customer@example.com', 'Mia', 'Muster') ->willReturn(new NewsletterSubscriptionRequestResult( 'customer@example.com', [10321569], NewsletterSubscriptionRequestResult::STATE_CONFIRMATION_REQUESTED, true, [ 10321569 => NewsletterSubscriptionRequestResult::LIST_STATE_PENDING, ], )); $response = $controller->newsletter(Request::create('/personal-data/newsletter', 'POST', [], [], [], [ 'HTTP_HX_REQUEST' => 'true', ])); self::assertInstanceOf(Response::class, $response); self::assertSame(200, $response->getStatusCode()); self::assertSame('/personal-data', $response->headers->get('HX-Redirect')); self::assertSame([ ['success', 'Bitte bestätige deine Newsletter-Anmeldung über den Link in der E-Mail.'], ], $controller->flashes); } public function testNewsletterActionResendsPendingConfirmation(): void { $controller = $this->createController(); $controller->apiClient ->expects(self::once()) ->method('getPersonalData') ->with('customer@example.com', 'secret') ->willReturn($this->createPersonalData('Mia', 'Muster')); $controller->newsletterManager ->expects(self::once()) ->method('hasPendingConfirmation') ->with('customer@example.com') ->willReturn(true); $controller->newsletterManager ->expects(self::once()) ->method('requestConfirmation') ->with('customer@example.com', 'Mia', 'Muster'); $controller->newsletterManager->expects(self::never())->method('requestDefaultListSubscription'); $response = $controller->newsletter(Request::create('/personal-data/newsletter', 'POST', [], [], [], [ 'HTTP_HX_REQUEST' => 'true', ])); self::assertInstanceOf(Response::class, $response); self::assertSame(200, $response->getStatusCode()); self::assertSame([ ['success', 'Wir haben dir eine neue Bestätigungs-E-Mail gesendet.'], ], $controller->flashes); } private function createController(): TestablePersonalDataController { $user = new User('customer@example.com'); $user->setPassword('secret'); $apiClient = $this->createMock(ApiClient::class); $crypt = $this->createMock(Crypt::class); $crypt ->method('decrypt') ->with('secret') ->willReturn('secret'); $form = $this->createMock(FormInterface::class); $form ->method('handleRequest') ->willReturnSelf(); $form ->method('isSubmitted') ->willReturn(false); return new TestablePersonalDataController( $apiClient, $crypt, $this->createMock(BookingEditDataLoader::class), $this->createMock(ProfileCompletenessChecker::class), $this->createMock(EntityManagerInterface::class), $this->createMock(NewsletterManager::class), $this->createMock(LoggerInterface::class), $user, $form, ); } private function createPersonalData(string $firstName, string $lastName): PersonalData { $personalData = new PersonalData(); $personalData->firstName = $firstName; $personalData->name = $lastName; return $personalData; } } final class TestablePersonalDataController extends PersonalDataController { /** * @var array */ public array $flashes = []; /** * @var array */ public array $renderedParameters = []; public string $renderedView = ''; public function __construct( public readonly ApiClient $apiClient, Crypt $crypt, BookingEditDataLoader $dataLoader, ProfileCompletenessChecker $completenessChecker, EntityManagerInterface $entityManager, public readonly NewsletterManager $newsletterManager, LoggerInterface $logger, private readonly User $user, private readonly FormInterface $form, ) { parent::__construct( $apiClient, $crypt, $dataLoader, $completenessChecker, $entityManager, $newsletterManager, $logger, ); } public function createForm(string $type, mixed $data = null, array $options = []): FormInterface { return $this->form; } protected function getUser(): UserInterface { return $this->user; } protected function addFlash(string $type, mixed $message): void { $this->flashes[] = [$type, (string) $message]; } protected function render(string $view, array $parameters = [], Response $response = null): Response { $this->renderedView = $view; $this->renderedParameters = $parameters; return new Response('ok'); } protected function generateUrl(string $route, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { return '/personal-data'; } }