feat: move display-assembly out of BookingPriceCalculator

This commit is contained in:
Björn Fromme
2026-04-16 13:34:54 +02:00
parent d1c92f2957
commit 4d86629315
8 changed files with 1093 additions and 1028 deletions
@@ -0,0 +1,256 @@
<?php
declare(strict_types=1);
namespace App\Tests\Service;
use App\BusProNet\Model\Insurance;
use App\BusProNet\Model\Travel;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Service\BookingPricingAssembler;
use App\Service\InsuranceManager;
use App\Service\ParticipantEligibilityChecker;
use App\Service\ParticipantPricingCalculator;
use App\Service\RoomPricingCalculator;
use PHPUnit\Framework\TestCase;
class BookingPricingAssemblerTest extends TestCase
{
private BookingPricingAssembler $assembler;
private ParticipantEligibilityChecker $eligibilityChecker;
protected function setUp(): void
{
$this->eligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$this->eligibilityChecker->method('isParticipantEligible')->willReturn(true);
$this->assembler = $this->createAssembler($this->eligibilityChecker);
}
public function testServicePricingWithoutBulkInsurance(): void
{
$travel = new Travel();
$travel->insurances = [];
$insurance1 = $this->createInsurance(1, 'Insurance A', 50.0);
$insurance2 = $this->createInsurance(2, 'Insurance B', 75.0);
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insurance1;
$participant1->bulkInsuranceBooking = false;
$participant2 = new ParticipantDto();
$participant2->index = 1;
$participant2->insurance = $insurance2;
$bookingDto = new BookingDto($travel, 2);
$bookingDto->participants = [$participant1, $participant2];
$result = $this->assembler->calculateServicePricing($bookingDto);
$this->assertNotEmpty($result);
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$this->assertCount(2, $insuranceGroup['services'], 'Should have 2 different insurance line items');
}
public function testServicePricingWithBulkInsuranceSamePriceTier(): void
{
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
$insurance->travelPriceFrom = 0.0;
$insurance->travelPriceTo = 10000.0;
$travel = new Travel();
$travel->insurances = [$insurance];
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insurance;
$participant1->bulkInsuranceBooking = true;
$participant2 = new ParticipantDto();
$participant2->index = 1;
$participant2->insurance = null;
$participant3 = new ParticipantDto();
$participant3->index = 2;
$participant3->insurance = null;
$bookingDto = new BookingDto($travel, 3);
$bookingDto->participants = [$participant1, $participant2, $participant3];
$result = $this->assembler->calculateServicePricing($bookingDto);
// 3× same insurance should aggregate into one line item
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
$this->assertEquals(3, $insuranceGroup['services'][0]['participantCount'], 'Should count all 3 participants');
$this->assertEquals(150.0, $insuranceGroup['services'][0]['totalPrice'], 'Total should be 3 × €50');
}
public function testServicePricingWithBulkInsuranceShowsPriceTierAdjustment(): void
{
$insuranceTier1 = $this->createInsurance(1, 'Reise-Rücktritt', 30.0, 'RRV');
$insuranceTier1->travelPriceFrom = 0.0;
$insuranceTier1->travelPriceTo = 500.0;
$insuranceTier2 = $this->createInsurance(2, 'Reise-Rücktritt', 50.0, 'RRV');
$insuranceTier2->travelPriceFrom = 501.0;
$insuranceTier2->travelPriceTo = 1000.0;
$travel = new Travel();
$travel->insurances = [$insuranceTier1, $insuranceTier2];
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insuranceTier2;
$participant1->bulkInsuranceBooking = true;
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant1];
$result = $this->assembler->calculateServicePricing($bookingDto);
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count applicant');
}
public function testServicePricingWithBulkInsuranceWhenNoBulkEnabled(): void
{
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
$travel = new Travel();
$travel->insurances = [$insurance];
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insurance;
$participant1->bulkInsuranceBooking = false;
$participant2 = new ParticipantDto();
$participant2->index = 1;
$participant2->insurance = null;
$bookingDto = new BookingDto($travel, 2);
$bookingDto->participants = [$participant1, $participant2];
$result = $this->assembler->calculateServicePricing($bookingDto);
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count only applicant');
$this->assertEquals(50.0, $insuranceGroup['services'][0]['totalPrice'], 'Total should be 1 × €50');
}
public function testServicePricingWithIneligibleParticipant(): void
{
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
$travel = new Travel();
$travel->insurances = [$insurance];
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insurance;
$participant1->bulkInsuranceBooking = false;
$participant2 = new ParticipantDto();
$participant2->index = 1;
$participant2->insurance = null;
$bookingDto = new BookingDto($travel, 2);
$bookingDto->participants = [$participant1, $participant2];
$eligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$eligibilityChecker->method('isParticipantEligible')
->willReturnCallback(fn ($booking, int $index): bool => 0 === $index);
$result = $this->createAssembler($eligibilityChecker)->calculateServicePricing($bookingDto);
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count only applicant');
}
public function testServicePricingWithBulkInsuranceCountsAllParticipants(): void
{
$insuranceA = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
$travel = new Travel();
$travel->insurances = [$insuranceA];
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
$participant1 = new ParticipantDto();
$participant1->index = 0;
$participant1->insurance = $insuranceA;
$participant1->bulkInsuranceBooking = true;
$participant2 = new ParticipantDto();
$participant2->index = 1;
$participant2->insurance = null;
$participant3 = new ParticipantDto();
$participant3->index = 2;
$participant3->insurance = null;
$bookingDto = new BookingDto($travel, 3);
$bookingDto->participants = [$participant1, $participant2, $participant3];
$result = $this->assembler->calculateServicePricing($bookingDto);
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
$totalParticipants = array_sum(array_column($insuranceGroup['services'], 'participantCount'));
$this->assertEquals(3, $totalParticipants, 'Should count all 3 participants across all tiers');
}
// Helpers
private function createAssembler(ParticipantEligibilityChecker $eligibilityChecker): BookingPricingAssembler
{
$roomPricingCalculator = new RoomPricingCalculator();
return new BookingPricingAssembler(
$roomPricingCalculator,
$eligibilityChecker,
new InsuranceManager(),
new ParticipantPricingCalculator($roomPricingCalculator),
);
}
private function createInsurance(int $id, string $label, float $price, string $subType = 'RRV'): Insurance
{
$insurance = new Insurance();
$insurance->id = (string) $id;
$insurance->label = $label;
$insurance->price = $price;
$insurance->subType = $subType;
$insurance->package = false;
$insurance->complementary = false;
return $insurance;
}
private function findServiceGroup(array $groups, string $groupName): ?array
{
foreach ($groups as $group) {
if ($group['groupName'] === $groupName) {
return $group;
}
}
return null;
}
}