Files
myep/tests/Controller/Booking/Create/Step4ControllerTest.php
T

294 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Controller\Booking\Create;
use App\BusProNet\ApiClient;
use App\BusProNet\Constants;
use App\BusProNet\Model\BookingResponse;
use App\BusProNet\Model\Hotel;
use App\BusProNet\Model\Travel;
use App\Entity\User;
use App\Form\Model\BookingCreateContext;
use App\Form\Model\BookingDto;
use App\Form\Model\BookingSummaryDto;
use App\Form\Model\BookingSummaryPricingDto;
use App\Form\Model\BookingSummaryVoucherDto;
use App\Form\Model\ParticipantDto;
use App\Form\Model\RoomGroupsDto;
use App\Repository\UserRepository;
use App\Service\BookingConfigurator;
use App\Service\BookingCreateContextFactory;
use App\Service\BookingGuestAccountManager;
use App\Service\BookingSessionManager;
use App\Service\NewsletterManager;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\SecurityBundle\Security;
class Step4ControllerTest extends WebTestCase
{
protected function tearDown(): void
{
self::ensureKernelShutdown();
parent::tearDown();
}
public function testStep4RendersCheckboxAndProvisionsGuestAccountAfterSuccessfulBooking(): void
{
$client = self::createClient([], ['HTTPS' => 'on']);
$client->disableReboot();
$bookingDto = $this->createBookingDto();
$context = $this->createBookingContext($bookingDto);
$bookingSessionManager = $this->createMock(BookingSessionManager::class);
$bookingSessionManager
->expects(self::exactly(2))
->method('getOrCreateBookingCreateDto')
->willReturn($bookingDto);
$contextFactory = $this->createMock(BookingCreateContextFactory::class);
$contextFactory
->expects(self::exactly(2))
->method('createWithParticipantPrices')
->with($bookingDto)
->willReturn($context);
$userRepository = $this->createMock(UserRepository::class);
$userRepository
->expects(self::exactly(2))
->method('findOneByNormalizedEmail')
->with('[email protected]')
->willReturn(null);
$guestAccountManager = $this->createMock(BookingGuestAccountManager::class);
$guestAccountManager
->expects(self::once())
->method('provisionGuestAccountForBooking')
->with('[email protected]', 123, 'Alice', 'Example')
->willReturn(true);
$bookingConfigurator = $this->createMock(BookingConfigurator::class);
$bookingConfigurator
->expects(self::once())
->method('applyCreateBookingStatusRules');
$apiClient = $this->createMock(ApiClient::class);
$apiClient
->expects(self::once())
->method('createBooking')
->with($bookingDto)
->willReturn(new BookingResponse(BookingResponse::BOOKING_PROCESSED, 123));
$newsletterManager = $this->createMock(NewsletterManager::class);
$newsletterManager->expects(self::never())->method('requestConfirmation');
self::getContainer()->set(BookingSessionManager::class, $bookingSessionManager);
self::getContainer()->set(BookingCreateContextFactory::class, $contextFactory);
self::getContainer()->set(UserRepository::class, $userRepository);
self::getContainer()->set(BookingGuestAccountManager::class, $guestAccountManager);
self::getContainer()->set(BookingConfigurator::class, $bookingConfigurator);
self::getContainer()->set(ApiClient::class, $apiClient);
self::getContainer()->set(NewsletterManager::class, $newsletterManager);
$crawler = $client->request('GET', '/bookings/create/confirmation');
self::assertResponseIsSuccessful();
self::assertStringContainsString('Benutzeraccount in MyE&P anlegen', $client->getResponse()->getContent());
$form = $crawler->selectButton('Verbindlich buchen')->form([
'booking_create_step4[confirmationAccepted]' => '1',
'booking_create_step4[termsAccepted]' => '1',
'booking_create_step4[guestAccountOptIn]' => '1',
]);
$client->submit($form);
self::assertResponseRedirects('/bookings/create/success');
}
public function testStep4HidesGuestAccountCheckboxForAuthenticatedUsers(): void
{
$bookingDto = $this->createBookingDto();
$userRepository = $this->createMock(UserRepository::class);
$userRepository->expects(self::never())->method('findOneByNormalizedEmail');
$controller = $this->createAuthenticatedController(
user: new User('[email protected]'),
userRepository: $userRepository,
);
self::assertNull($controller->exposeGuestAccountTargetEmail($bookingDto));
}
public function testStep4HidesGuestAccountCheckboxWhenLocalUserAlreadyExists(): void
{
$client = self::createClient([], ['HTTPS' => 'on']);
$client->disableReboot();
$bookingDto = $this->createBookingDto();
$context = $this->createBookingContext($bookingDto);
$bookingSessionManager = $this->createMock(BookingSessionManager::class);
$bookingSessionManager
->expects(self::once())
->method('getOrCreateBookingCreateDto')
->willReturn($bookingDto);
$contextFactory = $this->createMock(BookingCreateContextFactory::class);
$contextFactory
->expects(self::once())
->method('createWithParticipantPrices')
->with($bookingDto)
->willReturn($context);
$userRepository = $this->createMock(UserRepository::class);
$userRepository
->expects(self::once())
->method('findOneByNormalizedEmail')
->with('[email protected]')
->willReturn(new User('[email protected]'));
$guestAccountManager = $this->createMock(BookingGuestAccountManager::class);
$guestAccountManager->expects(self::never())->method('provisionGuestAccount');
self::getContainer()->set(BookingSessionManager::class, $bookingSessionManager);
self::getContainer()->set(BookingCreateContextFactory::class, $contextFactory);
self::getContainer()->set(UserRepository::class, $userRepository);
self::getContainer()->set(BookingGuestAccountManager::class, $guestAccountManager);
$client->request('GET', '/bookings/create/confirmation');
self::assertResponseIsSuccessful();
self::assertStringNotContainsString('Benutzeraccount in MyE&P anlegen', $client->getResponse()->getContent());
}
public function testGuestAccountActivationRedirectsToPersonalDataAndForcesPasswordChange(): void
{
$client = self::createClient([], ['HTTPS' => 'on']);
$client->disableReboot();
$guestAccountManager = $this->createMock(BookingGuestAccountManager::class);
$guestAccountManager
->expects(self::once())
->method('validateActivationRequest')
->with(self::isInstanceOf(\Symfony\Component\HttpFoundation\Request::class))
->willReturn(new User('[email protected]'));
$guestAccountManager
->expects(self::never())
->method('consumeActivation');
$security = $this->createMock(Security::class);
$security
->expects(self::once())
->method('login')
->with(self::isInstanceOf(User::class), null, 'main')
->willReturn(null);
self::getContainer()->set(BookingGuestAccountManager::class, $guestAccountManager);
self::getContainer()->set(Security::class, $security);
$client->request('GET', '/welcome/[email protected]&nonce=current-nonce&expires=9999999999&_hash=signature');
self::assertResponseRedirects('/personal-data');
self::assertTrue($client->getRequest()->getSession()->get(\App\Controller\Account\PersonalDataController::SESSION_FORCE_PASSWORD_CHANGE_KEY));
}
private function createBookingDto(): BookingDto
{
$travel = new Travel();
$travel->id = 123;
$travel->label = 'Winterreise';
$travel->productCode = 'PROD-1';
$travel->dateFrom = new \DateTimeImmutable('2026-01-01');
$travel->dateTo = new \DateTimeImmutable('2026-01-08');
$hotel = new Hotel();
$hotel->name = 'Hotel Test';
$travel->hotel = $hotel;
$bookingDto = new BookingDto($travel, 77);
$bookingDto->currentStep = 4;
$bookingDto->bookingStatus = 'F';
$bookingDto->paymentMethod = Constants::PAYMENT_METHOD_TRANSFER;
$participant = new ParticipantDto();
$participant->index = 0;
$participant->firstName = 'Alice';
$participant->lastName = 'Example';
$participant->email = '[email protected]';
$participant->gender = 'M';
$participant->nationality = 'DE';
$participant->dateOfBirth = new \DateTimeImmutable('1990-01-01');
$bookingDto->participants = [$participant];
return $bookingDto;
}
private function createBookingContext(BookingDto $bookingDto): BookingCreateContext
{
$pricing = new BookingSummaryPricingDto([], [], null, [], 0.0);
$vouchers = new BookingSummaryVoucherDto(null, 0.0);
$summary = new BookingSummaryDto([], 1, $pricing, $vouchers, null);
return new BookingCreateContext(
bookingDto: $bookingDto,
summaryData: $summary,
groupedRooms: new RoomGroupsDto([], []),
participantPrices: [0.0],
);
}
private function createAuthenticatedController(User $user, UserRepository $userRepository): TestableStep4Controller
{
return new TestableStep4Controller(
$this->createMock(BookingConfigurator::class),
$this->createMock(BookingSessionManager::class),
$this->createMock(BookingCreateContextFactory::class),
$this->createMock(BookingGuestAccountManager::class),
$this->createMock(ApiClient::class),
$this->createMock(\Symfony\Contracts\Cache\CacheInterface::class),
$this->createMock(NewsletterManager::class),
$userRepository,
$this->createMock(\Psr\Log\LoggerInterface::class),
$user,
);
}
}
final class TestableStep4Controller extends \App\Controller\Booking\Create\Step4Controller
{
public function __construct(
BookingConfigurator $bookingService,
BookingSessionManager $bookingSessionService,
BookingCreateContextFactory $createContextFactory,
BookingGuestAccountManager $guestAccountManager,
ApiClient $apiClient,
\Symfony\Contracts\Cache\CacheInterface $cache,
NewsletterManager $doubleOptInService,
UserRepository $userRepository,
\Psr\Log\LoggerInterface $logger,
private readonly User $user,
) {
parent::__construct(
$bookingService,
$bookingSessionService,
$createContextFactory,
$guestAccountManager,
$apiClient,
$cache,
$doubleOptInService,
$userRepository,
$logger,
);
}
public function exposeGuestAccountTargetEmail(BookingDto $bookingDto): ?string
{
return $this->resolveGuestAccountTargetEmail($bookingDto);
}
protected function getUser(): \Symfony\Component\Security\Core\User\UserInterface|null
{
return $this->user;
}
}