diff --git a/src/Validator/Constraints/ParticipantValidator.php b/src/Validator/Constraints/ParticipantValidator.php index 2b7084b..c70585d 100644 --- a/src/Validator/Constraints/ParticipantValidator.php +++ b/src/Validator/Constraints/ParticipantValidator.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Validator\Constraints; use App\Form\Model\ParticipantDto; +use App\Validator\SelectionBasedBodyDimensionExemption; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -23,12 +24,17 @@ class ParticipantValidator extends ConstraintValidator /** @var array */ private array $bodyDimensionRanges; + private readonly SelectionBasedBodyDimensionExemption $bodyDimensionExemption; + /** * @param array $bodyDimensionRanges */ - public function __construct(array $bodyDimensionRanges = []) - { + public function __construct( + array $bodyDimensionRanges = [], + ?SelectionBasedBodyDimensionExemption $bodyDimensionExemption = null, + ) { $this->bodyDimensionRanges = $this->resolveBodyDimensionRanges($bodyDimensionRanges); + $this->bodyDimensionExemption = $bodyDimensionExemption ?? new SelectionBasedBodyDimensionExemption(); } public function validate(mixed $value, Constraint $constraint): void @@ -38,7 +44,10 @@ class ParticipantValidator extends ConstraintValidator $this->assertPickupSelected($participant); $this->assertDropOffSelected($participant); - if (true === $this->hasRentalsSelected($participant)) { + if ( + true === $this->hasRentalsSelected($participant) + && false === $this->bodyDimensionExemption->isBodyDimensionValidationExempt($this->context->getRoot()) + ) { $this->assertBodyDimensionsInRange($participant); } } diff --git a/src/Validator/SelectionBasedBodyDimensionExemption.php b/src/Validator/SelectionBasedBodyDimensionExemption.php new file mode 100644 index 0000000..15285f7 --- /dev/null +++ b/src/Validator/SelectionBasedBodyDimensionExemption.php @@ -0,0 +1,21 @@ +bookingContext->travel->hasSelectionId(self::SELECTION_ID); + } +} diff --git a/tests/Validator/Constraints/ParticipantValidatorTest.php b/tests/Validator/Constraints/ParticipantValidatorTest.php index 963e1e0..8c85452 100644 --- a/tests/Validator/Constraints/ParticipantValidatorTest.php +++ b/tests/Validator/Constraints/ParticipantValidatorTest.php @@ -4,18 +4,27 @@ declare(strict_types=1); namespace App\Tests\Validator\Constraints; +use App\BusProNet\Model\CrmSelection; +use App\BusProNet\Model\CrmSelectionGroup; use App\BusProNet\Model\Pickup; use App\BusProNet\Model\Service; +use App\BusProNet\Model\Travel; +use App\Form\Model\BookingDto; use App\Form\Model\ParticipantDto; +use App\Form\Model\ParticipantEditDto; use App\Validator\Constraints\Participant; use App\Validator\Constraints\ParticipantValidator; +use App\Validator\SelectionBasedBodyDimensionExemption; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; class ParticipantValidatorTest extends ConstraintValidatorTestCase { protected function createValidator(): ParticipantValidator { - return new ParticipantValidator(); + return new ParticipantValidator( + bodyDimensionRanges: [], + bodyDimensionExemption: new SelectionBasedBodyDimensionExemption() + ); } public function testParticipantWithRentalsAndBodyMeasurementsPassesValidation(): void @@ -111,6 +120,46 @@ class ParticipantValidatorTest extends ConstraintValidatorTestCase ->assertRaised(); } + public function testParticipantWithOutOfRangeBodyDimensionsFailsValidationWithoutSelection1474(): void + { + $wrapper = $this->createWrapperWithParticipantAndTravelSelections( + $this->createValidParticipantWithOutOfRangeBodyDimensions(), + [2001] + ); + + $this->setRoot($wrapper); + $this->validator->validate($wrapper->participant, new Participant()); + + $this->assertCount(3, $this->context->getViolations()); + } + + public function testParticipantWithOutOfRangeBodyDimensionsPassesValidationInCreateModeWithSelection1474(): void + { + $wrapper = $this->createWrapperWithParticipantAndTravelSelections( + $this->createValidParticipantWithOutOfRangeBodyDimensions(), + [1474] + ); + + $this->setRoot($wrapper); + $this->validator->validate($wrapper->participant, new Participant()); + + $this->assertNoViolation(); + } + + public function testParticipantWithOutOfRangeBodyDimensionsPassesValidationInEditModeWithSelection1474(): void + { + $wrapper = $this->createWrapperWithParticipantAndTravelSelections( + $this->createValidParticipantWithOutOfRangeBodyDimensions(), + [1474], + true + ); + + $this->setRoot($wrapper); + $this->validator->validate($wrapper->participant, new Participant()); + + $this->assertNoViolation(); + } + public function testCanceledParticipantSkipsValidation(): void { $participant = $this->createValidParticipant(); @@ -226,4 +275,61 @@ class ParticipantValidatorTest extends ConstraintValidatorTestCase return $pickup; } + + /** + * @param list $selectionIds + */ + private function createWrapperWithParticipantAndTravelSelections( + ParticipantDto $participant, + array $selectionIds, + bool $editMode = false, + ): ParticipantEditDto { + $travel = new Travel(); + $travel->dateFrom = new \DateTimeImmutable('2030-01-01'); + $travel->dateTo = new \DateTimeImmutable('2030-01-08'); + $travel->selectionGroups = [ + 1 => $this->createSelectionGroup(1, $selectionIds), + ]; + + $bookingDto = new BookingDto($travel, 1); + $bookingDto->participants = [$participant]; + + if (true === $editMode) { + $bookingDto->booking = new \App\BusProNet\Model\Booking(); + } + + return new ParticipantEditDto( + participant: $participant, + bookingContext: $bookingDto, + ); + } + + private function createValidParticipantWithOutOfRangeBodyDimensions(): ParticipantDto + { + $participant = $this->createValidParticipant(); + $participant->rentals = [$this->createMockService()]; + $participant->height = '999'; + $participant->weight = '999'; + $participant->shoeSize = '999'; + + return $participant; + } + + /** + * @param list $selectionIds + */ + private function createSelectionGroup(int $groupId, array $selectionIds): CrmSelectionGroup + { + $group = new CrmSelectionGroup(); + $group->id = $groupId; + $group->selections = []; + + foreach ($selectionIds as $selectionId) { + $selection = new CrmSelection(); + $selection->id = $selectionId; + $group->selections[] = $selection; + } + + return $group; + } } diff --git a/tests/Validator/SelectionBasedBodyDimensionExemptionTest.php b/tests/Validator/SelectionBasedBodyDimensionExemptionTest.php new file mode 100644 index 0000000..640d1b3 --- /dev/null +++ b/tests/Validator/SelectionBasedBodyDimensionExemptionTest.php @@ -0,0 +1,77 @@ +exemption = new SelectionBasedBodyDimensionExemption(); + } + + public function testReturnsTrueWhenWrapperTravelContainsSelection1474(): void + { + $root = $this->createWrapperWithSelectionIds([1474]); + + $this->assertTrue($this->exemption->isBodyDimensionValidationExempt($root)); + } + + public function testReturnsFalseWhenSelection1474IsAbsent(): void + { + $root = $this->createWrapperWithSelectionIds([2001]); + + $this->assertFalse($this->exemption->isBodyDimensionValidationExempt($root)); + } + + public function testReturnsFalseWhenRootIsNotParticipantEditDto(): void + { + $this->assertFalse($this->exemption->isBodyDimensionValidationExempt(new \stdClass())); + } + + private function createWrapperWithSelectionIds(array $selectionIds): ParticipantEditDto + { + $travel = new Travel(); + $travel->selectionGroups = [ + 1 => $this->createSelectionGroup(1, $selectionIds), + ]; + + $bookingDto = new BookingDto($travel, 1); + $participant = new ParticipantDto(); + + return new ParticipantEditDto( + participant: $participant, + bookingContext: $bookingDto, + ); + } + + /** + * @param list $selectionIds + */ + private function createSelectionGroup(int $groupId, array $selectionIds): CrmSelectionGroup + { + $group = new CrmSelectionGroup(); + $group->id = $groupId; + $group->selections = []; + + foreach ($selectionIds as $selectionId) { + $selection = new CrmSelection(); + $selection->id = $selectionId; + $group->selections[] = $selection; + } + + return $group; + } +}