Files
myep/tests/Controller/Account/PersonalDataControllerTest.php
T

315 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Controller\Account;
use App\BusProNet\ApiClient;
use App\BusProNet\Model\Notification;
use App\BusProNet\Model\PersonalData;
use App\Controller\Account\PersonalDataController;
use App\Entity\User;
use App\Model\NewsletterSubscriptionRequestResult;
use App\Security\Crypt;
use App\Service\BookingEditDataLoader;
use App\Service\NewsletterManager;
use App\Service\ProfileCompletenessChecker;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class PersonalDataControllerTest extends TestCase
{
public function testIndexRendersNewsletterSubscribeCtaWhenNotSubscribed(): void
{
$controller = $this->createController();
$controller->apiClient
->expects(self::once())
->method('getPersonalData')
->with('[email protected]', 'secret')
->willReturn($this->createPersonalData('Mia', 'Muster'));
$controller->newsletterManager
->expects(self::once())
->method('hasConfirmedOptIn')
->with('[email protected]')
->willReturn(false);
$controller->newsletterManager
->expects(self::once())
->method('hasPendingConfirmation')
->with('[email protected]')
->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('[email protected]', 'secret')
->willReturn($this->createPersonalData('Mia', 'Muster'));
$controller->newsletterManager
->expects(self::once())
->method('hasConfirmedOptIn')
->with('[email protected]')
->willReturn(true);
$controller->newsletterManager
->expects(self::once())
->method('hasPendingConfirmation')
->with('[email protected]')
->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('[email protected]', 'secret')
->willReturn($this->createPersonalData('Mia', 'Muster'));
$controller->newsletterManager
->expects(self::once())
->method('hasPendingConfirmation')
->with('[email protected]')
->willReturn(false);
$controller->newsletterManager
->expects(self::once())
->method('requestDefaultListSubscription')
->with('[email protected]', 'Mia', 'Muster')
->willReturn(new NewsletterSubscriptionRequestResult(
'[email protected]',
[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('[email protected]', 'secret')
->willReturn($this->createPersonalData('Mia', 'Muster'));
$controller->newsletterManager
->expects(self::once())
->method('hasPendingConfirmation')
->with('[email protected]')
->willReturn(true);
$controller->newsletterManager
->expects(self::once())
->method('requestConfirmation')
->with('[email protected]', '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);
}
/**
* The e-mail lives on the BPN address and doubles as the login identity of every person on it,
* so this form must never accept a submitted value for it.
*/
public function testIndexLocksTheEmailField(): void
{
$controller = $this->createController();
$controller->apiClient
->method('getPersonalData')
->willReturn($this->createPersonalData('Mia', 'Muster'));
$controller->index(Request::create('/personal-data', 'GET'));
self::assertFalse($controller->createFormOptions['email_editable']);
}
/**
* A failed load yields an empty PersonalData. Handing that to BPN would blank every field we
* never read - including the e-mail, which would lock the customer out of the portal.
*/
public function testIndexDoesNotSubmitTheFormWhenPersonalDataCouldNotBeLoaded(): void
{
$controller = $this->createController();
$controller->apiClient
->expects(self::once())
->method('getPersonalData')
->willReturn(new Notification(500, 'no such customer'));
$controller->apiClient
->expects(self::never())
->method('updatePersonalData');
$controller->form
->expects(self::never())
->method('handleRequest');
$response = $controller->index(Request::create('/personal-data', 'POST', [
'personal_data' => ['email' => '[email protected]'],
]));
self::assertSame(200, $response->getStatusCode());
self::assertSame('account/personal_data.html.twig', $controller->renderedView);
}
private function createController(): TestablePersonalDataController
{
$user = new User('[email protected]');
$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->addressId = 4711;
$personalData->personId = 815;
$personalData->firstName = $firstName;
$personalData->name = $lastName;
$personalData->communication->email = '[email protected]';
return $personalData;
}
}
final class TestablePersonalDataController extends PersonalDataController
{
/**
* @var array<int, array{0: string, 1: string}>
*/
public array $flashes = [];
/**
* @var array<string, mixed>
*/
public array $renderedParameters = [];
public string $renderedView = '';
/**
* @var array<string, mixed>
*/
public array $createFormOptions = [];
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,
public readonly FormInterface $form,
) {
parent::__construct(
$apiClient,
$crypt,
$dataLoader,
$completenessChecker,
$entityManager,
$newsletterManager,
$logger,
);
}
public function createForm(string $type, mixed $data = null, array $options = []): FormInterface
{
$this->createFormOptions = $options;
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';
}
}