Make Visit::date field readonly

This commit is contained in:
Alejandro Celaya 2024-04-12 18:42:59 +02:00
parent ce0f61b66d
commit ca42425b33
13 changed files with 41 additions and 46 deletions

View file

@ -55,7 +55,7 @@ abstract class AbstractVisitsListCommand extends Command
$rowData = [ $rowData = [
'referer' => $visit->referer, 'referer' => $visit->referer,
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'userAgent' => $visit->userAgent, 'userAgent' => $visit->userAgent,
'potentialBot' => $visit->potentialBot, 'potentialBot' => $visit->potentialBot,
'country' => $visit->getVisitLocation()?->countryName ?? 'Unknown', 'country' => $visit->getVisitLocation()?->countryName ?? 'Unknown',

View file

@ -60,7 +60,7 @@ class GetDomainVisitsCommandTest extends TestCase
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| Referer | Date | User agent | Country | City | Short Url | | Referer | Date | User agent | Country | City | Short Url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| foo | {$visit->getDate()->toAtomString()} | bar | Spain | Madrid | the_short_url | | foo | {$visit->date->toAtomString()} | bar | Spain | Madrid | the_short_url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
OUTPUT, OUTPUT,

View file

@ -110,7 +110,7 @@ class GetShortUrlVisitsCommandTest extends TestCase
+---------+---------------------------+------------+---------+--------+ +---------+---------------------------+------------+---------+--------+
| Referer | Date | User agent | Country | City | | Referer | Date | User agent | Country | City |
+---------+---------------------------+------------+---------+--------+ +---------+---------------------------+------------+---------+--------+
| foo | {$visit->getDate()->toAtomString()} | bar | Spain | Madrid | | foo | {$visit->date->toAtomString()} | bar | Spain | Madrid |
+---------+---------------------------+------------+---------+--------+ +---------+---------------------------+------------+---------+--------+
OUTPUT, OUTPUT,

View file

@ -57,7 +57,7 @@ class GetTagVisitsCommandTest extends TestCase
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| Referer | Date | User agent | Country | City | Short Url | | Referer | Date | User agent | Country | City | Short Url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| foo | {$visit->getDate()->toAtomString()} | bar | Spain | Madrid | the_short_url | | foo | {$visit->date->toAtomString()} | bar | Spain | Madrid | the_short_url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
OUTPUT, OUTPUT,

View file

@ -56,7 +56,7 @@ class GetNonOrphanVisitsCommandTest extends TestCase
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| Referer | Date | User agent | Country | City | Short Url | | Referer | Date | User agent | Country | City | Short Url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
| foo | {$visit->getDate()->toAtomString()} | bar | Spain | Madrid | the_short_url | | foo | {$visit->date->toAtomString()} | bar | Spain | Madrid | the_short_url |
+---------+---------------------------+------------+---------+--------+---------------+ +---------+---------------------------+------------+---------+--------+---------------+
OUTPUT, OUTPUT,

View file

@ -54,7 +54,7 @@ class GetOrphanVisitsCommandTest extends TestCase
+---------+---------------------------+------------+---------+--------+----------+ +---------+---------------------------+------------+---------+--------+----------+
| Referer | Date | User agent | Country | City | Type | | Referer | Date | User agent | Country | City | Type |
+---------+---------------------------+------------+---------+--------+----------+ +---------+---------------------------+------------+---------+--------+----------+
| foo | {$visit->getDate()->toAtomString()} | bar | Spain | Madrid | base_url | | foo | {$visit->date->toAtomString()} | bar | Spain | Madrid | base_url |
+---------+---------------------------+------------+---------+--------+----------+ +---------+---------------------------+------------+---------+--------+----------+
OUTPUT, OUTPUT,

View file

@ -139,7 +139,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface
$importedVisits = 0; $importedVisits = 0;
foreach ($iterable as $importedOrphanVisit) { foreach ($iterable as $importedOrphanVisit) {
// Skip visits which are older than the most recent already imported visit's date // Skip visits which are older than the most recent already imported visit's date
if ($mostRecentOrphanVisit?->getDate()->greaterThanOrEquals(normalizeDate($importedOrphanVisit->date))) { if ($mostRecentOrphanVisit?->date->greaterThanOrEquals(normalizeDate($importedOrphanVisit->date))) {
continue; continue;
} }

View file

@ -209,7 +209,7 @@ class ShortUrl extends AbstractEntity
->setMaxResults(1); ->setMaxResults(1);
$visit = $this->visits->matching($criteria)->last(); $visit = $this->visits->matching($criteria)->last();
return $visit instanceof Visit ? $visit->getDate() : null; return $visit instanceof Visit ? $visit->date : null;
} }
/** /**

View file

@ -29,8 +29,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public readonly ?string $remoteAddr = null, public readonly ?string $remoteAddr = null,
public readonly ?string $visitedUrl = null, public readonly ?string $visitedUrl = null,
private ?VisitLocation $visitLocation = null, private ?VisitLocation $visitLocation = null,
// TODO Make public readonly once VisitRepositoryTest does not try to set it public readonly Chronos $date = new Chronos(),
private Chronos $date = new Chronos(),
) { ) {
} }
@ -147,14 +146,6 @@ class Visit extends AbstractEntity implements JsonSerializable
return $this->type; return $this->type;
} }
/**
* @internal
*/
public function getDate(): Chronos
{
return $this->date;
}
public function jsonSerialize(): array public function jsonSerialize(): array
{ {
$base = [ $base = [

View file

@ -6,7 +6,6 @@ namespace ShlinkioDbTest\Shlink\Core\Visit\Repository;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\Test;
use ReflectionObject;
use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
@ -371,15 +370,15 @@ class VisitRepositoryTest extends DatabaseTestCase
$botsCount = 3; $botsCount = 3;
for ($i = 0; $i < 6; $i++) { for ($i = 0; $i < 6; $i++) {
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forBasePath($botsCount < 1 ? Visitor::emptyInstance() : Visitor::botInstance()), fn () => Visit::forBasePath($botsCount < 1 ? Visitor::emptyInstance() : Visitor::botInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forInvalidShortUrl(Visitor::emptyInstance()), fn () => Visit::forInvalidShortUrl(Visitor::emptyInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forRegularNotFound(Visitor::emptyInstance()), fn () => Visit::forRegularNotFound(Visitor::emptyInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
@ -429,15 +428,15 @@ class VisitRepositoryTest extends DatabaseTestCase
for ($i = 0; $i < 6; $i++) { for ($i = 0; $i < 6; $i++) {
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forBasePath(Visitor::emptyInstance()), fn () => Visit::forBasePath(Visitor::emptyInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forInvalidShortUrl(Visitor::emptyInstance()), fn () => Visit::forInvalidShortUrl(Visitor::emptyInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
$this->getEntityManager()->persist($this->setDateOnVisit( $this->getEntityManager()->persist($this->setDateOnVisit(
Visit::forRegularNotFound(Visitor::emptyInstance()), fn () => Visit::forRegularNotFound(Visitor::emptyInstance()),
Chronos::parse(sprintf('2020-01-0%s', $i + 1)), Chronos::parse(sprintf('2020-01-0%s', $i + 1)),
)); ));
} }
@ -566,7 +565,7 @@ class VisitRepositoryTest extends DatabaseTestCase
{ {
for ($i = 0; $i < $amount; $i++) { for ($i = 0; $i < $amount; $i++) {
$visit = $this->setDateOnVisit( $visit = $this->setDateOnVisit(
Visit::forValidShortUrl( fn () => Visit::forValidShortUrl(
$shortUrl, $shortUrl,
$botsAmount < 1 ? Visitor::emptyInstance() : Visitor::botInstance(), $botsAmount < 1 ? Visitor::emptyInstance() : Visitor::botInstance(),
), ),
@ -578,12 +577,14 @@ class VisitRepositoryTest extends DatabaseTestCase
} }
} }
private function setDateOnVisit(Visit $visit, Chronos $date): Visit /**
* @param callable(): Visit $createVisit
*/
private function setDateOnVisit(callable $createVisit, Chronos $date): Visit
{ {
$ref = new ReflectionObject($visit); Chronos::setTestNow($date);
$dateProp = $ref->getProperty('date'); $visit = $createVisit();
$dateProp->setAccessible(true); Chronos::setTestNow();
$dateProp->setValue($visit, $date);
return $visit; return $visit;
} }

View file

@ -76,7 +76,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
'referer' => '', 'referer' => '',
'userAgent' => '', 'userAgent' => '',
'visitLocation' => null, 'visitLocation' => null,
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'potentialBot' => false, 'potentialBot' => false,
'visitedUrl' => '', 'visitedUrl' => '',
], ],
@ -100,7 +100,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
'referer' => '', 'referer' => '',
'userAgent' => '', 'userAgent' => '',
'visitLocation' => null, 'visitLocation' => null,
'date' => $orphanVisit->getDate()->toAtomString(), 'date' => $orphanVisit->date->toAtomString(),
'potentialBot' => false, 'potentialBot' => false,
'visitedUrl' => $orphanVisit->visitedUrl, 'visitedUrl' => $orphanVisit->visitedUrl,
'type' => $orphanVisit->type->value, 'type' => $orphanVisit->type->value,

View file

@ -26,7 +26,7 @@ class VisitTest extends TestCase
self::assertEquals([ self::assertEquals([
'referer' => 'some site', 'referer' => 'some site',
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'userAgent' => $userAgent, 'userAgent' => $userAgent,
'visitLocation' => null, 'visitLocation' => null,
'potentialBot' => $expectedToBePotentialBot, 'potentialBot' => $expectedToBePotentialBot,
@ -58,7 +58,7 @@ class VisitTest extends TestCase
$visit = Visit::forBasePath(Visitor::emptyInstance()), $visit = Visit::forBasePath(Visitor::emptyInstance()),
[ [
'referer' => '', 'referer' => '',
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'userAgent' => '', 'userAgent' => '',
'visitLocation' => null, 'visitLocation' => null,
'potentialBot' => false, 'potentialBot' => false,
@ -74,7 +74,7 @@ class VisitTest extends TestCase
)), )),
[ [
'referer' => 'bar', 'referer' => 'bar',
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'userAgent' => 'foo', 'userAgent' => 'foo',
'visitLocation' => null, 'visitLocation' => null,
'potentialBot' => false, 'potentialBot' => false,
@ -92,7 +92,7 @@ class VisitTest extends TestCase
)->locate($location = VisitLocation::fromGeolocation(Location::emptyInstance())), )->locate($location = VisitLocation::fromGeolocation(Location::emptyInstance())),
[ [
'referer' => 'referer', 'referer' => 'referer',
'date' => $visit->getDate()->toAtomString(), 'date' => $visit->date->toAtomString(),
'userAgent' => 'user-agent', 'userAgent' => 'user-agent',
'visitLocation' => $location, 'visitLocation' => $location,
'potentialBot' => false, 'potentialBot' => false,

View file

@ -8,7 +8,6 @@ use Cake\Chronos\Chronos;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use ReflectionObject;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Model\Visitor;
@ -50,27 +49,31 @@ class VisitsFixture extends AbstractFixture implements DependentFixtureInterface
); );
$manager->persist($this->setVisitDate( $manager->persist($this->setVisitDate(
Visit::forBasePath(new Visitor('shlink-tests-agent', 'https://s.test', '1.2.3.4', '')), fn () => Visit::forBasePath(new Visitor('shlink-tests-agent', 'https://s.test', '1.2.3.4', '')),
'2020-01-01', '2020-01-01',
)); ));
$manager->persist($this->setVisitDate( $manager->persist($this->setVisitDate(
Visit::forRegularNotFound(new Visitor('shlink-tests-agent', 'https://s.test/foo/bar', '1.2.3.4', '')), fn () => Visit::forRegularNotFound(
new Visitor('shlink-tests-agent', 'https://s.test/foo/bar', '1.2.3.4', ''),
),
'2020-02-01', '2020-02-01',
)); ));
$manager->persist($this->setVisitDate( $manager->persist($this->setVisitDate(
Visit::forInvalidShortUrl(new Visitor('cf-facebook', 'https://s.test/foo', '1.2.3.4', 'foo.com')), fn () => Visit::forInvalidShortUrl(new Visitor('cf-facebook', 'https://s.test/foo', '1.2.3.4', 'foo.com')),
'2020-03-01', '2020-03-01',
)); ));
$manager->flush(); $manager->flush();
} }
private function setVisitDate(Visit $visit, string $date): Visit /**
* @param callable(): Visit $createVisit
*/
private function setVisitDate(callable $createVisit, string $date): Visit
{ {
$ref = new ReflectionObject($visit); Chronos::setTestNow($date);
$dateProp = $ref->getProperty('date'); $visit = $createVisit();
$dateProp->setAccessible(true); Chronos::setTestNow();
$dateProp->setValue($visit, Chronos::parse($date));
return $visit; return $visit;
} }