service = new RoomAssigner(); } #[Test] public function shouldAutoAssignRoomsReturnsTrueForSingleRoomType(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 1, 'quantity' => 3], ]); $result = $this->service->shouldAutoAssignRooms($bookingDto); self::assertTrue($result, 'Should auto-assign when exactly one room type is selected'); } #[Test] public function shouldAutoAssignRoomsReturnsFalseForMultipleRoomTypes(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 1, 'quantity' => 2], ['roomId' => 2, 'quantity' => 1], ]); $result = $this->service->shouldAutoAssignRooms($bookingDto); self::assertFalse($result, 'Should not auto-assign when multiple room types are selected'); } #[Test] public function shouldAutoAssignRoomsReturnsFalseForNoRoomSelections(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([]); $result = $this->service->shouldAutoAssignRooms($bookingDto); self::assertFalse($result, 'Should not auto-assign when no rooms are selected'); } #[Test] public function shouldAutoAssignRoomsIgnoresZeroQuantityRooms(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 1, 'quantity' => 2], ['roomId' => 2, 'quantity' => 0], ['roomId' => 3, 'quantity' => null], ]); $result = $this->service->shouldAutoAssignRooms($bookingDto); self::assertTrue($result, 'Should treat zero/null quantity as not selected'); } #[Test] public function assignParticipantsToRoomsWorksWithSingleRoomType(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 2, 'capacity' => 2], ]); // Create 4 participants (2 rooms × 2 capacity) $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), ]; // Mock available room $room = new Room(); $room->id = 100; $room->minPax = 2; $room->available = 10; $room->status = Constants::STATUS_AVAILABLE; $bookingDto->travel->rooms = [$room]; $this->service->assignParticipantsToRooms($bookingDto); // Verify all participants got assigned self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); self::assertSame(100, $bookingDto->participants[2]->assignedRoomId); self::assertSame(100, $bookingDto->participants[3]->assignedRoomId); } #[Test] public function assignParticipantsToRoomsSkipsAssignmentWithMultipleRoomTypes(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 1, 'capacity' => 2], ['roomId' => 200, 'quantity' => 1, 'capacity' => 2], ]); // Create 4 participants (2 rooms × 2 capacity) $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), ]; // Mock available rooms $room1 = new Room(); $room1->id = 100; $room1->minPax = 2; $room1->available = 10; $room1->status = Constants::STATUS_AVAILABLE; $room2 = new Room(); $room2->id = 200; $room2->minPax = 2; $room2->available = 10; $room2->status = Constants::STATUS_AVAILABLE; $bookingDto->travel->rooms = [$room1, $room2]; $this->service->assignParticipantsToRooms($bookingDto); // Verify NO participants got assigned (conditional logic skipped assignment) self::assertNull($bookingDto->participants[0]->assignedRoomId); self::assertNull($bookingDto->participants[1]->assignedRoomId); self::assertNull($bookingDto->participants[2]->assignedRoomId); self::assertNull($bookingDto->participants[3]->assignedRoomId); } #[Test] public function assignParticipantsToRoomsHandlesMixedRoomCapacities(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 1, 'capacity' => 2], ]); // Create participants $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), ]; // Mock available room with capacity 2 $room = new Room(); $room->id = 100; $room->minPax = 2; $room->available = 10; $room->status = Constants::STATUS_AVAILABLE; $bookingDto->travel->rooms = [$room]; $this->service->assignParticipantsToRooms($bookingDto); // Both participants assigned to same room self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); } #[Test] public function validateAndResetInvalidAssignmentsResetsOrphanedAssignments(): void { // Create DTO with only room 100 selected $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 2, 'capacity' => 2], ]); // Create participants with assignments to room 100 and room 200 (no longer selected) $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), new ParticipantDto(), ]; $bookingDto->participants[0]->assignedRoomId = 100; $bookingDto->participants[1]->assignedRoomId = 100; $bookingDto->participants[2]->assignedRoomId = 200; // Invalid - room not selected $bookingDto->participants[3]->assignedRoomId = 200; // Invalid - room not selected $result = $this->service->validateAndResetInvalidAssignments($bookingDto); self::assertTrue($result, 'Should return true when assignments were reset'); self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); self::assertNull($bookingDto->participants[2]->assignedRoomId, 'Invalid assignment should be reset to null'); self::assertNull($bookingDto->participants[3]->assignedRoomId, 'Invalid assignment should be reset to null'); } #[Test] public function validateAndResetInvalidAssignmentsReturnsFalseWhenAllValid(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 2, 'capacity' => 2], ]); // All participants assigned to the valid room $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), ]; $bookingDto->participants[0]->assignedRoomId = 100; $bookingDto->participants[1]->assignedRoomId = 100; $result = $this->service->validateAndResetInvalidAssignments($bookingDto); self::assertFalse($result, 'Should return false when no assignments needed reset'); self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); } #[Test] public function validateAndResetInvalidAssignmentsHandlesNullAssignments(): void { $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 1, 'capacity' => 2], ]); // Participants with null assignments should not trigger a reset $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), ]; $bookingDto->participants[0]->assignedRoomId = null; $bookingDto->participants[1]->assignedRoomId = null; $result = $this->service->validateAndResetInvalidAssignments($bookingDto); self::assertFalse($result, 'Should return false when only null assignments exist'); } #[Test] public function validateAndResetInvalidAssignmentsHandlesEmptySelectedRooms(): void { // All rooms have quantity 0 (none selected) $bookingDto = $this->createBookingDtoWithRoomSelections([ ['roomId' => 100, 'quantity' => 0, 'capacity' => 2], ['roomId' => 200, 'quantity' => 0, 'capacity' => 2], ]); // Participants still have old assignments $bookingDto->participants = [ new ParticipantDto(), new ParticipantDto(), ]; $bookingDto->participants[0]->assignedRoomId = 100; $bookingDto->participants[1]->assignedRoomId = 200; $result = $this->service->validateAndResetInvalidAssignments($bookingDto); self::assertTrue($result, 'Should return true when all assignments become invalid'); self::assertNull($bookingDto->participants[0]->assignedRoomId); self::assertNull($bookingDto->participants[1]->assignedRoomId); } private function createBookingDtoWithRoomSelections(array $selections): BookingDto { $travel = new Travel(); $travel->rooms = []; $bookingDto = new BookingDto($travel, 1); $bookingDto->roomSelections = []; foreach ($selections as $selection) { $roomSelectionDto = new RoomSelectionDto(); $roomSelectionDto->id = $selection['roomId']; $roomSelectionDto->quantity = $selection['quantity']; if (isset($selection['capacity'])) { $roomSelectionDto->capacity = $selection['capacity']; } $bookingDto->roomSelections[] = $roomSelectionDto; } return $bookingDto; } }