diff --git a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php index 59c6b72f..b17ca369 100644 --- a/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/LocateVisitsCommandTest.php @@ -107,7 +107,7 @@ class LocateVisitsCommandTest extends TestCase #[Test, DataProvider('provideIgnoredAddresses')] public function localhostAndEmptyAddressesAreIgnored(IpCannotBeLocatedException $e, string $message): void { - $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()); $location = VisitLocation::fromGeolocation(Location::emptyInstance()); $this->lock->method('acquire')->with($this->isFalse())->willReturn(true); diff --git a/module/Core/src/Visit/Model/Visitor.php b/module/Core/src/Visit/Model/Visitor.php index c914f334..493280ef 100644 --- a/module/Core/src/Visit/Model/Visitor.php +++ b/module/Core/src/Visit/Model/Visitor.php @@ -51,7 +51,7 @@ final class Visitor ); } - public static function emptyInstance(): self + public static function empty(): self { return new self('', '', null, ''); } diff --git a/module/Core/src/Visit/RequestTracker.php b/module/Core/src/Visit/RequestTracker.php index 02fbd94e..cb36eee2 100644 --- a/module/Core/src/Visit/RequestTracker.php +++ b/module/Core/src/Visit/RequestTracker.php @@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\InvalidIpFormatException; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Util\IpAddressUtils; +use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use function Shlinkio\Shlink\Core\ipAddressFromRequest; @@ -22,24 +23,26 @@ readonly class RequestTracker implements RequestTrackerInterface, RequestMethodI { } - public function trackIfApplicable(ShortUrl $shortUrl, ServerRequestInterface $request): void - { - if ($this->shouldTrackRequest($request)) { - $this->visitsTracker->track($shortUrl, Visitor::fromRequest($request)); - } - } - - public function trackNotFoundIfApplicable(ServerRequestInterface $request): void + public function trackIfApplicable(ShortUrl $shortUrl, ServerRequestInterface $request): Visit|null { if (! $this->shouldTrackRequest($request)) { - return; + return null; + } + + return $this->visitsTracker->track($shortUrl, Visitor::fromRequest($request)); + } + + public function trackNotFoundIfApplicable(ServerRequestInterface $request): Visit|null + { + if (! $this->shouldTrackRequest($request)) { + return null; } /** @var NotFoundType|null $notFoundType */ $notFoundType = $request->getAttribute(NotFoundType::class); $visitor = Visitor::fromRequest($request); - match (true) { + return match (true) { $notFoundType?->isBaseUrl() => $this->visitsTracker->trackBaseUrlVisit($visitor), $notFoundType?->isRegularNotFound() => $this->visitsTracker->trackRegularNotFoundVisit($visitor), $notFoundType?->isInvalidShortUrl() => $this->visitsTracker->trackInvalidShortUrlVisit($visitor), diff --git a/module/Core/src/Visit/RequestTrackerInterface.php b/module/Core/src/Visit/RequestTrackerInterface.php index 9048b07f..4fb159b0 100644 --- a/module/Core/src/Visit/RequestTrackerInterface.php +++ b/module/Core/src/Visit/RequestTrackerInterface.php @@ -6,10 +6,11 @@ namespace Shlinkio\Shlink\Core\Visit; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; +use Shlinkio\Shlink\Core\Visit\Entity\Visit; interface RequestTrackerInterface { - public function trackIfApplicable(ShortUrl $shortUrl, ServerRequestInterface $request): void; + public function trackIfApplicable(ShortUrl $shortUrl, ServerRequestInterface $request): Visit|null; - public function trackNotFoundIfApplicable(ServerRequestInterface $request): void; + public function trackNotFoundIfApplicable(ServerRequestInterface $request): Visit|null; } diff --git a/module/Core/src/Visit/VisitsTracker.php b/module/Core/src/Visit/VisitsTracker.php index 85085220..1d33bbd8 100644 --- a/module/Core/src/Visit/VisitsTracker.php +++ b/module/Core/src/Visit/VisitsTracker.php @@ -21,65 +21,63 @@ readonly class VisitsTracker implements VisitsTrackerInterface ) { } - public function track(ShortUrl $shortUrl, Visitor $visitor): void + public function track(ShortUrl $shortUrl, Visitor $visitor): Visit|null { - $this->trackVisit( + return $this->trackVisit( fn (Visitor $v) => Visit::forValidShortUrl($shortUrl, $v, $this->options->anonymizeRemoteAddr), $visitor, ); } - public function trackInvalidShortUrlVisit(Visitor $visitor): void + public function trackInvalidShortUrlVisit(Visitor $visitor): Visit|null { - $this->trackOrphanVisit( + return $this->trackOrphanVisit( fn (Visitor $v) => Visit::forInvalidShortUrl($v, $this->options->anonymizeRemoteAddr), $visitor, ); } - public function trackBaseUrlVisit(Visitor $visitor): void + public function trackBaseUrlVisit(Visitor $visitor): Visit|null { - $this->trackOrphanVisit( + return $this->trackOrphanVisit( fn (Visitor $v) => Visit::forBasePath($v, $this->options->anonymizeRemoteAddr), $visitor, ); } - public function trackRegularNotFoundVisit(Visitor $visitor): void + public function trackRegularNotFoundVisit(Visitor $visitor): Visit|null { - $this->trackOrphanVisit( + return $this->trackOrphanVisit( fn (Visitor $v) => Visit::forRegularNotFound($v, $this->options->anonymizeRemoteAddr), $visitor, ); } - private function trackOrphanVisit(callable $createVisit, Visitor $visitor): void + private function trackOrphanVisit(callable $createVisit, Visitor $visitor): Visit|null { if (! $this->options->trackOrphanVisits) { - return; + return null; } - $this->trackVisit($createVisit, $visitor); + return $this->trackVisit($createVisit, $visitor); } /** * @param callable(Visitor $visitor): Visit $createVisit */ - private function trackVisit(callable $createVisit, Visitor $visitor): void + private function trackVisit(callable $createVisit, Visitor $visitor): Visit|null { if ($this->options->disableTracking) { - return; + return null; } $visit = $createVisit($visitor->normalizeForTrackingOptions($this->options)); - // Wrap persisting and flushing the visit in a transaction, so that the ShortUrlVisitsCountTracker performs - // changes inside that very same transaction atomically - $this->em->wrapInTransaction(function () use ($visit): void { - $this->em->persist($visit); - $this->em->flush(); - }); - + // Wrap persisting the visit in a transaction, so that the ShortUrlVisitsCountTracker performs changes inside + // that very same transaction atomically + $this->em->wrapInTransaction(fn () => $this->em->persist($visit)); $this->eventDispatcher->dispatch(new UrlVisited($visit->getId(), $visitor->remoteAddress)); + + return $visit; } } diff --git a/module/Core/src/Visit/VisitsTrackerInterface.php b/module/Core/src/Visit/VisitsTrackerInterface.php index dc650326..da2eae84 100644 --- a/module/Core/src/Visit/VisitsTrackerInterface.php +++ b/module/Core/src/Visit/VisitsTrackerInterface.php @@ -5,15 +5,16 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Visit; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; +use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; interface VisitsTrackerInterface { - public function track(ShortUrl $shortUrl, Visitor $visitor): void; + public function track(ShortUrl $shortUrl, Visitor $visitor): Visit|null; - public function trackInvalidShortUrlVisit(Visitor $visitor): void; + public function trackInvalidShortUrlVisit(Visitor $visitor): Visit|null; - public function trackBaseUrlVisit(Visitor $visitor): void; + public function trackBaseUrlVisit(Visitor $visitor): Visit|null; - public function trackRegularNotFoundVisit(Visitor $visitor): void; + public function trackRegularNotFoundVisit(Visitor $visitor): Visit|null; } diff --git a/module/Core/test-db/ShortUrl/Repository/DeleteExpiredShortUrlsRepositoryTest.php b/module/Core/test-db/ShortUrl/Repository/DeleteExpiredShortUrlsRepositoryTest.php index 1751aac9..2e10d935 100644 --- a/module/Core/test-db/ShortUrl/Repository/DeleteExpiredShortUrlsRepositoryTest.php +++ b/module/Core/test-db/ShortUrl/Repository/DeleteExpiredShortUrlsRepositoryTest.php @@ -92,7 +92,7 @@ class DeleteExpiredShortUrlsRepositoryTest extends DatabaseTestCase $this->getEntityManager()->persist($shortUrl); for ($j = 0; $j < $visitsPerShortUrl; $j++) { - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::empty())); } } } diff --git a/module/Core/test-db/ShortUrl/Repository/ShortUrlListRepositoryTest.php b/module/Core/test-db/ShortUrl/Repository/ShortUrlListRepositoryTest.php index 26d2dff5..435c3e58 100644 --- a/module/Core/test-db/ShortUrl/Repository/ShortUrlListRepositoryTest.php +++ b/module/Core/test-db/ShortUrl/Repository/ShortUrlListRepositoryTest.php @@ -74,7 +74,7 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase $foo2 = ShortUrl::withLongUrl('https://foo_2'); $visits2 = array_map(function () use ($foo2) { - $visit = Visit::forValidShortUrl($foo2, Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl($foo2, Visitor::empty()); $this->getEntityManager()->persist($visit); return $visit; @@ -304,9 +304,9 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase 'maxVisits' => 3, ]), $this->relationResolver); $this->getEntityManager()->persist($shortUrl4); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::empty())); $this->getEntityManager()->flush(); diff --git a/module/Core/test-db/Tag/Repository/TagRepositoryTest.php b/module/Core/test-db/Tag/Repository/TagRepositoryTest.php index 34210dbe..224e0c11 100644 --- a/module/Core/test-db/Tag/Repository/TagRepositoryTest.php +++ b/module/Core/test-db/Tag/Repository/TagRepositoryTest.php @@ -79,13 +79,13 @@ class TagRepositoryTest extends DatabaseTestCase $shortUrl = ShortUrl::create($metaWithTags($firstUrlTags, $apiKey), $this->relationResolver); $this->getEntityManager()->persist($shortUrl); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::empty())); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::botInstance())); $shortUrl2 = ShortUrl::create($metaWithTags($secondUrlTags, null), $this->relationResolver); $this->getEntityManager()->persist($shortUrl2); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::empty())); // One of the tags has two extra short URLs, but with no visits $this->getEntityManager()->persist( diff --git a/module/Core/test-db/Visit/Listener/OrphanVisitsCountTrackerTest.php b/module/Core/test-db/Visit/Listener/OrphanVisitsCountTrackerTest.php index ad8edcd2..34eeb9f9 100644 --- a/module/Core/test-db/Visit/Listener/OrphanVisitsCountTrackerTest.php +++ b/module/Core/test-db/Visit/Listener/OrphanVisitsCountTrackerTest.php @@ -26,7 +26,7 @@ class OrphanVisitsCountTrackerTest extends DatabaseTestCase #[Test] public function createsNewEntriesWhenNoneExist(): void { - $visit = Visit::forBasePath(Visitor::emptyInstance()); + $visit = Visit::forBasePath(Visitor::empty()); $this->getEntityManager()->persist($visit); $this->getEntityManager()->flush(); @@ -47,7 +47,7 @@ class OrphanVisitsCountTrackerTest extends DatabaseTestCase } $this->getEntityManager()->flush(); - $visit = Visit::forRegularNotFound(Visitor::emptyInstance()); + $visit = Visit::forRegularNotFound(Visitor::empty()); $this->getEntityManager()->persist($visit); $this->getEntityManager()->flush(); diff --git a/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php b/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php index 7a2a6c29..7a4c4d18 100644 --- a/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php +++ b/module/Core/test-db/Visit/Listener/ShortUrlVisitsCountTrackerTest.php @@ -30,7 +30,7 @@ class ShortUrlVisitsCountTrackerTest extends DatabaseTestCase $shortUrl = ShortUrl::createFake(); $this->getEntityManager()->persist($shortUrl); - $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl($shortUrl, Visitor::empty()); $this->getEntityManager()->persist($visit); $this->getEntityManager()->flush(); @@ -54,7 +54,7 @@ class ShortUrlVisitsCountTrackerTest extends DatabaseTestCase } $this->getEntityManager()->flush(); - $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl($shortUrl, Visitor::empty()); $this->getEntityManager()->persist($visit); $this->getEntityManager()->flush(); diff --git a/module/Core/test-db/Visit/Repository/VisitDeleterRepositoryTest.php b/module/Core/test-db/Visit/Repository/VisitDeleterRepositoryTest.php index 6529c6a9..eedd2897 100644 --- a/module/Core/test-db/Visit/Repository/VisitDeleterRepositoryTest.php +++ b/module/Core/test-db/Visit/Repository/VisitDeleterRepositoryTest.php @@ -28,8 +28,8 @@ class VisitDeleterRepositoryTest extends DatabaseTestCase { $shortUrl1 = ShortUrl::withLongUrl('https://foo.com'); $this->getEntityManager()->persist($shortUrl1); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl1, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl1, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl1, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl1, Visitor::empty())); $shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData([ ShortUrlInputFilter::LONG_URL => 'https://foo.com', @@ -37,17 +37,17 @@ class VisitDeleterRepositoryTest extends DatabaseTestCase ShortUrlInputFilter::CUSTOM_SLUG => 'foo', ]), new PersistenceShortUrlRelationResolver($this->getEntityManager())); $this->getEntityManager()->persist($shortUrl2); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::empty())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::empty())); $shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData([ ShortUrlInputFilter::LONG_URL => 'https://foo.com', ShortUrlInputFilter::CUSTOM_SLUG => 'foo', ]), new PersistenceShortUrlRelationResolver($this->getEntityManager())); $this->getEntityManager()->persist($shortUrl3); - $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl3, Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl3, Visitor::empty())); $this->getEntityManager()->flush(); @@ -62,7 +62,7 @@ class VisitDeleterRepositoryTest extends DatabaseTestCase #[Test] public function deletesExpectedOrphanVisits(): void { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); $this->getEntityManager()->persist(Visit::forBasePath($visitor)); $this->getEntityManager()->persist(Visit::forInvalidShortUrl($visitor)); $this->getEntityManager()->persist(Visit::forRegularNotFound($visitor)); diff --git a/module/Core/test-db/Visit/Repository/VisitIterationRepositoryTest.php b/module/Core/test-db/Visit/Repository/VisitIterationRepositoryTest.php index ee5843d5..60c2fbea 100644 --- a/module/Core/test-db/Visit/Repository/VisitIterationRepositoryTest.php +++ b/module/Core/test-db/Visit/Repository/VisitIterationRepositoryTest.php @@ -37,7 +37,7 @@ class VisitIterationRepositoryTest extends DatabaseTestCase $unmodifiedDate = Chronos::now(); for ($i = 0; $i < 6; $i++) { Chronos::setTestNow($unmodifiedDate->subDays($i)); // Enforce a different day for every visit - $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl($shortUrl, Visitor::empty()); if ($i >= 2) { $location = VisitLocation::fromGeolocation(Location::emptyInstance()); diff --git a/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php b/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php index 393e41da..29227b8b 100644 --- a/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php +++ b/module/Core/test-db/Visit/Repository/VisitRepositoryTest.php @@ -311,9 +311,9 @@ class VisitRepositoryTest extends DatabaseTestCase $this->getEntityManager()->persist($domainApiKey); // Visits not linked to any short URL - $this->getEntityManager()->persist(Visit::forBasePath(Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forInvalidShortUrl(Visitor::emptyInstance())); - $this->getEntityManager()->persist(Visit::forRegularNotFound(Visitor::emptyInstance())); + $this->getEntityManager()->persist(Visit::forBasePath(Visitor::empty())); + $this->getEntityManager()->persist(Visit::forInvalidShortUrl(Visitor::empty())); + $this->getEntityManager()->persist(Visit::forRegularNotFound(Visitor::empty())); $this->getEntityManager()->persist(Visit::forRegularNotFound(Visitor::botInstance())); $this->getEntityManager()->flush(); @@ -370,15 +370,15 @@ class VisitRepositoryTest extends DatabaseTestCase $botsCount = 3; for ($i = 0; $i < 6; $i++) { $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forBasePath($botsCount < 1 ? Visitor::emptyInstance() : Visitor::botInstance()), + fn () => Visit::forBasePath($botsCount < 1 ? Visitor::empty() : Visitor::botInstance()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forInvalidShortUrl(Visitor::emptyInstance()), + fn () => Visit::forInvalidShortUrl(Visitor::empty()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forRegularNotFound(Visitor::emptyInstance()), + fn () => Visit::forRegularNotFound(Visitor::empty()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); @@ -428,15 +428,15 @@ class VisitRepositoryTest extends DatabaseTestCase for ($i = 0; $i < 6; $i++) { $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forBasePath(Visitor::emptyInstance()), + fn () => Visit::forBasePath(Visitor::empty()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forInvalidShortUrl(Visitor::emptyInstance()), + fn () => Visit::forInvalidShortUrl(Visitor::empty()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); $this->getEntityManager()->persist($this->setDateOnVisit( - fn () => Visit::forRegularNotFound(Visitor::emptyInstance()), + fn () => Visit::forRegularNotFound(Visitor::empty()), Chronos::parse(sprintf('2020-01-0%s', $i + 1)), )); } @@ -515,7 +515,7 @@ class VisitRepositoryTest extends DatabaseTestCase { $this->assertNull($this->repo->findMostRecentOrphanVisit()); - $lastVisit = Visit::forBasePath(Visitor::emptyInstance()); + $lastVisit = Visit::forBasePath(Visitor::empty()); $this->getEntityManager()->persist($lastVisit); $this->getEntityManager()->flush(); @@ -567,7 +567,7 @@ class VisitRepositoryTest extends DatabaseTestCase $visit = $this->setDateOnVisit( fn () => Visit::forValidShortUrl( $shortUrl, - $botsAmount < 1 ? Visitor::emptyInstance() : Visitor::botInstance(), + $botsAmount < 1 ? Visitor::empty() : Visitor::botInstance(), ), Chronos::parse(sprintf('2016-01-%s', str_pad((string) ($i + 1), 2, '0', STR_PAD_LEFT)))->startOfDay(), ); diff --git a/module/Core/test/EventDispatcher/LocateUnlocatedVisitsTest.php b/module/Core/test/EventDispatcher/LocateUnlocatedVisitsTest.php index 0dda17b0..ef776d42 100644 --- a/module/Core/test/EventDispatcher/LocateUnlocatedVisitsTest.php +++ b/module/Core/test/EventDispatcher/LocateUnlocatedVisitsTest.php @@ -39,7 +39,7 @@ class LocateUnlocatedVisitsTest extends TestCase #[Test] public function visitToLocationHelperIsCalledToGeolocateVisits(): void { - $visit = Visit::forBasePath(Visitor::emptyInstance()); + $visit = Visit::forBasePath(Visitor::empty()); $location = Location::emptyInstance(); $this->visitToLocation->expects($this->once())->method('resolveVisitLocation')->with($visit)->willReturn( diff --git a/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php b/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php index ed0ada96..725980a1 100644 --- a/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php +++ b/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php @@ -60,7 +60,7 @@ class SendVisitToMatomoTest extends TestCase public function visitIsSentWhenItExists(string|null $originalIpAddress): void { $visitId = '123'; - $visit = Visit::forBasePath(Visitor::emptyInstance()); + $visit = Visit::forBasePath(Visitor::empty()); $this->em->expects($this->once())->method('find')->with(Visit::class, $visitId)->willReturn($visit); $this->visitSender->expects($this->once())->method('sendVisit')->with($visit, $originalIpAddress); diff --git a/module/Core/test/EventDispatcher/Mercure/NotifyVisitToMercureTest.php b/module/Core/test/EventDispatcher/Mercure/NotifyVisitToMercureTest.php index aa21411e..1e3dfb96 100644 --- a/module/Core/test/EventDispatcher/Mercure/NotifyVisitToMercureTest.php +++ b/module/Core/test/EventDispatcher/Mercure/NotifyVisitToMercureTest.php @@ -61,7 +61,7 @@ class NotifyVisitToMercureTest extends TestCase public function notificationsAreSentWhenVisitIsFound(): void { $visitId = '123'; - $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()); $update = Update::forTopicAndPayload('', []); $this->em->expects($this->once())->method('find')->with(Visit::class, $visitId)->willReturn($visit); @@ -81,7 +81,7 @@ class NotifyVisitToMercureTest extends TestCase public function debugIsLoggedWhenExceptionIsThrown(): void { $visitId = '123'; - $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()); $update = Update::forTopicAndPayload('', []); $e = new RuntimeException('Error'); @@ -122,7 +122,7 @@ class NotifyVisitToMercureTest extends TestCase public static function provideOrphanVisits(): iterable { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); yield VisitType::REGULAR_404->value => [Visit::forRegularNotFound($visitor)]; yield VisitType::INVALID_SHORT_URL->value => [Visit::forInvalidShortUrl($visitor)]; diff --git a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php index 7686f4ab..2e232038 100644 --- a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php +++ b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php @@ -48,7 +48,7 @@ class PublishingUpdatesGeneratorTest extends TestCase 'longUrl' => 'https://longUrl', 'title' => $title, ])); - $visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()); + $visit = Visit::forValidShortUrl($shortUrl, Visitor::empty()); /** @var Update $update */ $update = $this->generator->{$method}($visit); @@ -111,7 +111,7 @@ class PublishingUpdatesGeneratorTest extends TestCase public static function provideOrphanVisits(): iterable { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); yield VisitType::REGULAR_404->value => [Visit::forRegularNotFound($visitor)]; yield VisitType::INVALID_SHORT_URL->value => [Visit::forInvalidShortUrl($visitor)]; diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php index 1117d5d3..26785897 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -90,7 +90,7 @@ class NotifyVisitToRabbitMqTest extends TestCase public static function provideVisits(): iterable { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); yield 'orphan visit' => [Visit::forBasePath($visitor), ['newOrphanVisitUpdate']]; yield 'non-orphan visit' => [ @@ -110,7 +110,7 @@ class NotifyVisitToRabbitMqTest extends TestCase { $visitId = '123'; $this->em->expects($this->once())->method('find')->with(Visit::class, $visitId)->willReturn( - Visit::forBasePath(Visitor::emptyInstance()), + Visit::forBasePath(Visitor::empty()), ); $this->updatesGenerator->expects($this->once())->method('newOrphanVisitUpdate')->with( $this->isInstanceOf(Visit::class), @@ -152,7 +152,7 @@ class NotifyVisitToRabbitMqTest extends TestCase $never = static fn () => $exactly(0); yield 'non-orphan visit' => [ - Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::emptyInstance()), + Visit::forValidShortUrl(ShortUrl::withLongUrl('https://longUrl'), Visitor::empty()), function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator) use ($once, $never): void { $update = Update::forTopicAndPayload('', []); $updatesGenerator->expects($never())->method('newOrphanVisitUpdate'); @@ -166,7 +166,7 @@ class NotifyVisitToRabbitMqTest extends TestCase }, ]; yield 'orphan visit' => [ - Visit::forBasePath(Visitor::emptyInstance()), + Visit::forBasePath(Visitor::empty()), function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator) use ($once, $never): void { $update = Update::forTopicAndPayload('', []); $updatesGenerator->expects($once())->method('newOrphanVisitUpdate')->willReturn($update); diff --git a/module/Core/test/EventDispatcher/RedisPubSub/NotifyVisitToRedisTest.php b/module/Core/test/EventDispatcher/RedisPubSub/NotifyVisitToRedisTest.php index cbccffd7..20cd786a 100644 --- a/module/Core/test/EventDispatcher/RedisPubSub/NotifyVisitToRedisTest.php +++ b/module/Core/test/EventDispatcher/RedisPubSub/NotifyVisitToRedisTest.php @@ -53,7 +53,7 @@ class NotifyVisitToRedisTest extends TestCase { $visitId = '123'; $this->em->expects($this->once())->method('find')->with(Visit::class, $visitId)->willReturn( - Visit::forBasePath(Visitor::emptyInstance()), + Visit::forBasePath(Visitor::empty()), ); $this->updatesGenerator->expects($this->once())->method('newOrphanVisitUpdate')->with( $this->isInstanceOf(Visit::class), diff --git a/module/Core/test/Matomo/MatomoVisitSenderTest.php b/module/Core/test/Matomo/MatomoVisitSenderTest.php index bf568bfb..f78d0f33 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -77,9 +77,9 @@ class MatomoVisitSenderTest extends TestCase public static function provideTrackerMethods(): iterable { - yield 'unlocated orphan visit' => [Visit::forBasePath(Visitor::emptyInstance()), null, []]; + yield 'unlocated orphan visit' => [Visit::forBasePath(Visitor::empty()), null, []]; yield 'located regular visit' => [ - Visit::forValidShortUrl(ShortUrl::withLongUrl('https://shlink.io'), Visitor::emptyInstance()) + Visit::forValidShortUrl(ShortUrl::withLongUrl('https://shlink.io'), Visitor::empty()) ->locate(VisitLocation::fromGeolocation(new Location( countryCode: 'countryCode', countryName: 'countryName', @@ -115,7 +115,7 @@ class MatomoVisitSenderTest extends TestCase public static function provideUrlsToTrack(): iterable { - yield 'orphan visit without visited URL' => [Visit::forBasePath(Visitor::emptyInstance()), '']; + yield 'orphan visit without visited URL' => [Visit::forBasePath(Visitor::empty()), '']; yield 'orphan visit with visited URL' => [ Visit::forBasePath(new Visitor('', '', null, 'https://s.test/foo')), 'https://s.test/foo', @@ -126,7 +126,7 @@ class MatomoVisitSenderTest extends TestCase ShortUrlInputFilter::LONG_URL => 'https://shlink.io', ShortUrlInputFilter::CUSTOM_SLUG => 'bar', ]), - ), Visitor::emptyInstance()), + ), Visitor::empty()), 'http://s2.test/bar', ]; } @@ -135,7 +135,7 @@ class MatomoVisitSenderTest extends TestCase public function multipleVisitsCanBeSent(): void { $dateRange = DateRange::allTime(); - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); $bot = Visitor::botInstance(); $this->visitIterationRepository->expects($this->once())->method('findAllVisits')->with($dateRange)->willReturn([ diff --git a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php index faddafeb..73feece2 100644 --- a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php @@ -34,7 +34,7 @@ class DeleteShortUrlServiceTest extends TestCase protected function setUp(): void { $shortUrl = ShortUrl::createFake()->setVisits(new ArrayCollection( - array_map(fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), range(0, 10)), + array_map(fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 10)), )); $this->shortCode = $shortUrl->getShortCode(); diff --git a/module/Core/test/ShortUrl/ShortUrlResolverTest.php b/module/Core/test/ShortUrl/ShortUrlResolverTest.php index 4d199d67..d565a352 100644 --- a/module/Core/test/ShortUrl/ShortUrlResolverTest.php +++ b/module/Core/test/ShortUrl/ShortUrlResolverTest.php @@ -128,7 +128,7 @@ class ShortUrlResolverTest extends TestCase ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'https://longUrl']), ); $shortUrl->setVisits(new ArrayCollection(array_map( - fn () => Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()), + fn () => Visit::forValidShortUrl($shortUrl, Visitor::empty()), range(0, 4), ))); @@ -147,7 +147,7 @@ class ShortUrlResolverTest extends TestCase 'longUrl' => 'https://longUrl', ])); $shortUrl->setVisits(new ArrayCollection(array_map( - fn () => Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()), + fn () => Visit::forValidShortUrl($shortUrl, Visitor::empty()), range(0, 4), ))); diff --git a/module/Core/test/Visit/Entity/VisitTest.php b/module/Core/test/Visit/Entity/VisitTest.php index 3556c1f1..edb47a53 100644 --- a/module/Core/test/Visit/Entity/VisitTest.php +++ b/module/Core/test/Visit/Entity/VisitTest.php @@ -55,7 +55,7 @@ class VisitTest extends TestCase public static function provideOrphanVisits(): iterable { yield 'base path visit' => [ - $visit = Visit::forBasePath(Visitor::emptyInstance()), + $visit = Visit::forBasePath(Visitor::empty()), [ 'referer' => '', 'date' => $visit->date->toAtomString(), diff --git a/module/Core/test/Visit/Geolocation/VisitLocatorTest.php b/module/Core/test/Visit/Geolocation/VisitLocatorTest.php index f1d86f63..cd6f12da 100644 --- a/module/Core/test/Visit/Geolocation/VisitLocatorTest.php +++ b/module/Core/test/Visit/Geolocation/VisitLocatorTest.php @@ -48,7 +48,7 @@ class VisitLocatorTest extends TestCase $unlocatedVisits = array_map( fn (int $i) => Visit::forValidShortUrl( ShortUrl::withLongUrl(sprintf('https://short_code_%s', $i)), - Visitor::emptyInstance(), + Visitor::empty(), ), range(1, 200), ); @@ -87,7 +87,7 @@ class VisitLocatorTest extends TestCase bool $isNonLocatableAddress, ): void { $unlocatedVisits = [ - Visit::forValidShortUrl(ShortUrl::withLongUrl('https://foo'), Visitor::emptyInstance()), + Visit::forValidShortUrl(ShortUrl::withLongUrl('https://foo'), Visitor::empty()), ]; $this->repo->expects($this->once())->method($expectedRepoMethodName)->willReturn($unlocatedVisits); diff --git a/module/Core/test/Visit/Geolocation/VisitToLocationHelperTest.php b/module/Core/test/Visit/Geolocation/VisitToLocationHelperTest.php index 57926afe..1f6b7f09 100644 --- a/module/Core/test/Visit/Geolocation/VisitToLocationHelperTest.php +++ b/module/Core/test/Visit/Geolocation/VisitToLocationHelperTest.php @@ -40,7 +40,7 @@ class VisitToLocationHelperTest extends TestCase public static function provideNonLocatableVisits(): iterable { - yield [Visit::forBasePath(Visitor::emptyInstance()), IpCannotBeLocatedException::forEmptyAddress()]; + yield [Visit::forBasePath(Visitor::empty()), IpCannotBeLocatedException::forEmptyAddress()]; yield [ Visit::forBasePath(new Visitor('foo', 'bar', IpAddress::LOCALHOST, '')), IpCannotBeLocatedException::forLocalhost(), diff --git a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php index dd998f71..2dbaa25a 100644 --- a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php +++ b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php @@ -53,7 +53,7 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase #[Test, DataProvider('provideLimitAndOffset')] public function getSliceDelegatesToRepository(int $limit, int $offset): void { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); $list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)]; $this->repo->expects($this->once())->method('findNonOrphanVisits')->with(new VisitsListFiltering( $this->params->dateRange, diff --git a/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php index 3e50faf0..abad2fc0 100644 --- a/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php +++ b/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php @@ -53,7 +53,7 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase #[Test, DataProvider('provideLimitAndOffset')] public function getSliceDelegatesToRepository(int $limit, int $offset): void { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); $list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)]; $this->repo->expects($this->once())->method('findOrphanVisits')->with(new OrphanVisitsListFiltering( dateRange: $this->params->dateRange, diff --git a/module/Core/test/Visit/VisitsStatsHelperTest.php b/module/Core/test/Visit/VisitsStatsHelperTest.php index 10c11b64..d6762c00 100644 --- a/module/Core/test/Visit/VisitsStatsHelperTest.php +++ b/module/Core/test/Visit/VisitsStatsHelperTest.php @@ -104,7 +104,7 @@ class VisitsStatsHelperTest extends TestCase $repo->expects($this->once())->method('shortCodeIsInUse')->with($identifier, $spec)->willReturn(true); $list = array_map( - static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), + static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 1), ); $repo2 = $this->createMock(VisitRepository::class); @@ -164,7 +164,7 @@ class VisitsStatsHelperTest extends TestCase $repo->expects($this->once())->method('tagExists')->with($tag, $apiKey)->willReturn(true); $list = array_map( - static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), + static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 1), ); $repo2 = $this->createMock(VisitRepository::class); @@ -205,7 +205,7 @@ class VisitsStatsHelperTest extends TestCase $repo->expects($this->once())->method('domainExists')->with($domain, $apiKey)->willReturn(true); $list = array_map( - static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), + static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 1), ); $repo2 = $this->createMock(VisitRepository::class); @@ -235,7 +235,7 @@ class VisitsStatsHelperTest extends TestCase $repo->expects($this->never())->method('domainExists'); $list = array_map( - static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), + static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 1), ); $repo2 = $this->createMock(VisitRepository::class); @@ -261,7 +261,7 @@ class VisitsStatsHelperTest extends TestCase #[Test] public function orphanVisitsAreReturnedAsExpected(): void { - $list = array_map(static fn () => Visit::forBasePath(Visitor::emptyInstance()), range(0, 3)); + $list = array_map(static fn () => Visit::forBasePath(Visitor::empty()), range(0, 3)); $repo = $this->createMock(VisitRepository::class); $repo->expects($this->once())->method('countOrphanVisits')->with( $this->isInstanceOf(OrphanVisitsCountFiltering::class), @@ -280,7 +280,7 @@ class VisitsStatsHelperTest extends TestCase public function nonOrphanVisitsAreReturnedAsExpected(): void { $list = array_map( - static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::emptyInstance()), + static fn () => Visit::forValidShortUrl(ShortUrl::createFake(), Visitor::empty()), range(0, 3), ); $repo = $this->createMock(VisitRepository::class); diff --git a/module/Core/test/Visit/VisitsTrackerTest.php b/module/Core/test/Visit/VisitsTrackerTest.php index f45a27d8..bfcf2828 100644 --- a/module/Core/test/Visit/VisitsTrackerTest.php +++ b/module/Core/test/Visit/VisitsTrackerTest.php @@ -33,43 +33,44 @@ class VisitsTrackerTest extends TestCase #[Test, DataProvider('provideTrackingMethodNames')] public function trackPersistsVisitAndDispatchesEvent(string $method, array $args): void { - $this->em->expects($this->once())->method('persist')->with( - $this->callback(fn (Visit $visit) => $visit->setId('1') !== null), - ); - $this->em->expects($this->once())->method('flush'); + $this->em->expects($this->once())->method('persist')->with($this->isInstanceOf(Visit::class)); $this->eventDispatcher->expects($this->once())->method('dispatch')->with( $this->isInstanceOf(UrlVisited::class), ); - $this->visitsTracker()->{$method}(...$args); + $result = $this->visitsTracker()->{$method}(...$args); + + self::assertInstanceOf(Visit::class, $result); } #[Test, DataProvider('provideTrackingMethodNames')] public function trackingIsSkippedCompletelyWhenDisabledFromOptions(string $method, array $args): void { $this->em->expects($this->never())->method('persist'); - $this->em->expects($this->never())->method('flush'); $this->eventDispatcher->expects($this->never())->method('dispatch'); - $this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args); + $result = $this->visitsTracker(new TrackingOptions(disableTracking: true))->{$method}(...$args); + + self::assertNull($result); } public static function provideTrackingMethodNames(): iterable { - yield 'track' => ['track', [ShortUrl::createFake(), Visitor::emptyInstance()]]; - yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit', [Visitor::emptyInstance()]]; - yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit', [Visitor::emptyInstance()]]; - yield 'trackRegularNotFoundVisit' => ['trackRegularNotFoundVisit', [Visitor::emptyInstance()]]; + yield 'track' => ['track', [ShortUrl::createFake(), Visitor::empty()]]; + yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit', [Visitor::empty()]]; + yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit', [Visitor::empty()]]; + yield 'trackRegularNotFoundVisit' => ['trackRegularNotFoundVisit', [Visitor::empty()]]; } #[Test, DataProvider('provideOrphanTrackingMethodNames')] public function orphanVisitsAreNotTrackedWhenDisabled(string $method): void { $this->em->expects($this->never())->method('persist'); - $this->em->expects($this->never())->method('flush'); $this->eventDispatcher->expects($this->never())->method('dispatch'); - $this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::emptyInstance()); + $result = $this->visitsTracker(new TrackingOptions(trackOrphanVisits: false))->{$method}(Visitor::empty()); + + self::assertNull($result); } public static function provideOrphanTrackingMethodNames(): iterable diff --git a/module/Rest/test/Action/Visit/OrphanVisitsActionTest.php b/module/Rest/test/Action/Visit/OrphanVisitsActionTest.php index d5bdfef9..6892d3bd 100644 --- a/module/Rest/test/Action/Visit/OrphanVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/OrphanVisitsActionTest.php @@ -35,7 +35,7 @@ class OrphanVisitsActionTest extends TestCase #[Test] public function requestIsHandled(): void { - $visitor = Visitor::emptyInstance(); + $visitor = Visitor::empty(); $visits = [Visit::forInvalidShortUrl($visitor), Visit::forRegularNotFound($visitor)]; $this->visitsHelper->expects($this->once())->method('orphanVisits')->with( $this->isInstanceOf(OrphanVisitsParams::class),