fix: refuse implausible accommodation booking prices

This commit is contained in:
fromme committed 2026-09-26 15:39:04 +02:00
1 parent 773cd4f847
commit 56adf72d12
16 files changed
+628 -15

No files matched your search

@@ -8,11 +8,13 @@ use App\Controller\Groups\Booking\Step4Controller;
use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AccommodationBooking;
use App\Enum\Groups\AccommodationBookingOrigin;
use App\Exception\ImplausibleBookingPriceException;
use App\Form\Model\AccommodationBookingDto;
use App\Service\AccommodationBookingService;
use App\Service\AccommodationSessionManager;
use App\Service\AccommodationTermsUrlProvider;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -302,6 +304,96 @@ class Step4ControllerTest extends TestCase
self::assertNull($sessionManager->getDto($request));
}
public function testConfirmPostEndsOnTheErrorPageWhenThePriceIsImplausible(): void
{
// A mistyped catalog price is the one way an implausible total still gets computed, and
// the customer must not meet it as a 500 — SQLSTATE 22003 on /admin is what started this.
$dto = new AccommodationBookingDto();
$dto->currentStep = 4;
$dto->accommodationId = 1;
$session = new Session(new MockArraySessionStorage());
$request = Request::create('/groups/booking/step-4/confirm', 'POST');
$request->setSession($session);
$request->headers->set('HX-Request', 'true');
$sessionManager = new AccommodationSessionManager();
$sessionManager->save($request, $dto);
$accommodation = (new Accommodation())
->setName('Hotel')
->setCalendarCode('HOTEL')
->setMaxAdolescentAge(17);
$bookingService = $this->createMock(AccommodationBookingService::class);
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
$bookingService->method('loadAvailableServices')->willReturn([
'boardServices' => [],
'additionalServices' => [],
'groupedAdditionalServices' => [],
'ungroupedAdditionalServices' => [],
]);
$bookingService->method('loadPrices')->willReturn([]);
$bookingService->method('finalizeBooking')
->willThrowException(new ImplausibleBookingPriceException(AccommodationBooking::MAX_TOTAL_PRICE + 1, 40, 2));
$confirmationForm = $this->createStub(FormInterface::class);
$confirmationForm->method('handleRequest')->willReturnSelf();
$confirmationForm->method('isSubmitted')->willReturn(true);
$confirmationForm->method('isValid')->willReturn(true);
$controller = new TestableStep4Controller($bookingService, $sessionManager, $confirmationForm);
$response = $controller->confirm($request);
self::assertSame('/app_groups_booking_error', $response->headers->get('HX-Redirect'));
// The session survives: the desk still has the inquiry to pick up by phone.
self::assertNotNull($sessionManager->getDto($request));
}
public function testIndexPostEndsOnTheErrorPageWhenThePriceIsImplausible(): void
{
$dto = new AccommodationBookingDto();
$dto->currentStep = 4;
$dto->accommodationId = 1;
$session = new Session(new MockArraySessionStorage());
$request = Request::create('/groups/booking/step-4', 'POST');
$request->setSession($session);
$sessionManager = new AccommodationSessionManager();
$sessionManager->save($request, $dto);
$accommodation = (new Accommodation())
->setName('Hotel')
->setCalendarCode('HOTEL')
->setMaxAdolescentAge(17);
$bookingService = $this->createMock(AccommodationBookingService::class);
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
$bookingService->method('loadAvailableServices')->willReturn([
'boardServices' => [],
'additionalServices' => [],
'groupedAdditionalServices' => [],
'ungroupedAdditionalServices' => [],
]);
$bookingService->method('loadPrices')->willReturn([]);
$bookingService->method('finalizeBooking')
->willThrowException(new ImplausibleBookingPriceException(AccommodationBooking::MAX_TOTAL_PRICE + 1, 40, 2));
$inquiryForm = $this->createStub(FormInterface::class);
$inquiryForm->method('handleRequest')->willReturnSelf();
$inquiryForm->method('isSubmitted')->willReturn(true);
$inquiryForm->method('isValid')->willReturn(true);
$controller = new TestableStep4Controller($bookingService, $sessionManager, $inquiryForm);
$response = $controller->index($request);
self::assertInstanceOf(RedirectResponse::class, $response);
self::assertSame('/app_groups_booking_error', $response->getTargetUrl());
}
public function testConfirmPostInvalidReRendersModalWithoutPersisting(): void
{
$dto = new AccommodationBookingDto();
@@ -358,6 +450,7 @@ final class TestableStep4Controller extends Step4Controller
$bookingService,
$sessionManager,
new AccommodationTermsUrlProvider(['AT' => '', 'CH' => '', 'IT' => ''], 'https://example.test/agb/'),
new NullLogger(),
);
}