validator()->validate($query)); self::assertSame('2026-01-01', $query->dateFromDate()->format('Y-m-d')); } public function testCalendarQueryAcceptsAnOmittedRange(): void { $query = new ContingentCalendarQuery('HOTEL_1'); self::assertCount(0, $this->validator()->validate($query)); // Null is what puts the endpoint into full-span mode; there is no separate predicate for it. self::assertNull($query->dateFromDate()); self::assertNull($query->dateToDate()); } #[DataProvider('invalidCalendarQueries')] public function testCalendarQueryRejectsInvalidInput(ContingentCalendarQuery $query): void { self::assertGreaterThan(0, $this->validator()->validate($query)->count()); } public static function invalidCalendarQueries(): iterable { yield 'normalized invalid date' => [new ContingentCalendarQuery('HOTEL', '2026-02-31', '2026-03-01')]; yield 'reversed range' => [new ContingentCalendarQuery('HOTEL', '2026-03-02', '2026-03-01')]; yield 'range over 366 days' => [new ContingentCalendarQuery('HOTEL', '2026-01-01', '2027-01-03')]; yield 'invalid hotel code' => [new ContingentCalendarQuery('HOTEL CODE', '2026-01-01', '2026-01-02')]; yield 'dateFrom without dateTo' => [new ContingentCalendarQuery('HOTEL', '2026-01-01')]; yield 'dateTo without dateFrom' => [new ContingentCalendarQuery('HOTEL', null, '2026-01-02')]; } public function testPricesQueryRequiresFourDigitYear(): void { self::assertCount(0, $this->validator()->validate(new ContingentPricesQuery('HOTEL', 2026))); self::assertGreaterThan(0, $this->validator()->validate(new ContingentPricesQuery('HOTEL', 26))->count()); } private function validator(): \Symfony\Component\Validator\Validator\ValidatorInterface { return Validation::createValidatorBuilder() ->enableAttributeMapping() ->getValidator(); } }