diff --git a/src/Normalizer/AbsoluteDateNormalizer.php b/src/Normalizer/AbsoluteDateNormalizer.php index aaaa021..a5147b1 100644 --- a/src/Normalizer/AbsoluteDateNormalizer.php +++ b/src/Normalizer/AbsoluteDateNormalizer.php @@ -6,7 +6,7 @@ use AssoConnect\PHPDate\AbsoluteDate; use Symfony\Component\Serializer\Exception\InvalidArgumentException; -use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Throwable; @@ -52,16 +52,29 @@ public function supportsNormalization(mixed $data, ?string $format = null, array */ public function denormalize($data, string $type, ?string $format = null, array $context = []): AbsoluteDate { + $dateTimeFormat = $context[self::FORMAT_KEY] ?? AbsoluteDate::DEFAULT_DATE_FORMAT; + if ('' === $data || null === $data) { - throw new UnexpectedValueException(); + throw NotNormalizableValueException::createForUnexpectedDataType( + sprintf('The data is empty, a date in format "%s" is expected.', $dateTimeFormat), + $data, + [AbsoluteDate::class], + $context['deserialization_path'] ?? null, + true, + ); } - $dateTimeFormat = $context[self::FORMAT_KEY] ?? AbsoluteDate::DEFAULT_DATE_FORMAT; - try { return new AbsoluteDate($data, $dateTimeFormat); } catch (Throwable $e) { - throw new UnexpectedValueException(previous: $e); + throw NotNormalizableValueException::createForUnexpectedDataType( + sprintf('The data is not a valid date in format "%s".', $dateTimeFormat), + $data, + [AbsoluteDate::class], + $context['deserialization_path'] ?? null, + true, + previous: $e, + ); } } diff --git a/tests/Normalizer/AbsoluteDateNormalizerTest.php b/tests/Normalizer/AbsoluteDateNormalizerTest.php index d445e8e..8a0987d 100644 --- a/tests/Normalizer/AbsoluteDateNormalizerTest.php +++ b/tests/Normalizer/AbsoluteDateNormalizerTest.php @@ -6,9 +6,10 @@ use AssoConnect\PHPDate\AbsoluteDate; use AssoConnect\PHPDateBundle\Normalizer\AbsoluteDateNormalizer; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Exception\InvalidArgumentException; -use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; class AbsoluteDateNormalizerTest extends TestCase { @@ -70,15 +71,47 @@ public function testDenormalizeUsingFormatPassedInContext(): void ); } - public function testDenormalizeInvalidDataThrowsException(): void + /** + * @return iterable + */ + public static function provideNotNormalizableData(): iterable { - $this->expectException(UnexpectedValueException::class); - $this->normalizer->denormalize('invalid date', AbsoluteDate::class); + yield 'null' => [null, 'The data is empty, a date in format "Y-m-d" is expected.']; + yield 'empty string' => ['', 'The data is empty, a date in format "Y-m-d" is expected.']; + yield 'invalid date' => ['invalid date', 'The data is not a valid date in format "Y-m-d".']; + } + + #[DataProvider('provideNotNormalizableData')] + public function testDenormalizeNotNormalizableDataThrowsException(mixed $data, string $expectedMessage): void + { + try { + $this->normalizer->denormalize($data, AbsoluteDate::class, null, [ + 'deserialization_path' => 'startDate', + ]); + self::fail('A NotNormalizableValueException was expected.'); + } catch (NotNormalizableValueException $exception) { + self::assertSame($expectedMessage, $exception->getMessage()); + self::assertSame([AbsoluteDate::class], $exception->getExpectedTypes()); + self::assertSame('startDate', $exception->getPath()); + self::assertTrue($exception->canUseMessageForUser()); + } + } + + public function testDenormalizeInvalidDataChainsThePreviousException(): void + { + try { + $this->normalizer->denormalize('invalid date', AbsoluteDate::class); + self::fail('A NotNormalizableValueException was expected.'); + } catch (NotNormalizableValueException $exception) { + self::assertNotNull($exception->getPrevious()); + self::assertNull($exception->getPath()); + } } public function testDenormalizeFormatMismatchThrowsException(): void { - $this->expectException(UnexpectedValueException::class); + $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('The data is not a valid date in format "Y-m-d|".'); $this->normalizer->denormalize('2016/01/01', AbsoluteDate::class, null, [ AbsoluteDateNormalizer::FORMAT_KEY => 'Y-m-d|', ]);