342 lines
13 KiB
PHP
342 lines
13 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Form\Service;
|
|
|
|
use App\BusProNet\Model\Travel;
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Model\ParticipantDto;
|
|
use App\Form\Model\RoomSelectionDto;
|
|
use App\Form\Service\ParticipantAssignedRoomFieldHandler;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class ParticipantAssignedRoomFieldHandlerTest extends TestCase
|
|
{
|
|
private ParticipantAssignedRoomFieldHandler $handler;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->handler = new ParticipantAssignedRoomFieldHandler();
|
|
}
|
|
|
|
public function testGetFieldName(): void
|
|
{
|
|
$this->assertSame('assignedRoomId', $this->handler->getFieldName());
|
|
}
|
|
|
|
public function testGetDependencies(): void
|
|
{
|
|
$this->assertSame([], $this->handler->getDependencies());
|
|
}
|
|
|
|
public function testShouldProcessReturnsTrueWhenFieldPresent(): void
|
|
{
|
|
$this->assertTrue($this->handler->shouldProcess(['assignedRoomId' => '1'], BookingDto::MODE_CREATE, 0));
|
|
$this->assertTrue($this->handler->shouldProcess(['assignedRoomId' => ''], BookingDto::MODE_EDIT, 5));
|
|
}
|
|
|
|
public function testShouldProcessReturnsFalseWhenFieldNotPresent(): void
|
|
{
|
|
$this->assertFalse($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
|
|
$this->assertFalse($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
|
|
}
|
|
|
|
public function testProcessFieldWithoutParticipant(): void
|
|
{
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
// Should handle gracefully when participant doesn't exist
|
|
$this->expectNotToPerformAssertions();
|
|
}
|
|
|
|
public function testProcessFieldAssignsRoomWithAvailableCapacity(): void
|
|
{
|
|
$participant = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 1)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
$this->assertSame(1, $participant->assignedRoomId);
|
|
}
|
|
|
|
public function testProcessFieldUnassignsParticipantWhenRoomIdIsEmpty(): void
|
|
{
|
|
$participant = new ParticipantDto();
|
|
$participant->assignedRoomId = 1;
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$submittedData = ['assignedRoomId' => ''];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
$this->assertNull($participant->assignedRoomId);
|
|
}
|
|
|
|
public function testProcessFieldUnassignsParticipantWhenRoomIdIsNull(): void
|
|
{
|
|
$participant = new ParticipantDto();
|
|
$participant->assignedRoomId = 1;
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
|
|
$submittedData = ['assignedRoomId' => null];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
$this->assertNull($participant->assignedRoomId);
|
|
}
|
|
|
|
public function testProcessFieldUnassignsHighestIndexParticipantWhenRoomAtCapacity(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->assignedRoomId = null;
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2];
|
|
// 1x "Doppelzimmer" (capacity 2) - room is at capacity with participants 0 and 1
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 1)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
// Participant 2 tries to select room 1
|
|
$this->handler->processField($submittedData, $bookingDto, 2);
|
|
|
|
// Participant 1 (highest index with room 1) should be unassigned
|
|
$this->assertSame(1, $participant0->assignedRoomId); // Still assigned (applicant protection)
|
|
$this->assertNull($participant1->assignedRoomId); // Unassigned
|
|
$this->assertSame(1, $participant2->assignedRoomId); // Newly assigned
|
|
}
|
|
|
|
public function testProcessFieldGeneratesWarningNotificationForUnassignedParticipant(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2];
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 1)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 2);
|
|
|
|
// Participant 1 should have a warning notification
|
|
$notifications = $participant1->notifications;
|
|
$this->assertCount(1, $notifications);
|
|
// Get first notification (notifications are keyed by hash, not numeric index)
|
|
$firstNotification = reset($notifications);
|
|
$this->assertSame('warning', $firstNotification['type']);
|
|
$this->assertStringContainsString('Doppelzimmer', $firstNotification['message']);
|
|
$this->assertStringContainsString('wurde von', $firstNotification['message']);
|
|
$this->assertStringContainsString('entfernt', $firstNotification['message']);
|
|
}
|
|
|
|
public function testProcessFieldDoesNotGenerateNotificationForAssignedParticipant(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2];
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 1)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 2);
|
|
|
|
// Participant 2 (the one being assigned) should NOT have a notification (user-initiated action)
|
|
$this->assertEmpty($participant2->notifications);
|
|
}
|
|
|
|
public function testProcessFieldHandlesRoomChangeWithoutConflict(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1];
|
|
// 2x "Doppelzimmer" (capacity 2 each = 4 total beds)
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 2)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
// Participant 0 changes room (still room 1, but should not trigger conflict)
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
// Both should remain assigned (participant 0's own slot was freed, so no conflict)
|
|
$this->assertSame(1, $participant0->assignedRoomId);
|
|
$this->assertSame(1, $participant1->assignedRoomId);
|
|
$this->assertEmpty($participant1->notifications);
|
|
}
|
|
|
|
public function testProcessFieldUnassignsMultipleParticipantsWhenNeeded(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->assignedRoomId = 1;
|
|
|
|
$participant3 = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2, $participant3];
|
|
// 1x "Einzelzimmer" (capacity 1) - only 1 bed available
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Einzelzimmer', 1, 1)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
// Participant 3 tries to select room 1
|
|
$this->handler->processField($submittedData, $bookingDto, 3);
|
|
|
|
// All three participants with room 1 should be unassigned (highest indices first: 2, 1, 0)
|
|
// But we only need to unassign enough to make space (3 participants, only 1 space needed)
|
|
$this->assertNull($participant2->assignedRoomId); // Highest index, unassigned first
|
|
$this->assertNull($participant1->assignedRoomId); // Second highest, unassigned second
|
|
$this->assertNull($participant0->assignedRoomId); // Third highest, unassigned third
|
|
$this->assertSame(1, $participant3->assignedRoomId); // Newly assigned
|
|
}
|
|
|
|
public function testProcessFieldHandlesNoConflictWithMultipleRoomQuantities(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1;
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1;
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->assignedRoomId = 1;
|
|
|
|
$participant3 = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2, $participant3];
|
|
// 2x "Doppelzimmer" (capacity 2 each = 4 total beds)
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 2)];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
// Participant 3 tries to select room 1
|
|
$this->handler->processField($submittedData, $bookingDto, 3);
|
|
|
|
// All should remain assigned (4 beds available, 4 participants)
|
|
$this->assertSame(1, $participant0->assignedRoomId);
|
|
$this->assertSame(1, $participant1->assignedRoomId);
|
|
$this->assertSame(1, $participant2->assignedRoomId);
|
|
$this->assertSame(1, $participant3->assignedRoomId);
|
|
$this->assertEmpty($participant0->notifications);
|
|
$this->assertEmpty($participant1->notifications);
|
|
$this->assertEmpty($participant2->notifications);
|
|
}
|
|
|
|
public function testProcessFieldHandlesComplexScenarioWithMultipleRoomTypes(): void
|
|
{
|
|
$participant0 = new ParticipantDto();
|
|
$participant0->assignedRoomId = 1; // Doppelzimmer
|
|
|
|
$participant1 = new ParticipantDto();
|
|
$participant1->assignedRoomId = 1; // Doppelzimmer
|
|
|
|
$participant2 = new ParticipantDto();
|
|
$participant2->assignedRoomId = 2; // Einzelzimmer
|
|
|
|
$participant3 = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant0, $participant1, $participant2, $participant3];
|
|
$bookingDto->roomSelections = [
|
|
$this->createRoomSelection(1, 'Doppelzimmer', 2, 1), // 1x Doppelzimmer (capacity 2)
|
|
$this->createRoomSelection(2, 'Einzelzimmer', 1, 1), // 1x Einzelzimmer (capacity 1)
|
|
];
|
|
|
|
$submittedData = ['assignedRoomId' => '1'];
|
|
|
|
// Participant 3 tries to select Doppelzimmer (already at capacity with 0 and 1)
|
|
$this->handler->processField($submittedData, $bookingDto, 3);
|
|
|
|
// Participant 1 (highest index with room 1) should be unassigned
|
|
$this->assertSame(1, $participant0->assignedRoomId);
|
|
$this->assertNull($participant1->assignedRoomId); // Unassigned
|
|
$this->assertSame(2, $participant2->assignedRoomId); // Still in Einzelzimmer
|
|
$this->assertSame(1, $participant3->assignedRoomId); // Newly assigned to Doppelzimmer
|
|
}
|
|
|
|
public function testProcessFieldHandlesEdgeCaseWhenRoomNotFoundInSelections(): void
|
|
{
|
|
$participant = new ParticipantDto();
|
|
|
|
$travel = new Travel();
|
|
$bookingDto = new BookingDto($travel, 1);
|
|
$bookingDto->participants = [$participant];
|
|
$bookingDto->roomSelections = [$this->createRoomSelection(1, 'Doppelzimmer', 2, 1)];
|
|
|
|
// Try to assign room 999 (not in selections)
|
|
$submittedData = ['assignedRoomId' => '999'];
|
|
|
|
$this->handler->processField($submittedData, $bookingDto, 0);
|
|
|
|
// Should still assign (no conflict detection possible, but assignment proceeds)
|
|
$this->assertSame(999, $participant->assignedRoomId);
|
|
}
|
|
|
|
/**
|
|
* Helper method to create a RoomSelectionDto for testing.
|
|
*/
|
|
private function createRoomSelection(int $roomId, string $label, int $capacity, int $quantity): RoomSelectionDto
|
|
{
|
|
$selection = new RoomSelectionDto();
|
|
$selection->roomId = $roomId;
|
|
$selection->roomLabel = $label;
|
|
$selection->capacity = $capacity;
|
|
$selection->quantity = $quantity;
|
|
|
|
return $selection;
|
|
}
|
|
}
|