608 lines
20 KiB
PHP
608 lines
20 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\Surcharge;
|
|
use App\BusProNet\Model\Travel;
|
|
use App\BusProNet\XmlLoader\AgencyLoader;
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Model\ParticipantDto;
|
|
use App\Form\Model\ParticipantEditDto;
|
|
use App\Service\BookingPriceCalculator;
|
|
use App\Service\ParticipantCardAssembler;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\Validator\ConstraintViolationInterface;
|
|
use Symfony\Component\Validator\ConstraintViolationList;
|
|
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|
|
|
class ParticipantCardAssemblerTest extends TestCase
|
|
{
|
|
private ?ParticipantCardAssembler $service = null;
|
|
private BookingPriceCalculator $priceCalculator;
|
|
private ValidatorInterface $validator;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->priceCalculator = $this->createStub(BookingPriceCalculator::class);
|
|
$this->validator = $this->createStub(ValidatorInterface::class);
|
|
}
|
|
|
|
private function service(): ParticipantCardAssembler
|
|
{
|
|
return $this->service ??= new ParticipantCardAssembler(
|
|
$this->priceCalculator,
|
|
$this->validator
|
|
);
|
|
}
|
|
|
|
public function testGetCardDataWithFullParticipantData(): void
|
|
{
|
|
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
|
|
|
|
// Create test room
|
|
$room = new Room();
|
|
$room->id = 1;
|
|
$room->label = 'Doppelzimmer';
|
|
$room->price = 100.0;
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room];
|
|
|
|
// Create participant with full data
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = 1;
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
// Mock price calculation
|
|
$this->priceCalculator
|
|
->expects($this->once())
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->with($bookingDto)
|
|
->willReturn([450.50]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Max Mustermann', $result->name);
|
|
$this->assertSame('Doppelzimmer', $result->roomName);
|
|
$this->assertSame(450.50, $result->price->amount);
|
|
$this->assertFalse($result->price->showDash);
|
|
}
|
|
|
|
public function testGetCardDataWithPartialName(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
// Participant with only first name
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = null;
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Max', $result->name);
|
|
}
|
|
|
|
public function testGetCardDataWithNoName(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
// Participant without name
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = null;
|
|
$participant->lastName = null;
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Anmelder:in', $result->name);
|
|
}
|
|
|
|
public function testGetCardDataWithEmptyName(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
// Participant with empty strings for names
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = ' ';
|
|
$participant->lastName = ' ';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Anmelder:in', $result->name);
|
|
}
|
|
|
|
public function testGetCardDataWithNoRoomAssignment(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = null;
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Kein Zimmer zugewiesen', $result->roomName);
|
|
}
|
|
|
|
public function testGetCardDataWithUnknownRoom(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = 999; // Non-existent room
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame('Unbekanntes Zimmer', $result->roomName);
|
|
}
|
|
|
|
public function testGetCardDataWithZeroPriceAndNoRoom(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = null; // No room assigned
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
// When no room assigned and price is zero, display dash (incomplete configuration)
|
|
$this->assertNull($result->price->amount);
|
|
$this->assertTrue($result->price->showDash);
|
|
}
|
|
|
|
public function testGetCardDataWithZeroPriceButRoomAssigned(): void
|
|
{
|
|
// Create test room
|
|
$room = new Room();
|
|
$room->id = 1;
|
|
$room->label = 'Doppelzimmer';
|
|
$room->price = 0.0;
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = 1; // Room is assigned
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
// When room is assigned but price is zero, display formatted zero price
|
|
$this->assertSame(0.0, $result->price->amount);
|
|
$this->assertFalse($result->price->showDash);
|
|
}
|
|
|
|
public function testGetCardDataWithInvalidIndex(): void
|
|
{
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [];
|
|
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
$this->expectExceptionMessage('Participant at index 0 does not exist');
|
|
|
|
$this->service()->getCardData($bookingDto, 0);
|
|
}
|
|
|
|
public function testGetAllCardsDataWithMultipleParticipants(): void
|
|
{
|
|
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
|
|
|
|
// Create test rooms
|
|
$room1 = new Room();
|
|
$room1->id = 1;
|
|
$room1->label = 'Einzelzimmer';
|
|
|
|
$room2 = new Room();
|
|
$room2->id = 2;
|
|
$room2->label = 'Doppelzimmer';
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room1, $room2];
|
|
|
|
// Create participants
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->firstName = 'Max';
|
|
$participant1->lastName = 'Mustermann';
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->firstName = 'Anna';
|
|
$participant2->lastName = 'Schmidt';
|
|
$participant2->assignedRoomId = 2;
|
|
|
|
$participant3 = new ParticipantDto();
|
|
$participant3->firstName = null;
|
|
$participant3->lastName = null;
|
|
$participant3->assignedRoomId = 2;
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
|
|
|
// Mock price calculation
|
|
$this->priceCalculator
|
|
->expects($this->once())
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->with($bookingDto)
|
|
->willReturn([450.0, 500.0, 480.0]);
|
|
|
|
$result = $this->service()->getAllCardsData($bookingDto);
|
|
|
|
$this->assertCount(3, $result);
|
|
|
|
// First participant
|
|
$this->assertSame('Max Mustermann', $result[0]->name);
|
|
$this->assertSame('Einzelzimmer', $result[0]->roomName);
|
|
$this->assertSame(450.0, $result[0]->price->amount);
|
|
$this->assertFalse($result[0]->price->showDash);
|
|
|
|
// Second participant
|
|
$this->assertSame('Anna Schmidt', $result[1]->name);
|
|
$this->assertSame('Doppelzimmer', $result[1]->roomName);
|
|
$this->assertSame(500.0, $result[1]->price->amount);
|
|
$this->assertFalse($result[1]->price->showDash);
|
|
|
|
// Third participant (no name)
|
|
$this->assertSame('Teilnehmer:in', $result[2]->name);
|
|
$this->assertSame('Doppelzimmer', $result[2]->roomName);
|
|
$this->assertSame(480.0, $result[2]->price->amount);
|
|
$this->assertFalse($result[2]->price->showDash);
|
|
}
|
|
|
|
public function testGetAllCardsDataUsesCanceledSurchargesBeforePrecomputedActivePrices(): void
|
|
{
|
|
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
|
|
|
|
$room = new Room();
|
|
$room->id = 1;
|
|
$room->label = 'Doppelzimmer';
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room];
|
|
|
|
$activeParticipant = new ParticipantDto();
|
|
$activeParticipant->firstName = 'Max';
|
|
$activeParticipant->lastName = 'Mustermann';
|
|
$activeParticipant->assignedRoomId = 1;
|
|
|
|
$canceledParticipant = new ParticipantDto();
|
|
$canceledParticipant->firstName = 'Anna';
|
|
$canceledParticipant->lastName = 'Schmidt';
|
|
$canceledParticipant->assignedRoomId = 1;
|
|
|
|
$surcharge = new Surcharge();
|
|
$surcharge->mapping = [1];
|
|
$surcharge->individualPrice = [1 => 75.0];
|
|
|
|
$booking = new Booking();
|
|
$booking->participantsStatus = [0 => 'A', 1 => 'S'];
|
|
$booking->surcharges = [$surcharge];
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->booking = $booking;
|
|
$bookingDto->participants = [$activeParticipant, $canceledParticipant];
|
|
|
|
$this->priceCalculator
|
|
->expects($this->once())
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->with($bookingDto)
|
|
->willReturn([0 => 450.0, 1 => 999.0]);
|
|
|
|
$result = $this->service()->getAllCardsData($bookingDto);
|
|
|
|
$this->assertSame(450.0, $result[0]->price->amount);
|
|
$this->assertSame(75.0, $result[1]->price->amount);
|
|
$this->assertFalse($result[1]->price->showDash);
|
|
}
|
|
|
|
public function testGetAllCardsDataWithEmptyParticipants(): void
|
|
{
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [];
|
|
|
|
$result = $this->service()->getAllCardsData($bookingDto);
|
|
|
|
$this->assertEmpty($result);
|
|
}
|
|
|
|
public function testPriceFormattingWithLargeAmount(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([1234.56]);
|
|
|
|
$result = $this->service()->getCardData($bookingDto, 0);
|
|
|
|
$this->assertSame(1234.56, $result->price->amount);
|
|
$this->assertFalse($result->price->showDash);
|
|
}
|
|
|
|
public function testFallbackNameIndexingIsOneBasedNotZeroBased(): void
|
|
{
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant2 = new ParticipantDto();
|
|
$participant3 = new ParticipantDto();
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0, 0.0, 0.0]);
|
|
|
|
$result1 = $this->service()->getCardData($bookingDto, 0);
|
|
$result2 = $this->service()->getCardData($bookingDto, 1);
|
|
$result3 = $this->service()->getCardData($bookingDto, 2);
|
|
|
|
$this->assertSame('Anmelder:in', $result1->name);
|
|
$this->assertSame('Teilnehmer:in', $result2->name);
|
|
$this->assertSame('Teilnehmer:in', $result3->name);
|
|
}
|
|
|
|
public function testGetCardDataWithValidationReturnsValidCard(): void
|
|
{
|
|
$this->validator = $this->createMock(ValidatorInterface::class);
|
|
|
|
$room = new Room();
|
|
$room->id = 1;
|
|
$room->label = 'Doppelzimmer';
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->assignedRoomId = 1;
|
|
$participant->email = '[email protected]';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([450.50]);
|
|
|
|
// Mock validator to return no violations (valid)
|
|
$violations = $this->createMock(ConstraintViolationListInterface::class);
|
|
$violations->expects($this->once())
|
|
->method('count')
|
|
->willReturn(0);
|
|
|
|
$this->validator
|
|
->expects($this->once())
|
|
->method('validate')
|
|
->willReturn($violations);
|
|
|
|
$result = $this->service()->getCardDataWithValidation($bookingDto, 0);
|
|
|
|
$this->assertSame('Max Mustermann', $result->name);
|
|
$this->assertSame('Doppelzimmer', $result->roomName);
|
|
$this->assertSame(450.50, $result->price->amount);
|
|
$this->assertFalse($result->price->showDash);
|
|
$this->assertTrue($result->isValid);
|
|
$this->assertEmpty($result->errorMessages);
|
|
}
|
|
|
|
public function testGetCardDataWithValidationReturnsInvalidCard(): void
|
|
{
|
|
$this->validator = $this->createMock(ValidatorInterface::class);
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->firstName = 'Max';
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->email = '[email protected]';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
// Mock validator to return violations (invalid)
|
|
$violation = $this->createMock(ConstraintViolationInterface::class);
|
|
$violation->expects($this->once())
|
|
->method('getMessage')
|
|
->willReturn('Diese E-Mail Adresse wird bereits von einem anderen Teilnehmer verwendet');
|
|
|
|
// Use ConstraintViolationList directly instead of mocking
|
|
$violations = new ConstraintViolationList([$violation]);
|
|
|
|
$this->validator
|
|
->expects($this->once())
|
|
->method('validate')
|
|
->willReturn($violations);
|
|
|
|
$result = $this->service()->getCardDataWithValidation($bookingDto, 0);
|
|
|
|
$this->assertSame('Max Mustermann', $result->name);
|
|
$this->assertFalse($result->isValid);
|
|
$this->assertCount(1, $result->errorMessages);
|
|
$this->assertSame('Diese E-Mail Adresse wird bereits von einem anderen Teilnehmer verwendet', $result->errorMessages[0]);
|
|
}
|
|
|
|
public function testGetAllCardsDataWithValidationReturnsAllCards(): void
|
|
{
|
|
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
|
|
$this->validator = $this->createMock(ValidatorInterface::class);
|
|
|
|
$room = new Room();
|
|
$room->id = 1;
|
|
$room->label = 'Doppelzimmer';
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [$room];
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->firstName = 'Max';
|
|
$participant1->lastName = 'Mustermann';
|
|
$participant1->assignedRoomId = 1;
|
|
$participant1->email = '[email protected]';
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->firstName = 'Anna';
|
|
$participant2->lastName = 'Schmidt';
|
|
$participant2->assignedRoomId = 1;
|
|
$participant2->email = '[email protected]';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant1, $participant2];
|
|
|
|
$this->priceCalculator
|
|
->expects($this->once())
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([450.0, 500.0]);
|
|
|
|
// Mock validator to return no violations for both participants
|
|
$violations = $this->createMock(ConstraintViolationListInterface::class);
|
|
$violations->expects($this->exactly(2))
|
|
->method('count')
|
|
->willReturn(0);
|
|
|
|
$this->validator
|
|
->expects($this->exactly(2))
|
|
->method('validate')
|
|
->willReturn($violations);
|
|
|
|
$result = $this->service()->getAllCardsDataWithValidation($bookingDto);
|
|
|
|
$this->assertCount(2, $result);
|
|
$this->assertTrue($result[0]->isValid);
|
|
$this->assertTrue($result[1]->isValid);
|
|
$this->assertSame('Max Mustermann', $result[0]->name);
|
|
$this->assertSame('Anna Schmidt', $result[1]->name);
|
|
}
|
|
|
|
public function testGetCardDataWithValidationCanForceStrictRequiredInEditMode(): void
|
|
{
|
|
$this->validator = $this->createMock(ValidatorInterface::class);
|
|
|
|
$travel = new Travel();
|
|
$travel->rooms = [];
|
|
|
|
$participant = new ParticipantDto();
|
|
$participant->index = 0;
|
|
$participant->firstName = null;
|
|
$participant->lastName = 'Mustermann';
|
|
$participant->email = '[email protected]';
|
|
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->agencyCode = AgencyLoader::INTERNAL_AGENCY_CODE;
|
|
$bookingDto->booking = new Booking();
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$this->priceCalculator
|
|
->method('calculateAllParticipantIndividualPrices')
|
|
->willReturn([0.0]);
|
|
|
|
$violation = $this->createMock(ConstraintViolationInterface::class);
|
|
$violation->expects($this->once())
|
|
->method('getMessage')
|
|
->willReturn('Bitte angeben');
|
|
|
|
$violations = new ConstraintViolationList([$violation]);
|
|
|
|
$this->validator
|
|
->expects($this->once())
|
|
->method('validate')
|
|
->with(
|
|
$this->isInstanceOf(ParticipantEditDto::class),
|
|
null,
|
|
['booking_edit', 'strict_required']
|
|
)
|
|
->willReturn($violations);
|
|
|
|
$result = $this->service()->getCardDataWithValidation($bookingDto, 0, true);
|
|
|
|
$this->assertFalse($result->isValid);
|
|
$this->assertSame(['Bitte angeben'], $result->errorMessages);
|
|
}
|
|
}
|