mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
wip - cached scraper service unit tests
This commit is contained in:
parent
25378c263b
commit
c78d8cd333
@ -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));
|
||||||
|
142
tests/Unit/DefaultCachedScraperServiceTest.php
Normal file
142
tests/Unit/DefaultCachedScraperServiceTest.php
Normal 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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user