From e1e3c7f0614e456f76ab75910569386273fcb2b0 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 9 May 2020 10:10:48 +0200 Subject: [PATCH] Created paginator adapter tests --- ...AbstractCacheableCountPaginatorAdapter.php | 29 ++++++++++ .../Adapter/VisitsForTagPaginatorAdapter.php | 19 +------ .../Adapter/VisitsPaginatorAdapter.php | 19 +------ .../VisitsForTagPaginatorAdapterTest.php | 52 +++++++++++++++++ .../Adapter/VisitsPaginatorAdapterTest.php | 57 +++++++++++++++++++ 5 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 module/Core/src/Paginator/Adapter/AbstractCacheableCountPaginatorAdapter.php create mode 100644 module/Core/test/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php create mode 100644 module/Core/test/Paginator/Adapter/VisitsPaginatorAdapterTest.php diff --git a/module/Core/src/Paginator/Adapter/AbstractCacheableCountPaginatorAdapter.php b/module/Core/src/Paginator/Adapter/AbstractCacheableCountPaginatorAdapter.php new file mode 100644 index 00000000..cc2a8287 --- /dev/null +++ b/module/Core/src/Paginator/Adapter/AbstractCacheableCountPaginatorAdapter.php @@ -0,0 +1,29 @@ +count !== null) { + return $this->count; + } + + return $this->count = $this->doCount(); + } + + abstract protected function doCount(): int; +} diff --git a/module/Core/src/Paginator/Adapter/VisitsForTagPaginatorAdapter.php b/module/Core/src/Paginator/Adapter/VisitsForTagPaginatorAdapter.php index d456ad8c..e80fbcdd 100644 --- a/module/Core/src/Paginator/Adapter/VisitsForTagPaginatorAdapter.php +++ b/module/Core/src/Paginator/Adapter/VisitsForTagPaginatorAdapter.php @@ -4,18 +4,15 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Paginator\Adapter; -use Laminas\Paginator\Adapter\AdapterInterface; use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Repository\VisitRepositoryInterface; -class VisitsForTagPaginatorAdapter implements AdapterInterface +class VisitsForTagPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter { private VisitRepositoryInterface $visitRepository; private string $tag; private VisitsParams $params; - private ?int $count = null; - public function __construct(VisitRepositoryInterface $visitRepository, string $tag, VisitsParams $params) { $this->visitRepository = $visitRepository; @@ -33,18 +30,8 @@ class VisitsForTagPaginatorAdapter implements AdapterInterface ); } - public function count(): int + protected function doCount(): int { - // Since a new adapter instance is created every time visits are fetched, it is reasonably safe to internally - // cache the count value. - // The reason it is cached is because the Paginator is actually calling the method twice. - // An inconsistent value could be returned if between the first call and the second one, a new visit is created. - // However, it's almost instant, and then the adapter instance is discarded immediately after. - - if ($this->count !== null) { - return $this->count; - } - - return $this->count = $this->visitRepository->countVisitsByTag($this->tag, $this->params->getDateRange()); + return $this->visitRepository->countVisitsByTag($this->tag, $this->params->getDateRange()); } } diff --git a/module/Core/src/Paginator/Adapter/VisitsPaginatorAdapter.php b/module/Core/src/Paginator/Adapter/VisitsPaginatorAdapter.php index 6a42ecbc..404ae309 100644 --- a/module/Core/src/Paginator/Adapter/VisitsPaginatorAdapter.php +++ b/module/Core/src/Paginator/Adapter/VisitsPaginatorAdapter.php @@ -4,19 +4,16 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Paginator\Adapter; -use Laminas\Paginator\Adapter\AdapterInterface; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Repository\VisitRepositoryInterface; -class VisitsPaginatorAdapter implements AdapterInterface +class VisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter { private VisitRepositoryInterface $visitRepository; private ShortUrlIdentifier $identifier; private VisitsParams $params; - private ?int $count = null; - public function __construct( VisitRepositoryInterface $visitRepository, ShortUrlIdentifier $identifier, @@ -38,19 +35,9 @@ class VisitsPaginatorAdapter implements AdapterInterface ); } - public function count(): int + protected function doCount(): int { - // Since a new adapter instance is created every time visits are fetched, it is reasonably safe to internally - // cache the count value. - // The reason it is cached is because the Paginator is actually calling the method twice. - // An inconsistent value could be returned if between the first call and the second one, a new visit is created. - // However, it's almost instant, and then the adapter instance is discarded immediately after. - - if ($this->count !== null) { - return $this->count; - } - - return $this->count = $this->visitRepository->countVisitsByShortCode( + return $this->visitRepository->countVisitsByShortCode( $this->identifier->shortCode(), $this->identifier->domain(), $this->params->getDateRange(), diff --git a/module/Core/test/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php b/module/Core/test/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php new file mode 100644 index 00000000..e4418c5b --- /dev/null +++ b/module/Core/test/Paginator/Adapter/VisitsForTagPaginatorAdapterTest.php @@ -0,0 +1,52 @@ +repo = $this->prophesize(VisitRepositoryInterface::class); + $this->adapter = new VisitsForTagPaginatorAdapter($this->repo->reveal(), 'foo', VisitsParams::fromRawData([])); + } + + /** @test */ + public function repoIsCalledEveryTimeItemsAreFetched(): void + { + $count = 3; + $limit = 1; + $offset = 5; + $findVisits = $this->repo->findVisitsByTag('foo', new DateRange(), $limit, $offset)->willReturn([]); + + for ($i = 0; $i < $count; $i++) { + $this->adapter->getItems($offset, $limit); + } + + $findVisits->shouldHaveBeenCalledTimes($count); + } + + /** @test */ + public function repoIsCalledOnlyOnceForCount(): void + { + $count = 3; + $countVisits = $this->repo->countVisitsByTag('foo', new DateRange())->willReturn(3); + + for ($i = 0; $i < $count; $i++) { + $this->adapter->count(); + } + + $countVisits->shouldHaveBeenCalledOnce(); + } +} diff --git a/module/Core/test/Paginator/Adapter/VisitsPaginatorAdapterTest.php b/module/Core/test/Paginator/Adapter/VisitsPaginatorAdapterTest.php new file mode 100644 index 00000000..744582b7 --- /dev/null +++ b/module/Core/test/Paginator/Adapter/VisitsPaginatorAdapterTest.php @@ -0,0 +1,57 @@ +repo = $this->prophesize(VisitRepositoryInterface::class); + $this->adapter = new VisitsPaginatorAdapter( + $this->repo->reveal(), + new ShortUrlIdentifier(''), + VisitsParams::fromRawData([]), + ); + } + + /** @test */ + public function repoIsCalledEveryTimeItemsAreFetched(): void + { + $count = 3; + $limit = 1; + $offset = 5; + $findVisits = $this->repo->findVisitsByShortCode('', null, new DateRange(), $limit, $offset)->willReturn([]); + + for ($i = 0; $i < $count; $i++) { + $this->adapter->getItems($offset, $limit); + } + + $findVisits->shouldHaveBeenCalledTimes($count); + } + + /** @test */ + public function repoIsCalledOnlyOnceForCount(): void + { + $count = 3; + $countVisits = $this->repo->countVisitsByShortCode('', null, new DateRange())->willReturn(3); + + for ($i = 0; $i < $count; $i++) { + $this->adapter->count(); + } + + $countVisits->shouldHaveBeenCalledOnce(); + } +}