125 lines
4.9 KiB
PHP
125 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Integration\Shared\Helpers;
|
|
|
|
use Carbon\CarbonImmutable;
|
|
use Shared\Helpers\DatesHelper;
|
|
use Shared\Helpers\Exceptions\InvalidDateFormatException;
|
|
|
|
covers(DatesHelper::class);
|
|
|
|
afterEach(function (): void {
|
|
CarbonImmutable::setTestNow();
|
|
});
|
|
|
|
describe('Date Helper Integration Tests', function (): void {
|
|
it('prevents direct instantiation but covers constructor', function (): void {
|
|
// GIVEN
|
|
$reflection = new \ReflectionClass(DatesHelper::class);
|
|
|
|
// WHEN
|
|
$constructor = $reflection->getConstructor();
|
|
$constructor->invoke($reflection->newInstanceWithoutConstructor());
|
|
|
|
// THEN
|
|
expect(fn () => $reflection->newInstance())
|
|
->toThrow(\ReflectionException::class);
|
|
});
|
|
|
|
it('can parse various date formats and normalizes to UTC', function (string $input, string $expectedFormat): void {
|
|
// GIVEN & WHEN
|
|
$result = DatesHelper::parseDateTime($input);
|
|
|
|
// THEN
|
|
expect($result)->toBeInstanceOf(\DateTimeInterface::class)
|
|
->and($result->format('Y-m-d'))->toBe($expectedFormat)
|
|
->and($result->getTimezone()->getName())->toBe('UTC');
|
|
})->with([
|
|
['2025-12-25 12:10:23', '2025-12-25'],
|
|
['2025-12-25', '2025-12-25'],
|
|
['25-12-2025', '2025-12-25'],
|
|
['2025-12-25T12:10:23Z', '2025-12-25'],
|
|
['2025-12-25T12:10:23+02:00', '2025-12-25'],
|
|
['2025-12-25T12:10:23.123456+02:00', '2025-12-25'],
|
|
['2025/12/25', '2025-12-25'],
|
|
]);
|
|
|
|
it('converts non-UTC input timezone to exact UTC instant', function (): void {
|
|
// GIVEN
|
|
$inputWithOffset = '2025-12-25T14:10:23+02:00';
|
|
|
|
// WHEN
|
|
$result = DatesHelper::parseDateTime($inputWithOffset);
|
|
|
|
// THEN
|
|
expect($result->getTimezone()->getName())->toBe('UTC')
|
|
->and($result->format('Y-m-d H:i:s'))->toBe('2025-12-25 12:10:23');
|
|
});
|
|
|
|
it('throws InvalidDateFormatException for unparseable date strings', function (): void {
|
|
// GIVEN
|
|
$invalidDate = 'not-a-real-date-string';
|
|
|
|
// WHEN & THEN
|
|
expect(fn () => DatesHelper::parseDateTime($invalidDate))
|
|
->toThrow(InvalidDateFormatException::class, 'Unrecognized date/time format: not-a-real-date-string');
|
|
});
|
|
|
|
it('falls back to DateTimeImmutable for valid formats not fully handled by createFromFormat', function (): void {
|
|
// GIVEN
|
|
$fallbackDate = '2025-12-25T12:10:23.1234567Z'; // 7 digit microsecond matches regex but fails createFromFormat 'u' (6 digits)
|
|
|
|
// WHEN
|
|
$result = DatesHelper::parseDateTime($fallbackDate);
|
|
|
|
// THEN
|
|
expect($result)->toBeInstanceOf(\DateTimeInterface::class)
|
|
->and($result->format('Y-m-d'))->toBe('2025-12-25')
|
|
->and($result->getTimezone()->getName())->toBe('UTC');
|
|
});
|
|
|
|
it('throws InvalidDateFormatException in parseFallback when fallback date is completely invalid logically', function (): void {
|
|
// GIVEN
|
|
$invalidFallbackDate = '2025-12-25T25:10:23.1234567Z'; // Invalid hour 25, matches regex but fails both createFromFormat and new DateTimeImmutable
|
|
|
|
// WHEN & THEN
|
|
expect(fn () => DatesHelper::parseDateTime($invalidFallbackDate))
|
|
->toThrow(InvalidDateFormatException::class, "Unrecognized date/time format: {$invalidFallbackDate}");
|
|
});
|
|
|
|
it('handles isIn date boundary checks correctly in UTC', function (): void {
|
|
// GIVEN
|
|
$now = CarbonImmutable::create(2025, 6, 15, 12, 0, 0, 'UTC');
|
|
CarbonImmutable::setTestNow($now);
|
|
|
|
$past = CarbonImmutable::create(2025, 1, 1, 0, 0, 0, 'UTC');
|
|
$future = CarbonImmutable::create(2025, 12, 31, 0, 0, 0, 'UTC');
|
|
$farFuture = CarbonImmutable::create(2025, 12, 30, 0, 0, 0, 'UTC');
|
|
|
|
// WHEN & THEN
|
|
expect(DatesHelper::isIn($past, $future))->toBeTrue()
|
|
->and(DatesHelper::isIn($farFuture, $future))->toBeFalse()
|
|
->and(DatesHelper::isIn($past, null))->toBeTrue()
|
|
->and(DatesHelper::isIn($future, null))->toBeFalse()
|
|
->and(DatesHelper::isIn(null, $future))->toBeTrue()
|
|
->and(DatesHelper::isIn(null, $past))->toBeFalse()
|
|
->and(DatesHelper::isIn(null, null))->toBeFalse();
|
|
});
|
|
|
|
it('formats string or DateTimeInterface to RFC3339 in UTC', function (): void {
|
|
// GIVEN
|
|
$dateTimeObj = new \DateTimeImmutable('2025-12-25 12:10:23', new \DateTimeZone('Europe/Paris'));
|
|
|
|
// WHEN
|
|
$resultFromObj = DatesHelper::toRfc3339($dateTimeObj);
|
|
$resultFromStr = DatesHelper::toRfc3339('2025-12-25 12:10:23');
|
|
|
|
// THEN
|
|
expect($resultFromObj)->toBe('2025-12-25T11:10:23+00:00')
|
|
->and($resultFromStr)->toBe('2025-12-25T12:10:23+00:00')
|
|
->and(DatesHelper::toNullableRfc3339(null))->toBeNull()
|
|
->and(DatesHelper::toNullableRfc3339($dateTimeObj))->toBe('2025-12-25T11:10:23+00:00');
|
|
});
|
|
});
|