138 lines
5.7 KiB
PHP
138 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Form\Service;
|
|
|
|
use App\BusProNet\Model\Insurance;
|
|
use App\BusProNet\Model\Travel;
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Model\ParticipantDto;
|
|
use App\Form\Service\ParticipantFieldOptionsProvider;
|
|
use App\Service\BookingPriceCalculator;
|
|
use App\Service\InsuranceManager;
|
|
use App\Service\ServiceAvailabilityCalculator;
|
|
use App\Service\ServiceLabelFormatter;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
/**
|
|
* Regression tests for the family-insurance choices-list bug: the applicant selects a
|
|
* family insurance, it's correctly reassigned to the total-price tier on submit, but
|
|
* the re-rendered form showed no selection because the choices list was still filtered
|
|
* by the applicant's individual price instead of the total booking price.
|
|
*/
|
|
class ParticipantFieldOptionsProviderInsuranceTest extends TestCase
|
|
{
|
|
private ParticipantFieldOptionsProvider $provider;
|
|
private BookingPriceCalculator $priceCalculatorService;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$serviceAvailabilityCalculator = $this->createMock(ServiceAvailabilityCalculator::class);
|
|
// InsuranceManager is stateless - use the real implementation so actual
|
|
// eligibility/price-tier filtering runs, not a stubbed-out mock.
|
|
$insuranceService = new InsuranceManager();
|
|
$this->priceCalculatorService = $this->createMock(BookingPriceCalculator::class);
|
|
$serviceLabelFormatter = new ServiceLabelFormatter();
|
|
$translator = $this->createMock(TranslatorInterface::class);
|
|
$translator->method('trans')->willReturnCallback(fn (string $message) => $message);
|
|
|
|
$this->provider = new ParticipantFieldOptionsProvider(
|
|
$serviceAvailabilityCalculator,
|
|
$insuranceService,
|
|
$this->priceCalculatorService,
|
|
$serviceLabelFormatter,
|
|
$translator
|
|
);
|
|
}
|
|
|
|
public function testFamilyInsuranceCorrectTierAppearsInApplicantChoicesUsingTotalPrice(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->dateFrom = new \DateTimeImmutable('2025-08-01');
|
|
$travel->dateTo = new \DateTimeImmutable('2025-08-08');
|
|
|
|
// Low tier only matches the applicant's individual price (300), not the total (450)
|
|
$lowTierFamilyInsurance = new Insurance();
|
|
$lowTierFamilyInsurance->id = '1';
|
|
$lowTierFamilyInsurance->label = 'Reise-Rücktritt Familie';
|
|
$lowTierFamilyInsurance->familyInsurance = true;
|
|
$lowTierFamilyInsurance->travelPriceFrom = 0.0;
|
|
$lowTierFamilyInsurance->travelPriceTo = 400.0;
|
|
|
|
// Correct tier matches the total booking price (450), not the applicant's individual price (300)
|
|
$correctTierFamilyInsurance = new Insurance();
|
|
$correctTierFamilyInsurance->id = '2';
|
|
$correctTierFamilyInsurance->label = 'Reise-Rücktritt Familie';
|
|
$correctTierFamilyInsurance->familyInsurance = true;
|
|
$correctTierFamilyInsurance->travelPriceFrom = 400.01;
|
|
$correctTierFamilyInsurance->travelPriceTo = 600.0;
|
|
|
|
$travel->insurances = [$lowTierFamilyInsurance, $correctTierFamilyInsurance];
|
|
|
|
$applicant = new ParticipantDto();
|
|
$applicant->index = 0;
|
|
$applicant->dateOfBirth = new \DateTimeImmutable('1990-06-15');
|
|
|
|
$child = new ParticipantDto();
|
|
$child->index = 1;
|
|
$child->dateOfBirth = new \DateTimeImmutable('2015-01-01');
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$applicant, $child];
|
|
|
|
$this->priceCalculatorService
|
|
->method('calculateIndividualParticipantPriceExcludingInsurance')
|
|
->willReturn(300.0);
|
|
$this->priceCalculatorService
|
|
->method('calculateTotalBookingPriceExcludingInsurance')
|
|
->willReturn(450.0);
|
|
|
|
$options = $this->provider->getFieldOptions('insurance', $bookingDto, 0);
|
|
|
|
$choiceIds = array_map(fn (Insurance $insurance) => $insurance->id, $options['choices']);
|
|
|
|
$this->assertContains('2', $choiceIds, 'Family insurance tier matching the total booking price must be selectable');
|
|
$this->assertNotContains('1', $choiceIds, 'Family insurance tier that only matches the individual price must not be selectable');
|
|
}
|
|
|
|
public function testFamilyInsuranceNeverAppearsInDependentChoices(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->dateFrom = new \DateTimeImmutable('2025-08-01');
|
|
$travel->dateTo = new \DateTimeImmutable('2025-08-08');
|
|
|
|
$familyInsurance = new Insurance();
|
|
$familyInsurance->id = '1';
|
|
$familyInsurance->label = 'Reise-Rücktritt Familie';
|
|
$familyInsurance->familyInsurance = true;
|
|
|
|
$travel->insurances = [$familyInsurance];
|
|
|
|
$applicant = new ParticipantDto();
|
|
$applicant->index = 0;
|
|
$applicant->dateOfBirth = new \DateTimeImmutable('1990-06-15');
|
|
|
|
$child = new ParticipantDto();
|
|
$child->index = 1;
|
|
$child->dateOfBirth = new \DateTimeImmutable('2015-01-01');
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$applicant, $child];
|
|
|
|
$this->priceCalculatorService
|
|
->method('calculateIndividualParticipantPriceExcludingInsurance')
|
|
->willReturn(0.0);
|
|
$this->priceCalculatorService
|
|
->method('calculateTotalBookingPriceExcludingInsurance')
|
|
->willReturn(450.0);
|
|
|
|
$options = $this->provider->getFieldOptions('insurance', $bookingDto, 1);
|
|
|
|
$choiceIds = array_map(fn (Insurance $insurance) => $insurance->id, $options['choices']);
|
|
|
|
$this->assertNotContains('1', $choiceIds, 'Family insurance must never be selectable for a non-applicant participant');
|
|
}
|
|
}
|