Files
myep/tests/Service/AccommodationBookingLinkSignerTest.php
T

120 lines
4.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Service;
use App\Entity\Groups\AccommodationBooking;
use App\Service\AccommodationBookingLinkSigner;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class AccommodationBookingLinkSignerTest extends TestCase
{
public function testSignThenIsValidLinkRequestSucceeds(): void
{
$booking = $this->bookingWithAccessLink();
$signer = $this->createSigner();
$signedUrl = $signer->sign($booking);
$request = Request::create($signedUrl);
self::assertTrue($signer->isValidLinkRequest($request, $booking));
}
public function testTamperedQueryParamFails(): void
{
$booking = $this->bookingWithAccessLink();
$signer = $this->createSigner();
$signedUrl = $signer->sign($booking);
self::assertTrue($signer->isValidLinkRequest(Request::create($signedUrl), $booking));
// Flip the `t` value while keeping the original _hash — signature no longer matches.
$tamperedUrl = preg_replace('/(?<=[?&]t=)\d+/', '999999999', $signedUrl);
self::assertNotNull($tamperedUrl);
self::assertFalse($signer->isValidLinkRequest(Request::create($tamperedUrl), $booking));
}
public function testRegeneratedLinkInvalidatesThePreviousOne(): void
{
$booking = $this->bookingWithAccessLink();
$signer = $this->createSigner();
$signedUrl = $signer->sign($booking);
$request = Request::create($signedUrl);
// Regenerating overwrites accessLinkIssuedAt — the old signed `t` no longer matches.
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable('+1 minute'));
self::assertFalse($signer->isValidLinkRequest($request, $booking));
}
public function testExpiredLinkFails(): void
{
$booking = new AccommodationBooking();
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable('-91 days'));
$signer = $this->createSigner();
$signedUrl = $signer->sign($booking);
$request = Request::create($signedUrl);
self::assertFalse($signer->isValidLinkRequest($request, $booking));
}
public function testMissingAccessLinkIssuedAtFails(): void
{
$booking = new AccommodationBooking();
$signer = $this->createSigner();
$request = Request::create('https://example.com/groups/booking/offer/'.$booking->getUuid().'?t=123');
self::assertFalse($signer->isValidLinkRequest($request, $booking));
}
public function testSignThrowsWithoutAccessLinkIssuedAt(): void
{
$booking = new AccommodationBooking();
$signer = $this->createSigner();
$this->expectException(\LogicException::class);
$signer->sign($booking);
}
public function testExpiresAtIsNinetyDaysAfterIssuedAt(): void
{
$issuedAt = new \DateTimeImmutable('2026-01-01T00:00:00+00:00');
$booking = new AccommodationBooking();
$booking->setAccessLinkIssuedAt($issuedAt);
$signer = $this->createSigner();
self::assertSame('2026-04-01T00:00:00+00:00', $signer->expiresAt($booking)?->format(\DATE_ATOM));
}
private function bookingWithAccessLink(): AccommodationBooking
{
$booking = new AccommodationBooking();
$booking->setPaxCount(10);
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
return $booking;
}
private function createSigner(): AccommodationBookingLinkSigner
{
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
$urlGenerator
->method('generate')
->willReturnCallback(static fn (string $name, array $parameters) => sprintf(
'https://example.com/groups/booking/offer/%s?t=%s',
$parameters['uuid'],
$parameters['t'],
));
return new AccommodationBookingLinkSigner($urlGenerator, 'test-secret');
}
}