mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
multiple changes
- wip -> http tests should use model factories - test runner bootstrap: jikan models are cached in a temporary file - fixed various bugs - improved test execution time with typesense - added new dev dependency: ClassFinder - updated composer scripts to include coverage generation - added coverage reports in phpunit - improved roadrunner integration - updated docker image - added xdebug in disabled state
This commit is contained in:
parent
0211fc4128
commit
a530e9f5d6
26
.codecov.yml
Normal file
26
.codecov.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# Docs: <https://docs.codecov.io/docs/commit-status>
|
||||
|
||||
coverage:
|
||||
# coverage lower than 50 is red, higher than 90 green
|
||||
range: 30..80
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Choose a minimum coverage ratio that the commit must meet to be considered a success.
|
||||
#
|
||||
# `auto` will use the coverage from the base commit (pull request base or parent commit) coverage to compare
|
||||
# against.
|
||||
target: auto
|
||||
|
||||
# Allow the coverage to drop by X%, and posting a success status.
|
||||
threshold: 5%
|
||||
|
||||
# Resulting status will pass no matter what the coverage is or what other settings are specified.
|
||||
informational: true
|
||||
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 5%
|
||||
informational: true
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -11,3 +11,9 @@ composer.phar
|
||||
/storage/app/failovers.json
|
||||
/storage/app/source_failover_last_downtime
|
||||
/storage/app/source_failover.lock
|
||||
# Temp dirs & trash
|
||||
/temp
|
||||
/tmp
|
||||
/coverage
|
||||
.DS_Store
|
||||
*.cache
|
||||
|
@ -19,8 +19,11 @@ RUN set -ex \
|
||||
# enable opcache for CLI and JIT, docs: <https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit>
|
||||
&& echo -e "\nopcache.enable=1\nopcache.enable_cli=1\nopcache.jit_buffer_size=32M\nopcache.jit=1235\n" >> \
|
||||
${PHP_INI_DIR}/conf.d/docker-php-ext-opcache.ini \
|
||||
# show php version
|
||||
&& php -v \
|
||||
# show installed modules
|
||||
&& php -m \
|
||||
&& composer --version \
|
||||
# create unpriviliged user
|
||||
&& adduser --disabled-password --shell "/sbin/nologin" --home "/nonexistent" --no-create-home --uid "10001" --gecos "" "jikanapi" \
|
||||
&& mkdir /app /var/run/rr \
|
||||
|
@ -5,11 +5,12 @@ namespace App;
|
||||
use App\Concerns\FilteredByLetter;
|
||||
use App\Enums\ClubCategoryEnum;
|
||||
use App\Enums\ClubTypeEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Jikan\Request\Club\ClubRequest;
|
||||
|
||||
class Club extends JikanApiSearchableModel
|
||||
{
|
||||
use FilteredByLetter;
|
||||
use FilteredByLetter, HasFactory;
|
||||
|
||||
protected array $filters = ["order_by", "sort", "letter", "category", "type"];
|
||||
|
||||
@ -19,7 +20,7 @@ class Club extends JikanApiSearchableModel
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'mal_id', 'url', 'images', 'name', 'members', 'category', 'created', 'access', 'anime', 'manga'
|
||||
'mal_id', 'url', 'images', 'name', 'members', 'category', 'created', 'access', 'anime', 'manga', 'created_at', 'updated_at', 'characters', 'staff'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -48,7 +49,7 @@ class Club extends JikanApiSearchableModel
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
parent::__construct($attributes);
|
||||
$this->displayNameFieldName = "title";
|
||||
$this->displayNameFieldName = "name";
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
|
@ -40,7 +40,7 @@ abstract class RequestHandlerWithScraperCache implements RequestHandler
|
||||
protected function resource(Collection $results): JsonResource
|
||||
{
|
||||
return new ResultsResource(
|
||||
$results->first()
|
||||
$results->first() ?? ["results" => []]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,9 @@
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Http\HttpHelper;
|
||||
use App\Support\JikanConfig;
|
||||
use Closure;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Jikan\Exception\BadResponseException;
|
||||
|
||||
@ -18,6 +20,13 @@ class MicroCaching
|
||||
'InsightsController@main'
|
||||
];
|
||||
|
||||
private readonly bool $isEnabled;
|
||||
|
||||
public function __construct(JikanConfig $jikanConfig)
|
||||
{
|
||||
$this->isEnabled = $jikanConfig->isMicroCachingEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
@ -27,6 +36,9 @@ class MicroCaching
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (!$this->isEnabled) {
|
||||
return $next($request);
|
||||
}
|
||||
if (isset($request->route()[1]['uses'])) {
|
||||
$route = explode('\\', $request->route()[1]['uses']);
|
||||
$route = end($route);
|
||||
@ -65,7 +77,7 @@ class MicroCaching
|
||||
Cache::add(
|
||||
$fingerprint,
|
||||
json_encode(
|
||||
$next($request)->getData()
|
||||
$response->getData()
|
||||
),
|
||||
env('MICROCACHING_EXPIRE', 60)
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ namespace App;
|
||||
use Jikan\Helper\Parser;
|
||||
use Laravel\Scout\Builder;
|
||||
use Laravel\Scout\Searchable;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
trait JikanSearchable
|
||||
{
|
||||
@ -30,8 +31,14 @@ trait JikanSearchable
|
||||
}, $field);
|
||||
}
|
||||
|
||||
protected function convertToTimestamp(?string $datetime): int
|
||||
protected function convertToTimestamp(mixed $datetime): int
|
||||
{
|
||||
if ($datetime instanceof \DateTimeInterface) {
|
||||
return $datetime->getTimestamp();
|
||||
}
|
||||
if ($datetime instanceof UTCDateTime) {
|
||||
return $datetime->toDateTime()->getTimestamp();
|
||||
}
|
||||
return $datetime ? Parser::parseDate($datetime)->getTimestamp() : 0;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App;
|
||||
|
||||
use App\Concerns\FilteredByLetter;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Jikan\Jikan;
|
||||
use Jikan\Request\Magazine\MagazinesRequest;
|
||||
|
||||
@ -12,7 +13,7 @@ use Jikan\Request\Magazine\MagazinesRequest;
|
||||
*/
|
||||
class Magazine extends JikanApiSearchableModel
|
||||
{
|
||||
use FilteredByLetter;
|
||||
use FilteredByLetter, HasFactory;
|
||||
protected array $filters = ["order_by", "sort", "letter"];
|
||||
|
||||
/**
|
||||
|
@ -222,6 +222,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
Features\AnimeExternalLookupHandler::class => $unitOfWorkInstance->anime(),
|
||||
Features\AnimeStreamingLookupHandler::class => $unitOfWorkInstance->anime(),
|
||||
Features\AnimeThemesLookupHandler::class => $unitOfWorkInstance->anime(),
|
||||
Features\AnimeUserUpdatesLookupHandler::class => $unitOfWorkInstance->documents("anime_userupdates"),
|
||||
Features\CharacterLookupHandler::class => $unitOfWorkInstance->characters(),
|
||||
Features\CharacterFullLookupHandler::class => $unitOfWorkInstance->characters(),
|
||||
Features\CharacterAnimeLookupHandler::class => $unitOfWorkInstance->characters(),
|
||||
@ -355,8 +356,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
public static function servicesToWarm(): array
|
||||
{
|
||||
$services = [
|
||||
ScoutSearchService::class,
|
||||
UnitOfWork::class
|
||||
ScoutSearchService::class
|
||||
];
|
||||
|
||||
if (Env::get("SCOUT_DRIVER") === "typesense") {
|
||||
@ -375,4 +375,13 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
public static function servicesToClear(): array
|
||||
{
|
||||
return [
|
||||
// in RoadRunner we want to reset the repositories after each request, because we cache the query builders in them.
|
||||
// todo: refactor repositories to avoid caching query builders, so this step is not necessary
|
||||
JikanUnitOfWork::class
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,10 @@ use Jikan\Model\Common\DateRange;
|
||||
use Jikan\Model\Common\MalUrl;
|
||||
use JMS\Serializer\GraphNavigatorInterface;
|
||||
use JMS\Serializer\Handler\HandlerRegistry;
|
||||
use JMS\Serializer\SerializationContext;
|
||||
use JMS\Serializer\Serializer;
|
||||
use JMS\Serializer\SerializerBuilder;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
class SerializerFactory
|
||||
{
|
||||
@ -45,6 +47,13 @@ class SerializerFactory
|
||||
'json',
|
||||
self::convertCarbonDateRange(...)
|
||||
);
|
||||
|
||||
$registry->registerHandler(
|
||||
GraphNavigatorInterface::DIRECTION_SERIALIZATION,
|
||||
UTCDateTime::class,
|
||||
'json',
|
||||
self::convertBsonDateTime(...)
|
||||
);
|
||||
}
|
||||
)
|
||||
->setSerializationContextFactory(new SerializationContextFactory())
|
||||
@ -129,4 +138,9 @@ class SerializerFactory
|
||||
{
|
||||
return $obj ? $obj->format(DATE_ATOM) : null;
|
||||
}
|
||||
|
||||
private static function convertBsonDateTime($visitor, UTCDateTime $obj, array $type, SerializationContext $context): string
|
||||
{
|
||||
return $obj->toDateTime()->format(DATE_ATOM);
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ namespace App\Support;
|
||||
|
||||
use App\Concerns\ScraperCacheTtl;
|
||||
use App\JikanApiModel;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Env;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
final class CachedData
|
||||
{
|
||||
@ -82,16 +84,31 @@ final class CachedData
|
||||
|
||||
$result = $this->scraperResult->first();
|
||||
|
||||
if ($result instanceof JikanApiModel && !is_null($result->getAttributeValue("modifiedAt"))) {
|
||||
return (int) $result["modifiedAt"]->toDateTime()->format("U");
|
||||
if ($result instanceof JikanApiModel && null != $modifiedAt = $result->getAttributeValue("modifiedAt")) {
|
||||
return $this->mixedToTimestamp($modifiedAt);
|
||||
}
|
||||
|
||||
if (is_array($result) && array_key_exists("modifiedAt", $result)) {
|
||||
return (int) $result["modifiedAt"]->toDateTime()->format("U");
|
||||
return $this->mixedToTimestamp($result["modifiedAt"]);
|
||||
}
|
||||
|
||||
if (is_object($result) && property_exists($result, "modifiedAt")) {
|
||||
return $result->modifiedAt->toDateTime()->format("U");
|
||||
return $this->mixedToTimestamp($result->modifiedAt);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function mixedToTimestamp(mixed $modifiedAt): ?int
|
||||
{
|
||||
if ($modifiedAt instanceof UTCDateTime) {
|
||||
return (int) $modifiedAt->toDateTime()->format("U");
|
||||
}
|
||||
if ($modifiedAt instanceof \DateTimeInterface) {
|
||||
return (int) $modifiedAt->format("U");
|
||||
}
|
||||
if (is_string($modifiedAt)) {
|
||||
return Carbon::createFromTimeString($modifiedAt)->format("U");
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -16,11 +16,14 @@ final class JikanConfig
|
||||
|
||||
private int $defaultCacheExpire;
|
||||
|
||||
private bool $microCachingEnabled;
|
||||
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$config = collect($config);
|
||||
$this->perEndpointCacheTtl = $config->get("per_endpoint_cache_ttl", []);
|
||||
$this->defaultCacheExpire = $config->get("default_cache_expire", 0);
|
||||
$this->microCachingEnabled = in_array($config->get("micro_caching_enabled", false), [true, 1, "1", "true"]);
|
||||
}
|
||||
|
||||
public function cacheTtlForEndpoint(string $endpoint): ?int
|
||||
@ -32,4 +35,9 @@ final class JikanConfig
|
||||
{
|
||||
return $this->defaultCacheExpire;
|
||||
}
|
||||
|
||||
public function isMicroCachingEnabled(): bool
|
||||
{
|
||||
return $this->microCachingEnabled;
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,46 @@
|
||||
|
||||
namespace App\Testing;
|
||||
|
||||
use Typesense\LaravelTypesense\Typesense;
|
||||
|
||||
trait ScoutFlush
|
||||
{
|
||||
protected array $searchIndexModelCleanupList = [
|
||||
"App\\Anime", "App\\Manga", "App\\Character", "App\\GenreAnime", "App\\GenreManga", "App\\Person"
|
||||
"App\\Anime",
|
||||
"App\\Manga",
|
||||
"App\\Character",
|
||||
"App\\GenreAnime",
|
||||
"App\\GenreManga",
|
||||
"App\\Person",
|
||||
"App\\Club",
|
||||
"App\\Magazine"
|
||||
];
|
||||
|
||||
public function runScoutFlush(): void
|
||||
{
|
||||
if (config("scout.driver") === "typesense") {
|
||||
/**
|
||||
* @var Typesense $typeSenseClient
|
||||
*/
|
||||
$typeSenseClient = app(Typesense::class);
|
||||
// more optimized approach for quicker tests.
|
||||
foreach ($this->searchIndexModelCleanupList as $model) {
|
||||
$modelInstance = new $model;
|
||||
$collection = $typeSenseClient->getCollectionIndex($modelInstance);
|
||||
// we count items by exporting
|
||||
$items = $collection->documents->export();
|
||||
if (strlen($items) > 1) {
|
||||
$typeSenseClient->deleteDocuments($collection, [
|
||||
"filter_by" => "mal_id:>0",
|
||||
"batch_size" => 500
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->searchIndexModelCleanupList as $model) {
|
||||
$this->artisan("scout:flush", ["model" => $model]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,12 @@
|
||||
namespace App\Testing;
|
||||
|
||||
use App\JikanApiModel;
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* A trait for test cases which want to clear the database after each test
|
||||
*/
|
||||
trait SyntheticMongoDbTransaction
|
||||
{
|
||||
private static array $jikanModels = [];
|
||||
@ -13,7 +17,9 @@ trait SyntheticMongoDbTransaction
|
||||
{
|
||||
if (count(self::$jikanModels) === 0)
|
||||
{
|
||||
self::$jikanModels = array_filter(get_declared_classes(), fn($class) => is_subclass_of($class, JikanApiModel::class));
|
||||
self::$jikanModels = json_decode(
|
||||
file_get_contents(base_path("storage/app") . "/jikan_model_classes.json")
|
||||
);
|
||||
}
|
||||
|
||||
return self::$jikanModels;
|
||||
|
@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\JikanApiModel;
|
||||
use PackageVersions\Versions;
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
@ -7,3 +10,13 @@ require_once __DIR__.'/../vendor/autoload.php';
|
||||
Defines
|
||||
*/
|
||||
defined('JIKAN_PARSER_VERSION') or define('JIKAN_PARSER_VERSION', Versions::getVersion('jikan-me/jikan'));
|
||||
|
||||
|
||||
$classNamesCachePath = __DIR__ . "/../storage/app";
|
||||
// this line only works if dev dependencies are installed
|
||||
$classes = ClassFinder::getClassesInNamespace("App");
|
||||
$jikanModels = array_values(
|
||||
array_filter($classes, fn($class) => is_subclass_of($class, JikanApiModel::class))
|
||||
);
|
||||
file_put_contents($classNamesCachePath . "/jikan_model_classes.json", json_encode($jikanModels));
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.21",
|
||||
"haydenpierce/class-finder": "^0.4.4",
|
||||
"mockery/mockery": "^1.5.1",
|
||||
"phpunit/phpunit": "^9.5.28"
|
||||
},
|
||||
@ -64,8 +65,13 @@
|
||||
"post-root-package-install": [
|
||||
"php -r \"copy('.env.dist', '.env');\""
|
||||
],
|
||||
"phpunit": "@php ./vendor/bin/phpunit --no-coverage",
|
||||
"phpunit-cover": "@php ./vendor/bin/phpunit",
|
||||
"test": [
|
||||
"php ./vendor/phpunit/phpunit/phpunit"
|
||||
"@phpunit"
|
||||
],
|
||||
"test-cover": [
|
||||
"@phpunit-cover"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
|
52
composer.lock
generated
52
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "64ddaa662c7d0df9a7127336259b22ff",
|
||||
"content-hash": "9ffd521140d58bc63cf640fedd62a978",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amphp/amp",
|
||||
@ -7238,12 +7238,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pushrbx/lumen-roadrunner.git",
|
||||
"reference": "835e99ba6854f31236a7039f29e730c785800a88"
|
||||
"reference": "52ab151734d13861759415f1985c8e01e96885dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pushrbx/lumen-roadrunner/zipball/835e99ba6854f31236a7039f29e730c785800a88",
|
||||
"reference": "835e99ba6854f31236a7039f29e730c785800a88",
|
||||
"url": "https://api.github.com/repos/pushrbx/lumen-roadrunner/zipball/52ab151734d13861759415f1985c8e01e96885dd",
|
||||
"reference": "52ab151734d13861759415f1985c8e01e96885dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -7337,7 +7337,7 @@
|
||||
"issues": "https://github.com/spiral/roadrunner-laravel/issues",
|
||||
"source": "https://github.com/spiral/roadrunner-laravel"
|
||||
},
|
||||
"time": "2022-08-16T16:49:18+00:00"
|
||||
"time": "2023-02-01T00:49:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
@ -11552,6 +11552,48 @@
|
||||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "haydenpierce/class-finder",
|
||||
"version": "0.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@gitlab.com:hpierce1102/ClassFinder.git",
|
||||
"reference": "94c602870ddf8d4fa2d67fb9bae637d88f9bd76e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://gitlab.com/api/v4/projects/hpierce1102%2FClassFinder/repository/archive.zip?sha=94c602870ddf8d4fa2d67fb9bae637d88f9bd76e",
|
||||
"reference": "94c602870ddf8d4fa2d67fb9bae637d88f9bd76e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpunit/phpunit": "~9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"HaydenPierce\\ClassFinder\\": "src/",
|
||||
"HaydenPierce\\ClassFinder\\UnitTest\\": "test/unit"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hayden Pierce",
|
||||
"email": "hayden@haydenpierce.com"
|
||||
}
|
||||
],
|
||||
"description": "A library that can provide of a list of classes in a given namespace",
|
||||
"time": "2022-09-26T22:42:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.5.1",
|
||||
|
@ -1,12 +1,21 @@
|
||||
<?php
|
||||
|
||||
$db_username = env('DB_USERNAME', 'admin');
|
||||
$dsn = "mongodb://";
|
||||
if (empty($db_username)) {
|
||||
$dsn .= env('DB_HOST', 'localhost').":".env('DB_PORT', 27017)."/".env('DB_ADMIN', 'admin');
|
||||
}
|
||||
else {
|
||||
$dsn .= env('DB_USERNAME', 'admin').":".env('DB_PASSWORD', '')."@".env('DB_HOST', 'localhost').":".env('DB_PORT', 27017)."/".env('DB_ADMIN', 'admin');
|
||||
}
|
||||
|
||||
return [
|
||||
'default' => env('DB_CONNECTION', 'mongodb'),
|
||||
|
||||
'connections' => [
|
||||
'mongodb' => [
|
||||
'driver' => 'mongodb',
|
||||
'dsn'=> "mongodb://".env('DB_USERNAME', 'admin').":".env('DB_PASSWORD', '')."@".env('DB_HOST', 'localhost').":".env('DB_PORT', 27017)."/".env('DB_ADMIN', 'admin'),
|
||||
'dsn'=> $dsn,
|
||||
'database' => env('DB_DATABASE', 'jikan'),
|
||||
]
|
||||
],
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'micro_caching_enabled' => env('MICROCACHING', false),
|
||||
'default_cache_expire' => env('CACHE_DEFAULT_EXPIRE', 86400),
|
||||
'per_endpoint_cache_ttl' => [
|
||||
/**
|
||||
|
@ -33,6 +33,7 @@ return [
|
||||
'listeners' => [
|
||||
Events\BeforeLoopStartedEvent::class => [
|
||||
...Defaults::beforeLoopStarted(),
|
||||
Listeners\ResetLaravelScoutListener::class
|
||||
],
|
||||
|
||||
Events\BeforeLoopIterationEvent::class => [
|
||||
@ -86,6 +87,7 @@ return [
|
||||
|
||||
'clear' => [
|
||||
...Defaults::servicesToClear(),
|
||||
...\App\Providers\AppServiceProvider::servicesToClear(),
|
||||
'auth', // is not required for Laravel >= v8.35
|
||||
],
|
||||
|
||||
|
@ -21,7 +21,15 @@ class CharacterFactory extends JikanModelFactory
|
||||
return [
|
||||
"mal_id" => $mal_id,
|
||||
"url" => $url,
|
||||
"images" => [],
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/4/50197.jpg"
|
||||
],
|
||||
"webp" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/4/50197.webp",
|
||||
"small_image_url" => "https://cdn.myanimelist.net/images/characters/4/50197t.webp"
|
||||
]
|
||||
],
|
||||
"name" => $this->faker->name(),
|
||||
"name_kanji" => "岡",
|
||||
"nicknames" => [],
|
||||
@ -29,7 +37,20 @@ class CharacterFactory extends JikanModelFactory
|
||||
"about" => "test",
|
||||
"createdAt" => new UTCDateTime(),
|
||||
"modifiedAt" => new UTCDateTime(),
|
||||
"request_hash" => sprintf("request:%s:%s", "v4", $this->getItemTestUrl("character", $mal_id))
|
||||
"request_hash" => sprintf("request:%s:%s", "v4", $this->getItemTestUrl("character", $mal_id)),
|
||||
"animeography" => [],
|
||||
"mangaography" => [],
|
||||
"voice_actors" => [
|
||||
[
|
||||
"person" => [
|
||||
"mal_id" => 11,
|
||||
"url" => "https://myanimelist.net/people/11/Kouichi_Yamadera",
|
||||
"images" => [],
|
||||
"name" => "Yamadera, Kouichi"
|
||||
],
|
||||
"language" => "Japanese"
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
80
database/factories/ClubFactory.php
Normal file
80
database/factories/ClubFactory.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Club;
|
||||
use App\Testing\JikanDataGenerator;
|
||||
use Illuminate\Support\Carbon;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
final class ClubFactory extends JikanModelFactory
|
||||
{
|
||||
use JikanDataGenerator;
|
||||
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Club::class;
|
||||
|
||||
protected function definitionInternal(): array
|
||||
{
|
||||
$mal_id = $this->createMalId();
|
||||
$url = "https://myanimelist.net/clubs.php?cid=".$mal_id;
|
||||
$createdAt = Carbon::createFromTimestamp(
|
||||
$this->faker->dateTime()->getTimestamp())->toAtomString();
|
||||
$modifiedAt = Carbon::createFromTimestamp(
|
||||
$this->faker->dateTime()->getTimestamp())->toDateTimeString();
|
||||
|
||||
return [
|
||||
"mal_id" => $mal_id,
|
||||
"url" => $url,
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/clubs/16/222057.jpg"
|
||||
]
|
||||
],
|
||||
"category" => $this->faker->randomElement(["anime", "manga", "characters"]),
|
||||
"created" => $createdAt,
|
||||
"createdAt" => new UTCDateTime(),
|
||||
"modifiedAt" => new UTCDateTime(),
|
||||
"created_at" => $createdAt,
|
||||
"updated_at" => $modifiedAt,
|
||||
"name" => $this->faker->name(),
|
||||
"request_hash" => sprintf("request:%s:%s", "v4", $this->getItemTestUrl("club", $mal_id)),
|
||||
"anime" => [
|
||||
[
|
||||
"mal_id" => $this->createMalId(),
|
||||
"type" => "anime",
|
||||
"name" => $this->faker->name(),
|
||||
"url" => "https://myanimelist.net/anime/1/x"
|
||||
]
|
||||
],
|
||||
"characters" => [
|
||||
[
|
||||
"mal_id" => $this->createMalId(),
|
||||
"type" => "character",
|
||||
"name" => $this->faker->name(),
|
||||
"url" => "https://myanimelist.net/character/1234"
|
||||
]
|
||||
],
|
||||
"manga" => [
|
||||
[
|
||||
"mal_id" => $this->createMalId(),
|
||||
"type" => "manga",
|
||||
"name" => $this->faker->name(),
|
||||
"url" => "https://myanimelist.net/manga/1/x"
|
||||
]
|
||||
],
|
||||
"staff" => [
|
||||
[
|
||||
"url" => "https://myanimelist.net/profile/cyruz",
|
||||
"username" => "cryuz"
|
||||
]
|
||||
],
|
||||
"members" => $this->faker->numberBetween(1, 9999),
|
||||
"access" => $this->faker->randomElement(["public", "private"])
|
||||
];
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\CarbonDateRange;
|
||||
use Jikan\Model\Common\DateRange;
|
||||
use JMS\Serializer\Serializer;
|
||||
use \Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Spatie\Enum\Laravel\Faker\FakerEnumProvider;
|
||||
@ -28,7 +30,16 @@ abstract class JikanModelFactory extends Factory
|
||||
* @var Serializer $serializer
|
||||
*/
|
||||
$serializer = app("SerializerV4");
|
||||
return $serializer->toArray($stateDefinition);
|
||||
$translated = array_merge(array(), $stateDefinition);
|
||||
foreach ($stateDefinition as $k => $v)
|
||||
{
|
||||
if ($v instanceof DateRange || $v instanceof CarbonDateRange)
|
||||
{
|
||||
$converted = $serializer->toArray([$k => $v]);
|
||||
$translated[$k] = $converted[$k];
|
||||
}
|
||||
}
|
||||
return $translated;
|
||||
}
|
||||
|
||||
protected abstract function definitionInternal(): array;
|
||||
|
33
database/factories/MagazineFactory.php
Normal file
33
database/factories/MagazineFactory.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Magazine;
|
||||
use App\Testing\JikanDataGenerator;
|
||||
|
||||
final class MagazineFactory extends JikanModelFactory
|
||||
{
|
||||
use JikanDataGenerator;
|
||||
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Magazine::class;
|
||||
|
||||
protected function definitionInternal(): array
|
||||
{
|
||||
$mal_id = $this->createMalId();
|
||||
$name = $this->createTitle();
|
||||
$url = $this->createMalUrl($mal_id, "manga/magazine");
|
||||
$count = $this->faker->numberBetween(1, 999);
|
||||
|
||||
return [
|
||||
"mal_id" => $mal_id,
|
||||
"name" => $name,
|
||||
"url" => $url,
|
||||
"count" => $count
|
||||
];
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
FROM spiralscout/roadrunner:2.10.6 as roadrunner
|
||||
FROM composer:2.3.9 as composer
|
||||
FROM mlocati/php-extension-installer:1.5.29 as php-ext-installer
|
||||
FROM php:8.1-bullseye as runtime
|
||||
FROM spiralscout/roadrunner:2.12.2 as roadrunner
|
||||
FROM composer:2.5.1 as composer
|
||||
FROM mlocati/php-extension-installer:1.5.52 as php-ext-installer
|
||||
FROM php:8.1.13-bullseye as runtime
|
||||
ARG GITHUB_PERSONAL_TOKEN
|
||||
LABEL org.opencontainers.image.source=https://github.com/jikan-me/jikan-rest/docker/base_image/php-8.1
|
||||
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
||||
COPY --from=php-ext-installer /usr/bin/install-php-extensions /usr/local/bin/
|
||||
ENV COMPOSER_HOME="/tmp/composer"
|
||||
RUN install-php-extensions gd exif intl bz2 gettext mongodb-stable redis opcache sockets pcntl
|
||||
RUN set -x \
|
||||
&& install-php-extensions gd exif intl bz2 gettext mongodb-stable redis opcache sockets pcntl \
|
||||
# install xdebug (for testing with code coverage), but do not enable it
|
||||
&& IPE_DONT_ENABLE=1 install-php-extensions xdebug-3.2.0
|
||||
|
||||
# install roadrunner
|
||||
COPY --from=roadrunner /usr/bin/rr /usr/bin/rr
|
||||
|
23
phpunit.xml
23
phpunit.xml
@ -10,16 +10,33 @@
|
||||
<directory>./tests/HttpV4/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="integration">
|
||||
<directory>./tests/integration/</directory>
|
||||
<directory>./tests/Integration/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="unit">
|
||||
<directory>./tests/unit/</directory>
|
||||
<directory>./tests/Unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage>
|
||||
<coverage includeUncoveredFiles="false" processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>./vendor</directory>
|
||||
<directory>./tests</directory>
|
||||
<directory>./storage</directory>
|
||||
<directory>./resources</directory>
|
||||
<directory>./docker</directory>
|
||||
<directory>./bootstrap</directory>
|
||||
<directory>./config</directory>
|
||||
<directory>./routes</directory>
|
||||
<directory>./.github</directory>
|
||||
</exclude>
|
||||
<report>
|
||||
<html outputDirectory="./coverage/html"/>
|
||||
<xml outputDirectory="./coverage/xml"/>
|
||||
<clover outputFile="./coverage/clover.xml"/>
|
||||
<text outputFile="php://stdout" showUncoveredFiles="false"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<listeners>
|
||||
<listener class="Tests\IntegrationTestListener" />
|
||||
|
1
storage/app/.gitignore
vendored
1
storage/app/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
!.gitignore
|
||||
failovers.json
|
||||
source_failover.lock
|
||||
jikan_model_classes.json
|
||||
|
@ -1,12 +1,93 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
<?php
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\Integration;
|
||||
|
||||
use App\Anime;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AnimeControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
$this->searchIndexModelCleanupList = ["App\\Anime"];
|
||||
}
|
||||
|
||||
private function givenDummyCharactersStaffData($uri)
|
||||
{
|
||||
DB::table("anime_characters_staff")->insert([
|
||||
"createdAt" => new UTCDateTime(),
|
||||
"modifiedAt" => new UTCDateTime(),
|
||||
"request_hash" => "request:anime:" . sha1($uri),
|
||||
"characters" => [
|
||||
[
|
||||
"character" => [
|
||||
"mal_id" => 3,
|
||||
"url" => "https://myanimelist.net/character/3/Jet_Black",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/11/253723.jpg?s=6c8a19a79a88c46ae15f30e3ef5fd839",
|
||||
"small_image_url" => "https://cdn.myanimelist.net/images/characters/11/253723t.jpg?s=6c8a19a79a88c46ae15f30e3ef5fd839"
|
||||
],
|
||||
"webp" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/11/253723.webp?s=6c8a19a79a88c46ae15f30e3ef5fd839",
|
||||
"small_image_url" => "https://cdn.myanimelist.net/images/characters/11/253723t.webp?s=6c8a19a79a88c46ae15f30e3ef5fd839"
|
||||
]
|
||||
],
|
||||
"name" => "Black, Jet"
|
||||
],
|
||||
"role" => "Main",
|
||||
"favorites" => 1,
|
||||
"voice_actors" => [
|
||||
[
|
||||
"person" => [
|
||||
"mal_id" => 357,
|
||||
"url" => "https://myanimelist.net/people/357/Unshou_Ishizuk",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/voiceactors/2/17135.jpg?s=5925123b8a7cf9b51a445c225442f0ef"
|
||||
]
|
||||
],
|
||||
"name" => "Ishizuka, Unshou"
|
||||
],
|
||||
"language" => "Japanese"
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
"staff" => [
|
||||
[
|
||||
"person" => [
|
||||
"mal_id" => 40009,
|
||||
"url" => "https://myanimelist.net/people/40009/Yutaka_Maseba",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/voiceactors/3/40216.jpg?s=d9fb7a625868ec7d9cd3804fa0da3fd6"
|
||||
]
|
||||
],
|
||||
"name" => "Maseba, Yutaka"
|
||||
],
|
||||
"positions" => [
|
||||
"Producer"
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
$this->get('/v4/anime/1')
|
||||
Anime::factory(1)->create([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$this->getJson('/v4/anime/1')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data' => [
|
||||
'mal_id',
|
||||
@ -116,7 +197,10 @@ class AnimeControllerTest extends TestCase
|
||||
|
||||
public function testCharacters()
|
||||
{
|
||||
$this->get('/v4/anime/1/characters')
|
||||
// let's avoid sending request to MAL in tests
|
||||
$this->givenDummyCharactersStaffData("/v4/anime/1/characters");
|
||||
|
||||
$this->getJson('/v4/anime/1/characters')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data' => [
|
||||
[
|
||||
@ -125,7 +209,8 @@ class AnimeControllerTest extends TestCase
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url'
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
@ -155,7 +240,8 @@ class AnimeControllerTest extends TestCase
|
||||
|
||||
public function testStaff()
|
||||
{
|
||||
$this->get('/v4/anime/1/staff')
|
||||
$this->givenDummyCharactersStaffData('/v4/anime/1/staff');
|
||||
$this->getJson('/v4/anime/1/staff')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
[
|
||||
@ -172,374 +258,4 @@ class AnimeControllerTest extends TestCase
|
||||
]
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testEpisodes()
|
||||
{
|
||||
$this->get('/v4/anime/1/episodes')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => [
|
||||
[
|
||||
'mal_id',
|
||||
'url',
|
||||
'title',
|
||||
'title_japanese',
|
||||
'title_romanji',
|
||||
'aired',
|
||||
'score',
|
||||
'filler',
|
||||
'recap',
|
||||
'forum_url'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->get('/v4/anime/21/episodes?page=2')
|
||||
->seeStatusCode(200)
|
||||
->seeJson([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => [
|
||||
[
|
||||
'mal_id',
|
||||
'url',
|
||||
'title',
|
||||
'title_japanese',
|
||||
'title_romanji',
|
||||
'aired',
|
||||
'score',
|
||||
'filler',
|
||||
'recap',
|
||||
'forum_url'
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testEpisode()
|
||||
{
|
||||
$this->get('/v4/anime/21/episodes/1')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
'mal_id',
|
||||
'url',
|
||||
'title',
|
||||
'title_japanese',
|
||||
'title_romanji',
|
||||
'duration',
|
||||
'aired',
|
||||
'aired',
|
||||
'filler',
|
||||
'recap',
|
||||
'synopsis',
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testNews()
|
||||
{
|
||||
$this->get('/v4/anime/1/news')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => [
|
||||
[
|
||||
'mal_id',
|
||||
'url',
|
||||
'title',
|
||||
'date',
|
||||
'author_username',
|
||||
'author_url',
|
||||
'forum_url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
'comments',
|
||||
'excerpt'
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testPictures()
|
||||
{
|
||||
$this->get('/v4/anime/1/pictures')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
[
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
'large_image_url',
|
||||
'small_image_url',
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
'large_image_url',
|
||||
'small_image_url',
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testVideos()
|
||||
{
|
||||
$this->get('/v4/anime/1/videos')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
'promo' => [
|
||||
[
|
||||
'title',
|
||||
'trailer' => [
|
||||
'youtube_id',
|
||||
'url',
|
||||
'embed_url',
|
||||
'images' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'medium_image_url',
|
||||
'large_image_url',
|
||||
'maximum_image_url',
|
||||
]
|
||||
],
|
||||
]
|
||||
],
|
||||
'episodes' => [
|
||||
[
|
||||
'mal_id',
|
||||
'title',
|
||||
'episode',
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
]
|
||||
]
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testStats()
|
||||
{
|
||||
$this->get('/v4/anime/21/statistics')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
'watching',
|
||||
'completed',
|
||||
'on_hold',
|
||||
'dropped',
|
||||
'plan_to_watch',
|
||||
'total',
|
||||
'scores' => [
|
||||
[
|
||||
'score',
|
||||
'votes',
|
||||
'percentage'
|
||||
]
|
||||
]
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testForum()
|
||||
{
|
||||
$this->get('/v4/anime/1/forum')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
[
|
||||
'mal_id',
|
||||
'url',
|
||||
'title',
|
||||
'date',
|
||||
'author_username',
|
||||
'author_url',
|
||||
'comments',
|
||||
'last_comment' => [
|
||||
'url',
|
||||
'author_username',
|
||||
'author_url',
|
||||
'date'
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMoreInfo()
|
||||
{
|
||||
$this->get('/v4/anime/1/moreinfo')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
'moreinfo'
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testReviews()
|
||||
{
|
||||
$this->get('/v4/anime/1/reviews')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => [
|
||||
[
|
||||
'mal_id',
|
||||
'url',
|
||||
'type',
|
||||
'reactions',
|
||||
'date',
|
||||
'review',
|
||||
'score',
|
||||
'tags',
|
||||
'is_spoiler',
|
||||
'is_preliminary',
|
||||
'episodes_watched',
|
||||
'user' => [
|
||||
'url',
|
||||
'username',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->get('/v4/anime/1/reviews?page=100')
|
||||
->seeStatusCode(404)
|
||||
->seeJsonStructure([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
|
||||
public function testRecommendations()
|
||||
{
|
||||
$this->get('/v4/anime/1/recommendations')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
[
|
||||
'entry' => [
|
||||
'mal_id',
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'large_image_url'
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'large_image_url'
|
||||
],
|
||||
],
|
||||
'title'
|
||||
],
|
||||
'url',
|
||||
'votes',
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAnimeUserUpdates()
|
||||
{
|
||||
$this->get('/v4/anime/1/userupdates')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'pagination' => [
|
||||
'last_visible_page',
|
||||
'has_next_page',
|
||||
],
|
||||
'data' => [
|
||||
[
|
||||
'user' => [
|
||||
'username',
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
],
|
||||
'score',
|
||||
'status',
|
||||
'episodes_seen',
|
||||
'episodes_total',
|
||||
'date'
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->get('/v4/anime/1/userupdates?page=200')
|
||||
->seeStatusCode(404);
|
||||
}
|
||||
|
||||
public function testAnimeRelations()
|
||||
{
|
||||
$this->get('/v4/anime/1/relations')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
[
|
||||
'relation',
|
||||
'entry' => [
|
||||
[
|
||||
'mal_id',
|
||||
'type',
|
||||
'name',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAnimeThemes()
|
||||
{
|
||||
$this->get('/v4/anime/1/themes')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
'data' => [
|
||||
'openings',
|
||||
'endings',
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function test404()
|
||||
{
|
||||
$this->get('/v4/anime/2')
|
||||
->seeStatusCode(404);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Character;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
||||
class CharacterControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
Character::factory()->createOne([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$this->get('/v4/characters/1')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
@ -29,6 +38,20 @@ class CharacterControllerTest extends TestCase
|
||||
|
||||
public function testAnimeography()
|
||||
{
|
||||
Character::factory()->createOne([
|
||||
"mal_id" => 1,
|
||||
"animeography" => [
|
||||
[
|
||||
"role" => "Main",
|
||||
"anime" => [
|
||||
"mal_id" => 1,
|
||||
"url" => "https://myanimelist.net/anime/1/Cowboy_Bebop",
|
||||
"images" => [],
|
||||
"title" => "Cowboy Bebop"
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
$this->get('/v4/characters/1/anime')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
@ -57,6 +80,20 @@ class CharacterControllerTest extends TestCase
|
||||
|
||||
public function testMangaography()
|
||||
{
|
||||
Character::factory()->createOne([
|
||||
"mal_id" => 1,
|
||||
"mangaography" => [
|
||||
[
|
||||
"role" => "Main",
|
||||
"manga" => [
|
||||
"mal_id" => 1,
|
||||
"url" => "https://myanimelist.net/anime/1/Cowboy_Bebop",
|
||||
"images" => [],
|
||||
"title" => "Cowboy Bebop"
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
$this->get('/v4/characters/1/manga')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
@ -85,6 +122,9 @@ class CharacterControllerTest extends TestCase
|
||||
|
||||
public function testVoices()
|
||||
{
|
||||
Character::factory()->createOne([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$this->get('/v4/characters/1/voices')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
@ -106,6 +146,9 @@ class CharacterControllerTest extends TestCase
|
||||
|
||||
public function testPictures()
|
||||
{
|
||||
Character::factory()->createOne([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$this->get('/v4/characters/1/pictures')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
@ -121,6 +164,7 @@ class CharacterControllerTest extends TestCase
|
||||
|
||||
public function test404()
|
||||
{
|
||||
$this->mockJikanParserWith404RespondingUpstream();
|
||||
$this->get('/v4/characters/1000000')
|
||||
->seeStatusCode(404);
|
||||
}
|
||||
|
@ -1,13 +1,23 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use tests\TestCase;
|
||||
use App\Club;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ClubControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
$this->get('/v4/clubs/1')
|
||||
->seeStatusCode(200)
|
||||
Club::factory()->createOne([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$t = $this->get('/v4/clubs/1');
|
||||
$t->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
'mal_id',
|
||||
'name',
|
||||
@ -26,6 +36,32 @@ class ClubControllerTest extends TestCase
|
||||
|
||||
public function testMembers()
|
||||
{
|
||||
$m = Club::factory()->createOne([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$dummyUsername = $this->faker->userName();
|
||||
DB::table("clubs_members")->insert([
|
||||
// we are just copying the data from the manufactured model out of convenience
|
||||
"createdAt" => $m->createdAt,
|
||||
"modifiedAt" => $m->modifiedAt,
|
||||
"has_next_page" => false,
|
||||
"last_visible_page" => 1,
|
||||
"request_hash" => "request:clubs:".sha1("/v4/clubs/1/members"),
|
||||
"results" => [
|
||||
[
|
||||
"username" => $this->faker->userName(),
|
||||
"url" => "https://myanimelist.net/profile/".$dummyUsername,
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "http://httpbin.org/get"
|
||||
],
|
||||
"webp" => [
|
||||
"image_url" => "http://httpbin.org/get"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
$this->get('/v4/clubs/1/members')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure([
|
||||
@ -55,6 +91,7 @@ class ClubControllerTest extends TestCase
|
||||
|
||||
public function test404()
|
||||
{
|
||||
$this->mockJikanParserWith404RespondingUpstream();
|
||||
$this->get('/v4/clubs/1000000')
|
||||
->seeStatusCode(404);
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GenreControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testAnimeGenre()
|
||||
{
|
||||
$this->get('/v4/genres/anime')
|
||||
@ -34,6 +39,7 @@ class GenreControllerTest extends TestCase
|
||||
|
||||
public function test404()
|
||||
{
|
||||
$this->mockJikanParserWith404RespondingUpstream();
|
||||
$this->get('/v4/genres')
|
||||
->seeStatusCode(404);
|
||||
}
|
||||
|
@ -1,13 +1,20 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Magazine;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MagazineControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testMagazinesListing()
|
||||
{
|
||||
$this->get('/v4/magazines')
|
||||
->seeStatusCode(200)
|
||||
Magazine::factory(1)->create();
|
||||
$test = $this->get('/v4/magazines');
|
||||
$test->seeStatusCode(200)
|
||||
->seeJsonStructure(['data' => [
|
||||
[
|
||||
'mal_id',
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MangaControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
$this->get('/v4/manga/1')
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PersonControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
$this->get('/v4/people/1')
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ProducerControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testProducersListing()
|
||||
{
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecommendationsControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testAnimeRecommendations()
|
||||
{
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ReviewsControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testAnimeReviews()
|
||||
{
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ScheduleControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testSchedule()
|
||||
{
|
||||
$this->get('/v4/schedules')
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SearchControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testAnimeSearch()
|
||||
{
|
||||
$this->get('/v4/anime?order_by=id&sort=asc')
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TopControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testTopAnime()
|
||||
{
|
||||
$this->get('/v4/top/anime')
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testUserProfile()
|
||||
{
|
||||
$this->get('/v4/users/nekomata1037')
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\HttpV4\Controllers;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Tests\TestCase;
|
||||
|
||||
class WatchControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function testWatchEpisodes()
|
||||
{
|
||||
|
@ -1,261 +0,0 @@
|
||||
<?php
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\Integration;
|
||||
|
||||
use App\Anime;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AnimeControllerTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
$this->searchIndexModelCleanupList = ["App\\Anime"];
|
||||
}
|
||||
|
||||
private function givenDummyCharactersStaffData($uri)
|
||||
{
|
||||
DB::table("anime_characters_staff")->insert([
|
||||
"createdAt" => new UTCDateTime(),
|
||||
"modifiedAt" => new UTCDateTime(),
|
||||
"request_hash" => "request:anime:" . sha1($uri),
|
||||
"characters" => [
|
||||
[
|
||||
"character" => [
|
||||
"mal_id" => 3,
|
||||
"url" => "https://myanimelist.net/character/3/Jet_Black",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/11/253723.jpg?s=6c8a19a79a88c46ae15f30e3ef5fd839",
|
||||
"small_image_url" => "https://cdn.myanimelist.net/images/characters/11/253723t.jpg?s=6c8a19a79a88c46ae15f30e3ef5fd839"
|
||||
],
|
||||
"webp" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/characters/11/253723.webp?s=6c8a19a79a88c46ae15f30e3ef5fd839",
|
||||
"small_image_url" => "https://cdn.myanimelist.net/images/characters/11/253723t.webp?s=6c8a19a79a88c46ae15f30e3ef5fd839"
|
||||
]
|
||||
],
|
||||
"name" => "Black, Jet"
|
||||
],
|
||||
"role" => "Main",
|
||||
"favorites" => 1,
|
||||
"voice_actors" => [
|
||||
[
|
||||
"person" => [
|
||||
"mal_id" => 357,
|
||||
"url" => "https://myanimelist.net/people/357/Unshou_Ishizuk",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/voiceactors/2/17135.jpg?s=5925123b8a7cf9b51a445c225442f0ef"
|
||||
]
|
||||
],
|
||||
"name" => "Ishizuka, Unshou"
|
||||
],
|
||||
"language" => "Japanese"
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
"staff" => [
|
||||
[
|
||||
"person" => [
|
||||
"mal_id" => 40009,
|
||||
"url" => "https://myanimelist.net/people/40009/Yutaka_Maseba",
|
||||
"images" => [
|
||||
"jpg" => [
|
||||
"image_url" => "https://cdn.myanimelist.net/images/voiceactors/3/40216.jpg?s=d9fb7a625868ec7d9cd3804fa0da3fd6"
|
||||
]
|
||||
],
|
||||
"name" => "Maseba, Yutaka"
|
||||
],
|
||||
"positions" => [
|
||||
"Producer"
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMain()
|
||||
{
|
||||
Anime::factory(1)->create([
|
||||
"mal_id" => 1
|
||||
]);
|
||||
$this->getJson('/v4/anime/1')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data' => [
|
||||
'mal_id',
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'large_image_url'
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'large_image_url'
|
||||
],
|
||||
],
|
||||
'trailer' => [
|
||||
'youtube_id',
|
||||
'url',
|
||||
'embed_url',
|
||||
'images' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
'medium_image_url',
|
||||
'large_image_url',
|
||||
'maximum_image_url',
|
||||
]
|
||||
],
|
||||
'title',
|
||||
'title_english',
|
||||
'title_japanese',
|
||||
'title_synonyms',
|
||||
'type',
|
||||
'source',
|
||||
'episodes',
|
||||
'status',
|
||||
'airing',
|
||||
'aired' => [
|
||||
'from',
|
||||
'to',
|
||||
'prop' => [
|
||||
'from' => [
|
||||
'day',
|
||||
'month',
|
||||
'year'
|
||||
],
|
||||
'to' => [
|
||||
'day',
|
||||
'month',
|
||||
'year'
|
||||
]
|
||||
],
|
||||
'string'
|
||||
],
|
||||
'duration',
|
||||
'rating',
|
||||
'score',
|
||||
'scored_by',
|
||||
'rank',
|
||||
'popularity',
|
||||
'members',
|
||||
'favorites',
|
||||
'synopsis',
|
||||
'background',
|
||||
'season',
|
||||
'year',
|
||||
'broadcast' => [
|
||||
'day',
|
||||
'time',
|
||||
'timezone',
|
||||
'string'
|
||||
],
|
||||
'producers' => [
|
||||
[
|
||||
'mal_id',
|
||||
'type',
|
||||
'name',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
'licensors' => [
|
||||
[
|
||||
'mal_id',
|
||||
'type',
|
||||
'name',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
'studios' => [
|
||||
[
|
||||
'mal_id',
|
||||
'type',
|
||||
'name',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
'genres' => [
|
||||
[
|
||||
'mal_id',
|
||||
'type',
|
||||
'name',
|
||||
'url'
|
||||
]
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testCharacters()
|
||||
{
|
||||
// let's avoid sending request to MAL in tests
|
||||
$this->givenDummyCharactersStaffData("/v4/anime/1/characters");
|
||||
|
||||
$this->getJson('/v4/anime/1/characters')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data' => [
|
||||
[
|
||||
'character' => [
|
||||
'mal_id',
|
||||
'url',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
],
|
||||
'webp' => [
|
||||
'image_url',
|
||||
'small_image_url',
|
||||
],
|
||||
],
|
||||
'name',
|
||||
],
|
||||
'role',
|
||||
'voice_actors' => [
|
||||
[
|
||||
'person' => [
|
||||
'mal_id',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
'name'
|
||||
],
|
||||
'language'
|
||||
]
|
||||
]
|
||||
]
|
||||
]]);
|
||||
}
|
||||
|
||||
public function testStaff()
|
||||
{
|
||||
$this->givenDummyCharactersStaffData('/v4/anime/1/staff');
|
||||
$this->getJson('/v4/anime/1/staff')
|
||||
->seeStatusCode(200)
|
||||
->seeJsonStructure(['data'=>[
|
||||
[
|
||||
'person' => [
|
||||
'mal_id',
|
||||
'images' => [
|
||||
'jpg' => [
|
||||
'image_url',
|
||||
],
|
||||
],
|
||||
'name'
|
||||
],
|
||||
'positions'
|
||||
]
|
||||
]]);
|
||||
}
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
|
||||
namespace Tests;
|
||||
use Illuminate\Support\Str;
|
||||
use \Throwable;
|
||||
use PHPUnit\Framework\TestListener;
|
||||
|
||||
// fixme: with phpunit 10, this should be replaced with the new event system
|
||||
class IntegrationTestListener implements TestListener
|
||||
{
|
||||
private $app;
|
||||
private \Laravel\Lumen\Application $app;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$app = require __DIR__.'/../bootstrap/app.php';
|
||||
$database = env('DB_DATABASE', 'jikan_tests');
|
||||
$app['config']->set('database.connections.mongodb.database', $database === 'jikan' ? 'jikan_tests' : $database);
|
||||
$app['config']->set('jikan.micro_caching_enabled', false);
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
@ -41,10 +43,18 @@ class IntegrationTestListener implements TestListener
|
||||
{
|
||||
}
|
||||
|
||||
private function isIntegrationTest(\PHPUnit\Framework\TestSuite $suite): bool
|
||||
{
|
||||
$suiteName = $suite->getName();
|
||||
return in_array($suiteName, [
|
||||
"integration", "http-integration", "Tests\HttpV4\Controllers", "Tests\Integration",
|
||||
"Integration"
|
||||
]);
|
||||
}
|
||||
|
||||
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite): void
|
||||
{
|
||||
echo $suite->getName();
|
||||
if ($suite->getName() == "integration") {
|
||||
if ($this->isIntegrationTest($suite)) {
|
||||
$app = $this->app;
|
||||
$kernel = $app->make(
|
||||
'Illuminate\Contracts\Console\Kernel'
|
||||
@ -53,13 +63,14 @@ class IntegrationTestListener implements TestListener
|
||||
$kernel->call('migrate:fresh', []);
|
||||
} catch (\Exception $ex) {
|
||||
print_r($ex->getMessage());
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function endTestSuite(\PHPUnit\Framework\TestSuite $suite): void
|
||||
{
|
||||
if ($suite->getName() == "integration") {
|
||||
if ($this->isIntegrationTest($suite)) {
|
||||
$app = $this->app;
|
||||
$kernel = $app->make(
|
||||
'Illuminate\Contracts\Console\Kernel'
|
||||
@ -70,11 +81,9 @@ class IntegrationTestListener implements TestListener
|
||||
|
||||
public function startTest(\PHPUnit\Framework\Test $test): void
|
||||
{
|
||||
// TODO: Implement startTest() method.
|
||||
}
|
||||
|
||||
public function endTest(\PHPUnit\Framework\Test $test, float $time): void
|
||||
{
|
||||
// TODO: Implement endTest() method.
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,11 @@ use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Jikan\MyAnimeList\MalClient;
|
||||
use Laravel\Lumen\Testing\TestCase as LumenTestCase;
|
||||
use Spatie\Enum\Faker\FakerEnumProvider;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
abstract class TestCase extends LumenTestCase
|
||||
{
|
||||
@ -34,6 +37,7 @@ abstract class TestCase extends LumenTestCase
|
||||
$app = require __DIR__.'/../bootstrap/app.php';
|
||||
$database = env('DB_DATABASE', 'jikan_tests');
|
||||
$app['config']->set('database.connections.mongodb.database', $database === 'jikan' ? 'jikan_tests' : $database);
|
||||
$app['config']->set('jikan.micro_caching_enabled', false);
|
||||
$app->register(TestServiceProvider::class);
|
||||
|
||||
return $app;
|
||||
@ -78,4 +82,18 @@ abstract class TestCase extends LumenTestCase
|
||||
$this->assertEquals(0, $expectedItems->diff($actualItems)->count());
|
||||
$this->assertEquals($expectedItems->toArray(), $actualItems->toArray());
|
||||
}
|
||||
|
||||
protected function mockJikanParserWith404RespondingUpstream()
|
||||
{
|
||||
$httpClient = \Mockery::mock(HttpClientInterface::class);
|
||||
$response = \Mockery::mock(ResponseInterface::class);
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$httpClient->allows()->request(\Mockery::any(), \Mockery::any(), \Mockery::any())->andReturn($response);
|
||||
$response->allows([
|
||||
"getStatusCode" => 404,
|
||||
"getHeaders" => [],
|
||||
"getContent" => ""
|
||||
]);
|
||||
$this->app->instance("JikanParser", new MalClient($httpClient));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ final class DefaultCachedScraperServiceTest extends TestCase
|
||||
{
|
||||
public function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
// reset time pinning
|
||||
Carbon::setTestNow();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user