fix: refresh live room availability during create flow
This commit is contained in:
1 parent
a73e8a593f
commit
cdce7d57e0
26 files changed
+1976
-54
No files matched your search
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Controller\Booking\Create;
|
||||
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Controller\Booking\Create\Step4Controller;
|
||||
use App\Form\Model\BookingCreateContext;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Form\Model\RoomGroupsDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingAvailabilityRefreshResult;
|
||||
use App\Service\BookingConfigurator;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\NewsletterManager;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
class Step4ControllerTest extends TestCase
|
||||
{
|
||||
public function testIndexRerendersWithErrorWhenForcedRefreshFails(): void
|
||||
{
|
||||
$request = Request::create('/bookings/create/confirmation', 'POST');
|
||||
$bookingDto = $this->createBookingDto();
|
||||
|
||||
$bookingSessionService = $this->createMock(BookingSessionManager::class);
|
||||
$bookingSessionService->expects($this->once())
|
||||
->method('getOrCreateBookingCreateDto')
|
||||
->with($request)
|
||||
->willReturn($bookingDto);
|
||||
$bookingSessionService->expects($this->exactly(2))
|
||||
->method('refreshCreateAvailability')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
new BookingAvailabilityRefreshResult(
|
||||
availabilityRefreshed: true,
|
||||
roomSelectionQuantityReset: false,
|
||||
roomSelectionSnapshotChanged: false,
|
||||
hasAvailableRooms: true,
|
||||
),
|
||||
new BookingAvailabilityRefreshResult(
|
||||
availabilityRefreshed: false,
|
||||
roomSelectionQuantityReset: false,
|
||||
roomSelectionSnapshotChanged: false,
|
||||
hasAvailableRooms: true,
|
||||
),
|
||||
);
|
||||
|
||||
$bookingService = $this->createMock(BookingConfigurator::class);
|
||||
$bookingService->expects($this->never())->method('applyCreateBookingStatusRules');
|
||||
|
||||
$apiClient = $this->createMock(ApiClient::class);
|
||||
$apiClient->expects($this->never())->method('createBooking');
|
||||
|
||||
$form = $this->createMock(FormInterface::class);
|
||||
$form->expects($this->once())->method('handleRequest')->with($request)->willReturnSelf();
|
||||
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
|
||||
$form->expects($this->once())->method('isValid')->willReturn(true);
|
||||
|
||||
$createContextFactory = $this->createMock(BookingCreateContextFactory::class);
|
||||
$createContextFactory->expects($this->once())
|
||||
->method('createWithParticipantPrices')
|
||||
->with($bookingDto)
|
||||
->willReturn(new BookingCreateContext(
|
||||
bookingDto: $bookingDto,
|
||||
summaryData: $this->createMock(BookingSummaryDto::class),
|
||||
groupedRooms: new RoomGroupsDto([], []),
|
||||
));
|
||||
|
||||
$controller = new TestableStep4Controller(
|
||||
$bookingService,
|
||||
$bookingSessionService,
|
||||
$createContextFactory,
|
||||
$apiClient,
|
||||
$this->createMock(CacheInterface::class),
|
||||
$this->createMock(NewsletterManager::class),
|
||||
new NullLogger(),
|
||||
$form,
|
||||
);
|
||||
|
||||
$response = $controller->index($request);
|
||||
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertNotInstanceOf(RedirectResponse::class, $response);
|
||||
$this->assertSame('rendered:booking/create/step_4.html.twig', $response->getContent());
|
||||
$this->assertSame(['error', 'Die Verfügbarkeit konnte gerade nicht aktualisiert werden. Bitte versuche es erneut.'], $controller->flashes[0]);
|
||||
}
|
||||
|
||||
public function testIndexRedirectsWithWarningWhenPriceChangesDuringForcedRefresh(): void
|
||||
{
|
||||
$request = Request::create('/bookings/create/confirmation', 'POST');
|
||||
$bookingDto = $this->createBookingDto();
|
||||
|
||||
$bookingSessionService = $this->createMock(BookingSessionManager::class);
|
||||
$bookingSessionService->expects($this->once())
|
||||
->method('getOrCreateBookingCreateDto')
|
||||
->with($request)
|
||||
->willReturn($bookingDto);
|
||||
$bookingSessionService->expects($this->exactly(2))
|
||||
->method('refreshCreateAvailability')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
new BookingAvailabilityRefreshResult(
|
||||
availabilityRefreshed: true,
|
||||
roomSelectionQuantityReset: false,
|
||||
roomSelectionSnapshotChanged: false,
|
||||
hasAvailableRooms: true,
|
||||
),
|
||||
new BookingAvailabilityRefreshResult(
|
||||
availabilityRefreshed: true,
|
||||
roomSelectionQuantityReset: false,
|
||||
roomSelectionSnapshotChanged: true,
|
||||
hasAvailableRooms: true,
|
||||
),
|
||||
);
|
||||
|
||||
$bookingService = $this->createMock(BookingConfigurator::class);
|
||||
$bookingService->expects($this->never())->method('applyCreateBookingStatusRules');
|
||||
|
||||
$apiClient = $this->createMock(ApiClient::class);
|
||||
$apiClient->expects($this->never())->method('createBooking');
|
||||
|
||||
$form = $this->createMock(FormInterface::class);
|
||||
$form->expects($this->once())->method('handleRequest')->with($request)->willReturnSelf();
|
||||
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
|
||||
$form->expects($this->once())->method('isValid')->willReturn(true);
|
||||
|
||||
$createContextFactory = $this->createMock(BookingCreateContextFactory::class);
|
||||
$createContextFactory->expects($this->never())->method('createWithParticipantPrices');
|
||||
|
||||
$controller = new TestableStep4Controller(
|
||||
$bookingService,
|
||||
$bookingSessionService,
|
||||
$createContextFactory,
|
||||
$apiClient,
|
||||
$this->createMock(CacheInterface::class),
|
||||
$this->createMock(NewsletterManager::class),
|
||||
new NullLogger(),
|
||||
$form,
|
||||
);
|
||||
|
||||
$response = $controller->index($request);
|
||||
|
||||
$this->assertInstanceOf(RedirectResponse::class, $response);
|
||||
$this->assertSame('app_booking_create_step_4', $response->getTargetUrl());
|
||||
$this->assertSame(['warning', 'Die Zimmerpreise haben sich aktualisiert. Bitte überprüfe deine Buchung erneut.'], $controller->flashes[0]);
|
||||
}
|
||||
|
||||
private function createBookingDto(): BookingDto
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->id = 1234;
|
||||
$travel->hotelId = 77;
|
||||
|
||||
$room = new Room();
|
||||
$room->id = 1;
|
||||
$room->label = 'Doppelzimmer';
|
||||
$room->available = 2;
|
||||
$room->status = 'Frei';
|
||||
$travel->rooms = [$room];
|
||||
|
||||
$bookingDto = new BookingDto($travel, 77);
|
||||
$bookingDto->currentStep = 4;
|
||||
|
||||
$selection = new RoomSelectionDto();
|
||||
$selection->id = 1;
|
||||
$selection->quantity = 1;
|
||||
$bookingDto->roomSelections = [$selection];
|
||||
|
||||
return $bookingDto;
|
||||
}
|
||||
}
|
||||
|
||||
final class TestableStep4Controller extends Step4Controller
|
||||
{
|
||||
/**
|
||||
* @var array<int, array{string, mixed}>
|
||||
*/
|
||||
public array $flashes = [];
|
||||
|
||||
public function __construct(
|
||||
BookingConfigurator $bookingService,
|
||||
BookingSessionManager $bookingSessionService,
|
||||
BookingCreateContextFactory $createContextFactory,
|
||||
ApiClient $apiClient,
|
||||
CacheInterface $cache,
|
||||
NewsletterManager $doubleOptInService,
|
||||
\Psr\Log\LoggerInterface $logger,
|
||||
private readonly FormInterface $form,
|
||||
) {
|
||||
parent::__construct(
|
||||
$bookingService,
|
||||
$bookingSessionService,
|
||||
$createContextFactory,
|
||||
$apiClient,
|
||||
$cache,
|
||||
$doubleOptInService,
|
||||
$logger,
|
||||
);
|
||||
}
|
||||
|
||||
protected function createForm(string $type, mixed $data = null, array $options = []): FormInterface
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
protected function addFlash(string $type, mixed $message): void
|
||||
{
|
||||
$this->flashes[] = [$type, $message];
|
||||
}
|
||||
|
||||
protected function render(string $view, array $parameters = [], ?Response $response = null): Response
|
||||
{
|
||||
return new Response('rendered:'.$view);
|
||||
}
|
||||
|
||||
protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse
|
||||
{
|
||||
return new RedirectResponse($route, $status);
|
||||
}
|
||||
|
||||
protected function generateUrl(string $route, array $parameters = [], int $referenceType = 0): string
|
||||
{
|
||||
return $route;
|
||||
}
|
||||
|
||||
protected function getUser(): ?\Symfony\Component\Security\Core\User\UserInterface
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user