wip - cached scraper service unit tests

This commit is contained in:
pushrbx 2023-01-29 20:51:25 +00:00
parent 25378c263b
commit c78d8cd333
2 changed files with 150 additions and 7 deletions

View File

@ -7,10 +7,11 @@ use App\Contracts\Repository;
use App\Http\HttpHelper; use App\Http\HttpHelper;
use App\Support\CachedData; use App\Support\CachedData;
use Illuminate\Contracts\Database\Query\Builder; use Illuminate\Contracts\Database\Query\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Jikan\MyAnimeList\MalClient; use Jikan\MyAnimeList\MalClient;
use JMS\Serializer\SerializerInterface;
use MongoDB\BSON\UTCDateTime; use MongoDB\BSON\UTCDateTime;
use JMS\Serializer\Serializer;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
@ -21,7 +22,7 @@ final class DefaultCachedScraperService implements CachedScraperService
public function __construct( public function __construct(
private readonly Repository $repository, private readonly Repository $repository,
private readonly MalClient $jikan, private readonly MalClient $jikan,
private readonly Serializer $serializer, private readonly SerializerInterface $serializer,
) )
{ {
} }
@ -143,9 +144,7 @@ final class DefaultCachedScraperService implements CachedScraperService
// insert cache if resource doesn't exist // insert cache if resource doesn't exist
if ($results->isEmpty()) { if ($results->isEmpty()) {
$this->repository->insert($response->toArray()); $this->repository->insert($response->toArray());
} } else if ($results->isExpired()) {
if ($results->isExpired()) {
$this->getQueryableByCacheKey($cacheKey)->update($response->toArray()); $this->getQueryableByCacheKey($cacheKey)->update($response->toArray());
} }
@ -157,13 +156,15 @@ final class DefaultCachedScraperService implements CachedScraperService
$meta = []; $meta = [];
if ($resultsEmpty) { if ($resultsEmpty) {
$meta = [ $meta = [
'createdAt' => new UTCDateTime(), // Using Carbon here for testability
'createdAt' => new UTCDateTime(Carbon::now()->getPreciseTimestamp(3)),
'request_hash' => $cacheKey 'request_hash' => $cacheKey
]; ];
} }
// Update `modifiedAt` meta // Update `modifiedAt` meta
$meta['modifiedAt'] = new UTCDateTime(); // Using Carbon here for testability
$meta['modifiedAt'] = new UTCDateTime(Carbon::now()->getPreciseTimestamp(3));
// join meta data with response // join meta data with response
return CachedData::from(collect($meta + $scraperResponse)); return CachedData::from(collect($meta + $scraperResponse));

View File

@ -0,0 +1,142 @@
<?php
namespace Unit;
use App\Contracts\Repository;
use App\Services\DefaultCachedScraperService;
use Illuminate\Support\Carbon;
use Jenssegers\Mongodb\Eloquent\Builder;
use Jikan\MyAnimeList\MalClient;
use JMS\Serializer\SerializerInterface;
use Mockery;
use MongoDB\BSON\UTCDateTime;
use Tests\TestCase;
final class DefaultCachedScraperServiceTest extends TestCase
{
public function tearDown(): void
{
Mockery::close();
// reset time pinning
Carbon::setTestNow();
}
private function requestHash(): string
{
return sha1("asdf");
}
private function makeCtorArgMocks(int $repositoryWhereCallCount = 1): array
{
$queryBuilderMock = Mockery::mock(Builder::class)->makePartial();
$repositoryMock = Mockery::mock(Repository::class);
$serializerMock = Mockery::mock(SerializerInterface::class);
$repositoryMock
->expects()
->where("request_hash", $this->requestHash())
->times($repositoryWhereCallCount)
->andReturn($queryBuilderMock);
return [
$queryBuilderMock,
$repositoryMock,
$serializerMock
];
}
public function testIfFindListReturnsNotExpiredItems()
{
$testRequestHash = $this->requestHash();
// the cached data in the database
$dummyResults = collect([
["dummy" => "dummy1", "modifiedAt" => new UTCDateTime()],
["dummy" => "dummy2", "modifiedAt" => new UTCDateTime()]
]);
[$queryBuilderMock, $repositoryMock, $serializerMock] = $this->makeCtorArgMocks();
$queryBuilderMock->expects()->get()->once()->andReturn($dummyResults);
$target = new DefaultCachedScraperService($repositoryMock, new MalClient(), $serializerMock);
$result = $target->findList($testRequestHash, fn() => []);
$this->assertEquals($dummyResults->toArray(), $result->toArray());
}
public function testIfFindListUpdatesCacheIfItemsExpired()
{
$testRequestHash = $this->requestHash();
$now = Carbon::now();
Carbon::setTestNow($now);
// the cached data in the database
$dummyResults = collect([
["dummy" => "dummy1", "modifiedAt" => new UTCDateTime($now->sub("2 days")->timestamp)],
["dummy" => "dummy2", "modifiedAt" => new UTCDateTime($now->sub("2 days")->timestamp)]
]);
// the data returned by the scraper
$scraperData = [
"results" => [
["dummy" => "dummy1"],
["dummy" => "dummy2"]
]
];
[$queryBuilderMock, $repositoryMock, $serializerMock] = $this->makeCtorArgMocks(3);
$queryBuilderMock->expects()->get()->twice()->andReturn($dummyResults);
$queryBuilderMock->expects()->update(Mockery::any())->once()->andReturn(1);
$serializerMock->allows([
"toArray" => $scraperData
]);
$target = new DefaultCachedScraperService($repositoryMock, new MalClient(), $serializerMock);
$result = $target->findList($testRequestHash, fn() => []);
$this->assertEquals([
["dummy" => "dummy1", "modifiedAt" => new UTCDateTime($now->getPreciseTimestamp(3))],
["dummy" => "dummy2", "modifiedAt" => new UTCDateTime($now->getPreciseTimestamp(3))]
], $result->toArray());
}
public function testIfFindListUpdatesCacheIfCacheIsEmpty()
{
$testRequestHash = $this->requestHash();
$now = Carbon::now();
Carbon::setTestNow($now);
// the data returned by the scraper
$scraperData = [
"results" => [
["dummy" => "dummy1"],
["dummy" => "dummy2"]
]
];
$cacheData = [
["dummy" => "dummy1", "modifiedAt" => new UTCDateTime($now->getPreciseTimestamp(3))],
["dummy" => "dummy2", "modifiedAt" => new UTCDateTime($now->getPreciseTimestamp(3))]
];
[$queryBuilderMock, $repositoryMock, $serializerMock] = $this->makeCtorArgMocks(2);
// at first the cache is empty
$queryBuilderMock->expects()->get()->once()->andReturn(collect());
// then it gets "inserted" into
$queryBuilderMock->expects()->get()->once()->andReturn(collect($cacheData));
$repositoryMock->expects()->insert(Mockery::any())->once()->andReturn(true);
$serializerMock->allows([
"toArray" => $scraperData
]);
$target = new DefaultCachedScraperService($repositoryMock, new MalClient(), $serializerMock);
$result = $target->findList($testRequestHash, fn() => []);
$this->assertEquals($cacheData, $result->toArray());
}
public function testIfModifiedAtValueSetCorrectlyDuringCacheUpdate()
{
}
}