313 lines
10 KiB
PHP
313 lines
10 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Tests\Service;
|
||
|
||
use App\BusProNet\Model\Booking;
|
||
use App\BusProNet\Model\Room;
|
||
use App\BusProNet\Model\Travel;
|
||
use App\Form\Model\BookingDto;
|
||
use App\Form\Model\ParticipantDto;
|
||
use App\Form\Model\RoomSelectionDto;
|
||
use App\Service\RoomPricingCalculator;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
class RoomPricingCalculatorTest extends TestCase
|
||
{
|
||
private RoomPricingCalculator $calculator;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->calculator = new RoomPricingCalculator();
|
||
}
|
||
|
||
public function testCalculateRoomPricingWithParticipantBasedCalculation(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 1;
|
||
$room->price = 100.0;
|
||
$room->minPax = 2;
|
||
$room->label = 'Double Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 1;
|
||
$roomSelection->quantity = 2;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
$bookingDto->participants = [
|
||
$this->participantInRoom(1),
|
||
$this->participantInRoom(1),
|
||
$this->participantInRoom(1),
|
||
$this->participantInRoom(1),
|
||
];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
// 4 assigned participants × €100 = €400 total
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(1, $result[0]['roomId']);
|
||
$this->assertEquals('Double Room', $result[0]['label']);
|
||
$this->assertEquals(2, $result[0]['quantity']);
|
||
$this->assertEquals(4, $result[0]['participantCount']);
|
||
$this->assertEquals(100.0, $result[0]['unitPrice']);
|
||
$this->assertEquals(400.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingWithSingleRoomSelection(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 2;
|
||
$room->price = 150.0;
|
||
$room->minPax = 3;
|
||
$room->label = 'Triple Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 2;
|
||
$roomSelection->quantity = 1;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
$bookingDto->participants = [
|
||
$this->participantInRoom(2),
|
||
$this->participantInRoom(2),
|
||
];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
// 2 assigned participants × €150 = €300 total
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(2, $result[0]['roomId']);
|
||
$this->assertEquals('Triple Room', $result[0]['label']);
|
||
$this->assertEquals(1, $result[0]['quantity']);
|
||
$this->assertEquals(2, $result[0]['participantCount']);
|
||
$this->assertEquals(150.0, $result[0]['unitPrice']);
|
||
$this->assertEquals(300.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingWithMultipleRoomTypes(): void
|
||
{
|
||
$singleRoom = new Room();
|
||
$singleRoom->id = 1;
|
||
$singleRoom->price = 80.0;
|
||
$singleRoom->minPax = 1;
|
||
$singleRoom->label = 'Single Room';
|
||
|
||
$doubleRoom = new Room();
|
||
$doubleRoom->id = 2;
|
||
$doubleRoom->price = 120.0;
|
||
$doubleRoom->minPax = 2;
|
||
$doubleRoom->label = 'Double Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$singleRoom, $doubleRoom];
|
||
|
||
$singleRoomSelection = new RoomSelectionDto();
|
||
$singleRoomSelection->id = 1;
|
||
$singleRoomSelection->quantity = 1;
|
||
|
||
$doubleRoomSelection = new RoomSelectionDto();
|
||
$doubleRoomSelection->id = 2;
|
||
$doubleRoomSelection->quantity = 2;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$singleRoomSelection, $doubleRoomSelection];
|
||
$bookingDto->participants = [
|
||
$this->participantInRoom(1),
|
||
$this->participantInRoom(2),
|
||
$this->participantInRoom(2),
|
||
$this->participantInRoom(2),
|
||
];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
// Single: 1 participant × €80 = €80
|
||
// Double: 3 participants × €120 = €360
|
||
$this->assertCount(2, $result);
|
||
|
||
$this->assertEquals(1, $result[0]['roomId']);
|
||
$this->assertEquals(1, $result[0]['quantity']);
|
||
$this->assertEquals(1, $result[0]['participantCount']);
|
||
$this->assertEquals(80.0, $result[0]['unitPrice']);
|
||
$this->assertEquals(80.0, $result[0]['totalPrice']);
|
||
|
||
$this->assertEquals(2, $result[1]['roomId']);
|
||
$this->assertEquals(2, $result[1]['quantity']);
|
||
$this->assertEquals(3, $result[1]['participantCount']);
|
||
$this->assertEquals(120.0, $result[1]['unitPrice']);
|
||
$this->assertEquals(360.0, $result[1]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingSkipsRoomsWithNullPrice(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 1;
|
||
$room->price = null;
|
||
$room->minPax = 2;
|
||
$room->label = 'Free Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 1;
|
||
$roomSelection->quantity = 1;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
$this->assertEmpty($result);
|
||
}
|
||
|
||
public function testCalculateRoomPricingWithZeroQuantityRooms(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 1;
|
||
$room->price = 100.0;
|
||
$room->minPax = 2;
|
||
$room->label = 'Double Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 1;
|
||
$roomSelection->quantity = 0;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
$this->assertEmpty($result);
|
||
}
|
||
|
||
public function testCalculateRoomPricingShowsSelectedRoomWithZeroAssignments(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 1;
|
||
$room->price = 100.0;
|
||
$room->label = 'Double Room';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 1;
|
||
$roomSelection->quantity = 1;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
$bookingDto->participants = [];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(1, $result[0]['roomId']);
|
||
$this->assertEquals(1, $result[0]['quantity']);
|
||
$this->assertEquals(0, $result[0]['participantCount']);
|
||
$this->assertEquals(0.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingUsesSelectionModeForStepOneSummary(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 3;
|
||
$room->price = 229.0;
|
||
$room->minPax = 2;
|
||
$room->label = 'Doppelzimmer Dusche/WC';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 3;
|
||
$roomSelection->quantity = 1;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
$bookingDto->participants = [
|
||
$this->participantInRoom(3),
|
||
$this->participantInRoom(3),
|
||
];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto, RoomPricingCalculator::PRICING_MODE_SELECTION);
|
||
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(2, $result[0]['participantCount']);
|
||
$this->assertEquals(458.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingFallsBackToOneParticipantWhenOccupancyIsUnknown(): void
|
||
{
|
||
$room = new Room();
|
||
$room->id = 4;
|
||
$room->price = 419.0;
|
||
$room->minPax = null;
|
||
$room->label = 'Bett im Mehrbettzimmer';
|
||
|
||
$travel = new Travel();
|
||
$travel->rooms = [$room];
|
||
|
||
$roomSelection = new RoomSelectionDto();
|
||
$roomSelection->id = 4;
|
||
$roomSelection->quantity = 2;
|
||
|
||
$bookingDto = new BookingDto($travel, 1);
|
||
$bookingDto->roomSelections = [$roomSelection];
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto, RoomPricingCalculator::PRICING_MODE_SELECTION);
|
||
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(2, $result[0]['participantCount']);
|
||
$this->assertEquals(838.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
public function testCalculateRoomPricingInEditModeUsesStoredIndividualPrices(): void
|
||
{
|
||
$travel = new Travel();
|
||
$bookingDto = new BookingDto($travel, 2);
|
||
|
||
$participantA = new ParticipantDto();
|
||
$participantA->assignedRoomId = 10;
|
||
$participantB = new ParticipantDto();
|
||
$participantB->assignedRoomId = 10;
|
||
$bookingDto->participants = [$participantA, $participantB];
|
||
|
||
$booking = new Booking();
|
||
$room = new Room();
|
||
$room->id = 10;
|
||
$room->label = 'Stored Room';
|
||
$room->mapping = [0, 1];
|
||
$room->individualPrice = [0 => 200.0, 1 => 220.0];
|
||
$room->totalCount = 1;
|
||
$booking->rooms = [$room];
|
||
$bookingDto->booking = $booking;
|
||
|
||
$result = $this->calculator->calculateRoomPricing($bookingDto);
|
||
|
||
$this->assertCount(1, $result);
|
||
$this->assertEquals(10, $result[0]['roomId']);
|
||
$this->assertEquals(2, $result[0]['participantCount']);
|
||
$this->assertEquals(210.0, $result[0]['unitPrice']);
|
||
$this->assertEquals(420.0, $result[0]['totalPrice']);
|
||
}
|
||
|
||
private function participantInRoom(int $roomId): ParticipantDto
|
||
{
|
||
$participant = new ParticipantDto();
|
||
$participant->assignedRoomId = $roomId;
|
||
|
||
return $participant;
|
||
}
|
||
}
|