Files
myep/tests/Form/Service/ParticipantParkingFieldHandlerTest.php

358 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Form\Service;
use App\BusProNet\Constants;
use App\BusProNet\Model\Service;
use App\BusProNet\Model\Travel;
use App\BusProNet\Utility\DirectionMapper;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\ParticipantParkingFieldHandler;
use App\Service\ServiceAvailabilityCalculator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class ParticipantParkingFieldHandlerTest extends TestCase
{
private ParticipantParkingFieldHandler $handler;
protected function setUp(): void
{
$this->handler = new ParticipantParkingFieldHandler(new ServiceAvailabilityCalculator());
}
public function testGetFieldName(): void
{
$this->assertSame('parking', $this->handler->getFieldName());
}
public function testGetDependencies(): void
{
$this->assertSame(['transportationOutbound', 'transportationInbound', 'dateOfBirth'], $this->handler->getDependencies());
}
public function testShouldProcessAlwaysReturnsTrue(): void
{
$this->assertTrue($this->handler->shouldProcess([], BookingDto::MODE_CREATE, 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], BookingDto::MODE_EDIT, 5));
}
#[DataProvider('normalizeCheckboxValueCheckedProvider')]
public function testNormalizeCheckboxValueReturnsTrueWhenChecked(mixed $value): void
{
$this->assertTrue($this->invokeNormalizeCheckboxValue($value));
}
#[DataProvider('normalizeCheckboxValueUncheckedProvider')]
public function testNormalizeCheckboxValueReturnsFalseWhenUnchecked(mixed $value): void
{
$this->assertFalse($this->invokeNormalizeCheckboxValue($value));
}
public function testProcessFieldWithoutParticipant(): void
{
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->expectNotToPerformAssertions();
}
public function testProcessFieldClearsParkingWhenTransportationIsNotPkw(): void
{
$busService = $this->createTransportationService(DirectionMapper::SUBTYPE_BUS_API);
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-25 years');
$participant->transportationOutbound = $busService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
public function testProcessFieldClearsParkingWhenParticipantIsUnder18(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// 17-year-old participant
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-17 years');
$participant->transportationOutbound = $pkwService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
public function testProcessFieldClearsParkingWhenParticipantIsExactly17(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// Participant who is exactly 17 (just before 18th birthday)
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('2007-06-15');
$participant->transportationOutbound = $pkwService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
// Assume today is 2024-06-14 (one day before 17th birthday would make them 16)
// But since we use current date, we need to be careful with this test
// Let's use a fixed approach - participant born 17 years ago today is exactly 17
$participant->dateOfBirth = (new \DateTimeImmutable())->modify('-17 years');
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
}
public function testProcessFieldAllowsParkingForAdultWithPkw(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// 25-year-old participant
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-25 years');
$participant->transportationOutbound = $pkwService;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => '1'];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertTrue($participant->parking);
}
public function testProcessFieldAllowsParkingForExactly18YearOld(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// Exactly 18 years old
$participant = new ParticipantDto();
$participant->dateOfBirth = (new \DateTimeImmutable())->modify('-18 years');
$participant->transportationOutbound = $pkwService;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertTrue($participant->parking);
}
public function testProcessFieldClearsParkingWhenNoDateOfBirth(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
$participant = new ParticipantDto();
$participant->dateOfBirth = null; // No date of birth
$participant->transportationOutbound = $pkwService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
public function testProcessFieldClearsParkingWhenBabyParticipant(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// Baby participant (1 year old)
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-1 year');
$participant->transportationOutbound = $pkwService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
public function testProcessFieldClearsParkingForChildParticipant(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
// 10-year-old child
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-10 years');
$participant->transportationOutbound = $pkwService;
$participant->parking = true;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
}
public function testProcessFieldRejectsParkingWhenSoldOut(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
$parkingService = $this->createParkingService(available: 0);
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-25 years');
$participant->transportationOutbound = $pkwService;
$travel = new Travel();
$travel->additionalServices = [$parkingService->id => $parkingService];
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
public function testProcessFieldAllowsParkingWhenAvailable(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
$parkingService = $this->createParkingService(available: 5);
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-25 years');
$participant->transportationOutbound = $pkwService;
$travel = new Travel();
$travel->additionalServices = [$parkingService->id => $parkingService];
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => true];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertTrue($participant->parking);
$this->assertSame($parkingService, $participant->parkingService);
}
public function testProcessFieldSetsParkingToFalseWhenNotSelected(): void
{
$pkwService = $this->createTransportationService(DirectionMapper::SUBTYPE_CAR_API);
$participant = new ParticipantDto();
$participant->dateOfBirth = new \DateTimeImmutable('-25 years');
$participant->transportationOutbound = $pkwService;
$travel = new Travel();
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['parking' => false];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertFalse($participant->parking);
$this->assertNull($participant->parkingService);
}
/**
* @return array<string, array{mixed}>
*/
public static function normalizeCheckboxValueCheckedProvider(): array
{
return [
'true' => [true],
'int 1' => [1],
'string 1' => ['1'],
];
}
/**
* @return array<string, array{mixed}>
*/
public static function normalizeCheckboxValueUncheckedProvider(): array
{
return [
'false' => [false],
'int 0' => [0],
'string 0' => ['0'],
'empty string' => [''],
'null' => [null],
'non-numeric string' => ['off'],
];
}
private function invokeNormalizeCheckboxValue(mixed $value): bool
{
$reflection = new \ReflectionMethod($this->handler, 'normalizeCheckboxValue');
return $reflection->invoke($this->handler, $value);
}
private function createTransportationService(string $subType): Service
{
$service = new Service();
$service->id = 100;
$service->label = 'Test Transportation';
$service->subType = $subType;
return $service;
}
private function createParkingService(int $available): Service
{
$service = new Service();
$service->id = 200;
$service->label = 'Parkplatz';
$service->subType = Constants::TOKEN_PARKING;
$service->available = $available;
return $service;
}
}