624 lines
27 KiB
PHP
624 lines
27 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Service;
|
|
|
|
use App\BpnConnect\BpnConnectClient;
|
|
use App\BpnConnect\Model\BookingLookupResult;
|
|
use App\Email\Mailer;
|
|
use App\Entity\User;
|
|
use App\Exception\GuestAccountActivationExpiredException;
|
|
use App\Repository\UserRepository;
|
|
use App\Service\BookingGuestAccountManager;
|
|
use App\Service\UserBootstrapper;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\NullLogger;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\UriSigner;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
use Symfony\Component\Mailer\Exception\TransportException;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
|
|
class BookingGuestAccountManagerTest extends TestCase
|
|
{
|
|
public function testProvisionGuestAccountCreatesCustomerAndSendsActivationMail(): void
|
|
{
|
|
$persistedUser = null;
|
|
$plainPassword = null;
|
|
$generatedRoute = null;
|
|
$generatedParameters = null;
|
|
$generatedReferenceType = null;
|
|
$capturedContext = null;
|
|
$capturedOptions = null;
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn(null);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager
|
|
->expects(self::once())
|
|
->method('persist')
|
|
->with(self::callback(static function (User $user) use (&$persistedUser): bool {
|
|
$persistedUser = $user;
|
|
|
|
return true;
|
|
}));
|
|
$entityManager
|
|
->expects(self::once())
|
|
->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher
|
|
->expects(self::once())
|
|
->method('hashPassword')
|
|
->with(self::isInstanceOf(User::class), self::callback(static function (string $password) use (&$plainPassword): bool {
|
|
$plainPassword = $password;
|
|
|
|
return '' !== $password;
|
|
}))
|
|
->willReturn('hashed-password');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturnCallback(static function (string $route, array $parameters, int $referenceType) use (&$generatedRoute, &$generatedParameters, &$generatedReferenceType): string {
|
|
$generatedRoute = $route;
|
|
$generatedParameters = $parameters;
|
|
$generatedReferenceType = $referenceType;
|
|
|
|
return 'https://example.test/welcome/activate?'.http_build_query($parameters);
|
|
});
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail')
|
|
->willReturnCallback(static function (array $context, array $options) use (&$capturedContext, &$capturedOptions): void {
|
|
$capturedContext = $context;
|
|
$capturedOptions = $options;
|
|
});
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer);
|
|
$uriSigner = new UriSigner('test-secret');
|
|
|
|
self::assertTrue($manager->provisionGuestAccount(' [email protected] ', 'Alice', 'Example', 12345, 67890));
|
|
self::assertSame('app_guest_account_activate', $generatedRoute);
|
|
self::assertSame(UrlGeneratorInterface::ABSOLUTE_URL, $generatedReferenceType);
|
|
self::assertSame('[email protected]', $generatedParameters['email']);
|
|
self::assertSame($persistedUser?->getWelcomeActivationNonce(), $generatedParameters['nonce']);
|
|
self::assertTrue($uriSigner->check((string) $capturedContext['activationUrl']));
|
|
self::assertSame('[email protected]', $capturedOptions['to']);
|
|
self::assertSame('Dein MyE&P Konto ist bereit', $capturedOptions['subject']);
|
|
self::assertSame('email/booking_guest_account_welcome.html.twig', $capturedOptions['template']);
|
|
self::assertSame('Alice', $capturedContext['firstName']);
|
|
self::assertSame('Example', $capturedContext['lastName']);
|
|
self::assertMatchesRegularExpression('/^[a-f0-9]{48}$/', (string) $plainPassword);
|
|
self::assertInstanceOf(User::class, $persistedUser);
|
|
self::assertSame('[email protected]', $persistedUser->getEmail());
|
|
self::assertSame(12345, $persistedUser->getAddressId());
|
|
self::assertSame(67890, $persistedUser->getPersonId());
|
|
self::assertSame(['ROLE_USER', 'ROLE_CUSTOMER'], $persistedUser->getRoles());
|
|
self::assertSame([], $persistedUser->getHotelCodes());
|
|
self::assertTrue($persistedUser->isProfileComplete());
|
|
self::assertMatchesRegularExpression('/^[a-f0-9]{64}$/', (string) $persistedUser->getWelcomeActivationNonce());
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $persistedUser->getWelcomeActivationExpiresAt());
|
|
self::assertNull($persistedUser->getWelcomeActivatedAt());
|
|
}
|
|
|
|
public function testProvisionGuestAccountReturnsFalseWhenLocalUserAlreadyExists(): void
|
|
{
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn(new User('[email protected]'));
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager->expects(self::never())->method('persist');
|
|
$entityManager->expects(self::never())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher->expects(self::never())->method('hashPassword');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator->expects(self::never())->method('generate');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer->expects(self::never())->method('createAndSendEmail');
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer);
|
|
|
|
self::assertFalse($manager->provisionGuestAccount('[email protected]', 'Alice', 'Example'));
|
|
}
|
|
|
|
public function testRequestPortalAccessProvisionsGuestAccountWhenBookingMatches(): void
|
|
{
|
|
$persistedUser = null;
|
|
$capturedContext = null;
|
|
$capturedOptions = null;
|
|
$booking = $this->createLookupResult('[email protected]', 123, 'Alice', 'Example');
|
|
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
$bpnConnectClient
|
|
->expects(self::once())
|
|
->method('lookupBooking')
|
|
->with('[email protected]', 123)
|
|
->willReturn($booking);
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::exactly(2))
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturnOnConsecutiveCalls(null, null);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager
|
|
->expects(self::once())
|
|
->method('persist')
|
|
->with(self::callback(static function (User $user) use (&$persistedUser): bool {
|
|
$persistedUser = $user;
|
|
|
|
return true;
|
|
}));
|
|
$entityManager->expects(self::once())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher
|
|
->expects(self::once())
|
|
->method('hashPassword')
|
|
->willReturn('hashed-password');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturn('https://example.test/welcome/[email protected]&nonce=current&expires=9999999999');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail')
|
|
->willReturnCallback(static function (array $context, array $options) use (&$capturedContext, &$capturedOptions): void {
|
|
$capturedContext = $context;
|
|
$capturedOptions = $options;
|
|
});
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
|
|
self::assertSame(BookingGuestAccountManager::PORTAL_ACCESS_SENT, $manager->requestPortalAccess('[email protected]', 123));
|
|
self::assertInstanceOf(User::class, $persistedUser);
|
|
self::assertSame('[email protected]', $persistedUser->getEmail());
|
|
self::assertSame(12345, $persistedUser->getAddressId());
|
|
self::assertSame(67890, $persistedUser->getPersonId());
|
|
self::assertSame('[email protected]', $capturedOptions['to']);
|
|
self::assertSame('Alice', $capturedContext['firstName']);
|
|
self::assertSame('Example', $capturedContext['lastName']);
|
|
self::assertMatchesRegularExpression('/^[a-f0-9]{64}$/', (string) $persistedUser->getWelcomeActivationNonce());
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $persistedUser->getWelcomeActivationExpiresAt());
|
|
self::assertNull($persistedUser->getWelcomeActivatedAt());
|
|
}
|
|
|
|
public function testRequestPortalAccessResendsPendingInviteWhenBookingMatches(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setAddressId(12345);
|
|
$user->setPersonId(67890);
|
|
$user->setWelcomeActivationNonce('current-nonce');
|
|
$user->setWelcomeActivationExpiresAt(new \DateTimeImmutable('+30 minutes'));
|
|
$booking = $this->createLookupResult('[email protected]', 123, 'Alice', 'Example');
|
|
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
$bpnConnectClient
|
|
->expects(self::once())
|
|
->method('lookupBooking')
|
|
->with('[email protected]', 123)
|
|
->willReturn($booking);
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn($user);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager->expects(self::once())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher->expects(self::never())->method('hashPassword');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturn('https://example.test/welcome/[email protected]&nonce=new-nonce&expires=9999999999');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail');
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
|
|
self::assertSame(BookingGuestAccountManager::PORTAL_ACCESS_SENT, $manager->requestPortalAccess('[email protected]', 123));
|
|
self::assertNotSame('current-nonce', $user->getWelcomeActivationNonce());
|
|
self::assertNull($user->getWelcomeActivatedAt());
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $user->getWelcomeActivationExpiresAt());
|
|
}
|
|
|
|
public function testRequestPortalAccessUsesExistingUserIdsAndDoesNotMailActivatedAccount(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setAddressId(12345);
|
|
$user->setPersonId(67890);
|
|
$user->markWelcomeActivated();
|
|
$booking = $this->createLookupResult('[email protected]', 123, 'Alice', 'Example');
|
|
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
$bpnConnectClient
|
|
->expects(self::once())
|
|
->method('lookupBooking')
|
|
->with('[email protected]', 123)
|
|
->willReturn($booking);
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn($user);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager->expects(self::never())->method('persist');
|
|
$entityManager->expects(self::never())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher->expects(self::never())->method('hashPassword');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator->expects(self::never())->method('generate');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer->expects(self::never())->method('createAndSendEmail');
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
|
|
self::assertSame(BookingGuestAccountManager::PORTAL_ACCESS_ALREADY_ACTIVE, $manager->requestPortalAccess('[email protected]', 123));
|
|
}
|
|
|
|
public function testProvisionGuestAccountForBookingFetchesBookingIdentifiers(): void
|
|
{
|
|
$persistedUser = null;
|
|
$booking = $this->createLookupResult('[email protected]', 123, 'Alice', 'Example');
|
|
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
$bpnConnectClient
|
|
->expects(self::once())
|
|
->method('lookupBooking')
|
|
->with('[email protected]', 123)
|
|
->willReturn($booking);
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn(null);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager
|
|
->expects(self::once())
|
|
->method('persist')
|
|
->with(self::callback(static function (User $user) use (&$persistedUser): bool {
|
|
$persistedUser = $user;
|
|
|
|
return true;
|
|
}));
|
|
$entityManager->expects(self::once())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher
|
|
->expects(self::once())
|
|
->method('hashPassword')
|
|
->willReturn('hashed-password');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturn('https://example.test/welcome/[email protected]&nonce=current&expires=9999999999');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail');
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
|
|
self::assertTrue($manager->provisionGuestAccountForBooking('[email protected]', 123));
|
|
self::assertInstanceOf(User::class, $persistedUser);
|
|
self::assertSame('[email protected]', $persistedUser->getEmail());
|
|
self::assertSame(12345, $persistedUser->getAddressId());
|
|
self::assertSame(67890, $persistedUser->getPersonId());
|
|
}
|
|
|
|
public function testRequestPortalAccessRejectsMismatchedBookingGenerically(): void
|
|
{
|
|
$booking = $this->createLookupResult('[email protected]', 123, 'Alice', 'Example');
|
|
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
$bpnConnectClient
|
|
->expects(self::once())
|
|
->method('lookupBooking')
|
|
->with('[email protected]', 123)
|
|
->willReturn($booking);
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn(null);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager->expects(self::never())->method('persist');
|
|
$entityManager->expects(self::never())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher->expects(self::never())->method('hashPassword');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator->expects(self::never())->method('generate');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer->expects(self::never())->method('createAndSendEmail');
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
|
|
self::assertSame(BookingGuestAccountManager::PORTAL_ACCESS_NOT_SENT, $manager->requestPortalAccess('[email protected]', 123));
|
|
}
|
|
|
|
public function testConsumeActivationMarksInviteAsActivated(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setWelcomeActivationNonce('current-nonce');
|
|
$user->setWelcomeActivationExpiresAt(new \DateTimeImmutable('+30 minutes'));
|
|
|
|
$manager = $this->createValidationManager($user);
|
|
$manager->consumeActivation($user);
|
|
|
|
self::assertNull($user->getWelcomeActivationNonce());
|
|
self::assertNull($user->getWelcomeActivationExpiresAt());
|
|
self::assertInstanceOf(\DateTimeImmutable::class, $user->getWelcomeActivatedAt());
|
|
}
|
|
|
|
public function testValidateActivationRequestAcceptsValidSignedRequestAndRejectsReplay(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setWelcomeActivationNonce('current-nonce');
|
|
|
|
$manager = $this->createValidationManager($user);
|
|
$request = Request::create(
|
|
$this->buildActivationUrl('[email protected]', 'current-nonce', time() + 3600),
|
|
'GET'
|
|
);
|
|
|
|
self::assertSame($user, $manager->validateActivationRequest($request));
|
|
|
|
$manager->consumeActivation($user);
|
|
|
|
$this->expectException(NotFoundHttpException::class);
|
|
$manager->validateActivationRequest($request);
|
|
}
|
|
|
|
public function testValidateActivationRequestRejectsExpiredLink(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setWelcomeActivationNonce('current-nonce');
|
|
|
|
$manager = $this->createValidationManager($user);
|
|
$request = Request::create(
|
|
$this->buildActivationUrl('[email protected]', 'current-nonce', time() - 60),
|
|
'GET'
|
|
);
|
|
|
|
$this->expectException(GuestAccountActivationExpiredException::class);
|
|
$manager->validateActivationRequest($request);
|
|
}
|
|
|
|
public function testValidateActivationUrlForResendAcceptsExpiredLink(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setWelcomeActivationNonce('current-nonce');
|
|
|
|
$manager = $this->createValidationManager($user);
|
|
$activationUrl = $this->buildActivationUrl('[email protected]', 'current-nonce', time() - 60);
|
|
|
|
self::assertSame($user, $manager->validateActivationUrlForResend($activationUrl));
|
|
}
|
|
|
|
public function testResendWelcomeEmailRotatesNonceAndSendsActivationMail(): void
|
|
{
|
|
$user = new User('[email protected]');
|
|
$user->setWelcomeActivationNonce('old-nonce');
|
|
|
|
$generatedRoute = null;
|
|
$generatedParameters = null;
|
|
$generatedReferenceType = null;
|
|
$capturedContext = null;
|
|
$capturedOptions = null;
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$mailer = $this->createMock(Mailer::class);
|
|
|
|
$entityManager->expects(self::once())->method('flush');
|
|
$passwordHasher->expects(self::never())->method('hashPassword');
|
|
$userRepository->expects(self::never())->method('findOneByNormalizedEmail');
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturnCallback(static function (string $route, array $parameters, int $referenceType) use (&$generatedRoute, &$generatedParameters, &$generatedReferenceType): string {
|
|
$generatedRoute = $route;
|
|
$generatedParameters = $parameters;
|
|
$generatedReferenceType = $referenceType;
|
|
|
|
return 'https://example.test/welcome/activate?'.http_build_query($parameters);
|
|
});
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail')
|
|
->willReturnCallback(static function (array $context, array $options) use (&$capturedContext, &$capturedOptions): void {
|
|
$capturedContext = $context;
|
|
$capturedOptions = $options;
|
|
});
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer);
|
|
|
|
$manager->resendWelcomeEmail($user);
|
|
|
|
self::assertSame('app_guest_account_activate', $generatedRoute);
|
|
self::assertSame(UrlGeneratorInterface::ABSOLUTE_URL, $generatedReferenceType);
|
|
self::assertSame('[email protected]', $generatedParameters['email']);
|
|
self::assertSame($user->getWelcomeActivationNonce(), $generatedParameters['nonce']);
|
|
self::assertSame('[email protected]', $capturedOptions['to']);
|
|
self::assertSame('Dein MyE&P Konto ist bereit', $capturedOptions['subject']);
|
|
self::assertSame('email/booking_guest_account_welcome.html.twig', $capturedOptions['template']);
|
|
self::assertTrue((new UriSigner('test-secret'))->check((string) $capturedContext['activationUrl']));
|
|
self::assertNotSame('old-nonce', $user->getWelcomeActivationNonce());
|
|
self::assertMatchesRegularExpression('/^[a-f0-9]{64}$/', (string) $user->getWelcomeActivationNonce());
|
|
}
|
|
|
|
public function testProvisionGuestAccountKeepsPersistedUserWhenMailSendFails(): void
|
|
{
|
|
$persistedUser = null;
|
|
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->expects(self::once())
|
|
->method('findOneByNormalizedEmail')
|
|
->with('[email protected]')
|
|
->willReturn(null);
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
$entityManager
|
|
->expects(self::once())
|
|
->method('persist')
|
|
->with(self::callback(static function (User $user) use (&$persistedUser): bool {
|
|
$persistedUser = $user;
|
|
|
|
return true;
|
|
}));
|
|
$entityManager->expects(self::once())->method('flush');
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$passwordHasher
|
|
->expects(self::once())
|
|
->method('hashPassword')
|
|
->willReturn('hashed-password');
|
|
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->expects(self::once())
|
|
->method('generate')
|
|
->willReturn('https://example.test/welcome/[email protected]&nonce=current&expires=9999999999');
|
|
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$mailer
|
|
->expects(self::once())
|
|
->method('createAndSendEmail')
|
|
->willThrowException(new TransportException('smtp down'));
|
|
|
|
$manager = $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer);
|
|
|
|
$this->expectException(TransportException::class);
|
|
try {
|
|
$manager->provisionGuestAccount('[email protected]', 'Alice', 'Example');
|
|
} finally {
|
|
self::assertInstanceOf(User::class, $persistedUser);
|
|
self::assertSame('[email protected]', $persistedUser->getEmail());
|
|
self::assertSame(['ROLE_USER', 'ROLE_CUSTOMER'], $persistedUser->getRoles());
|
|
self::assertTrue($persistedUser->isProfileComplete());
|
|
self::assertNotNull($persistedUser->getWelcomeActivationNonce());
|
|
}
|
|
}
|
|
|
|
private function createManager(
|
|
UserRepository $userRepository,
|
|
EntityManagerInterface $entityManager,
|
|
UserPasswordHasherInterface $passwordHasher,
|
|
UrlGeneratorInterface $urlGenerator,
|
|
Mailer $mailer,
|
|
?BpnConnectClient $bpnConnectClient = null,
|
|
): BookingGuestAccountManager {
|
|
$bpnConnectClient ??= $this->createMock(BpnConnectClient::class);
|
|
|
|
return new BookingGuestAccountManager(
|
|
$bpnConnectClient,
|
|
$userRepository,
|
|
$entityManager,
|
|
new UserBootstrapper(),
|
|
$passwordHasher,
|
|
$urlGenerator,
|
|
new UriSigner('test-secret'),
|
|
$mailer,
|
|
new NullLogger(),
|
|
);
|
|
}
|
|
|
|
private function createValidationManager(User $user): BookingGuestAccountManager
|
|
{
|
|
$userRepository = $this->createMock(UserRepository::class);
|
|
$userRepository
|
|
->method('findOneByNormalizedEmail')
|
|
->willReturnCallback(static function (string $email) use ($user): ?User {
|
|
return '[email protected]' === $email ? $user : null;
|
|
});
|
|
|
|
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
|
|
$passwordHasher = $this->createMock(UserPasswordHasherInterface::class);
|
|
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
|
|
$mailer = $this->createMock(Mailer::class);
|
|
$bpnConnectClient = $this->createMock(BpnConnectClient::class);
|
|
|
|
return $this->createManager($userRepository, $entityManager, $passwordHasher, $urlGenerator, $mailer, $bpnConnectClient);
|
|
}
|
|
|
|
private function buildActivationUrl(string $email, string $nonce, int $expires): string
|
|
{
|
|
$baseUrl = 'https://example.test/welcome/activate?'.http_build_query([
|
|
'email' => $email,
|
|
'nonce' => $nonce,
|
|
'expires' => $expires,
|
|
]);
|
|
|
|
return (new UriSigner('test-secret'))->sign($baseUrl);
|
|
}
|
|
|
|
private function createLookupResult(string $email, int $bookingNumber, string $firstName, string $lastName): BookingLookupResult
|
|
{
|
|
return new BookingLookupResult(
|
|
bookingNumber: $bookingNumber,
|
|
bookingId: 98765,
|
|
addressId: 12345,
|
|
personId: 67890,
|
|
email: $email,
|
|
firstName: $firstName,
|
|
lastName: $lastName,
|
|
);
|
|
}
|
|
}
|