mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
added manga search endpoint integration tests
This commit is contained in:
parent
cefdbb1a1d
commit
950b5c6a2d
1390
composer.lock
generated
1390
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,16 +2,12 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\CarbonDateRange;
|
||||
use App\GenreAnime;
|
||||
use App\Anime;
|
||||
use App\Http\QueryBuilder\AnimeSearchQueryBuilder;
|
||||
use App\Testing\JikanDataGenerator;
|
||||
use Illuminate\Support\Collection;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
|
||||
class AnimeFactory extends JikanModelFactory
|
||||
class AnimeFactory extends JikanMediaModelFactory
|
||||
{
|
||||
use JikanDataGenerator;
|
||||
|
||||
@ -106,399 +102,4 @@ class AnimeFactory extends JikanModelFactory
|
||||
"request_hash" => sprintf("request:%s:%s", "v4", $this->getItemTestUrl("anime", $mal_id))
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for overriding fields of the model factory based on query string parameters.
|
||||
*
|
||||
* @param array $additionalParams
|
||||
* @param bool $doOpposite
|
||||
* @return self
|
||||
*/
|
||||
public function overrideFromQueryStringParameters(array $additionalParams, bool $doOpposite = false): self
|
||||
{
|
||||
$additionalParams = collect($additionalParams);
|
||||
|
||||
if ($doOpposite) {
|
||||
$overrides = $this->getOppositeOverridesFromQueryStringParameters($additionalParams);
|
||||
}
|
||||
else {
|
||||
$overrides = $this->getOverridesFromQueryStringParameters($additionalParams);
|
||||
}
|
||||
|
||||
return $this->state($this->serializeStateDefinition($overrides));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create records in db in sequential order.
|
||||
* The records are created with an increasing value for the specified field.
|
||||
* @param string $orderByField The field to order items by. Used to generate increasing value for the field.
|
||||
* @return Collection The created items/records
|
||||
*/
|
||||
public function createManyWithOrder(string $orderByField): Collection
|
||||
{
|
||||
$count = $this->count ?? 3;
|
||||
$items = collect();
|
||||
|
||||
$fieldValueGenerator = match($orderByField) {
|
||||
"aired.from", "aired.to" => ((function() {
|
||||
$randomDate = $this->createRandomDateTime("-5 years");
|
||||
return fn($i) => $randomDate->copy()->addDays($i);
|
||||
})()),
|
||||
"rating" => ((function() {
|
||||
$validRatingItems = array_values(AnimeSearchQueryBuilder::MAP_RATING);
|
||||
$validRatingItemsCount = count($validRatingItems);
|
||||
return fn($i) => $validRatingItems[$i % $validRatingItemsCount];
|
||||
})()),
|
||||
"title" => ((function() {
|
||||
$alphabet = range("a", "z");
|
||||
$alphabetCount = count($alphabet);
|
||||
return fn($i) => $alphabet[$i % $alphabetCount];
|
||||
})()),
|
||||
"type" => ((function() {
|
||||
$types = array_values(AnimeSearchQueryBuilder::MAP_TYPES);
|
||||
$typesCount = count($types);
|
||||
return fn($i) => $types[$i % $typesCount];
|
||||
})()),
|
||||
default => fn($i) => $i,
|
||||
};
|
||||
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
if ($orderByField === "aired.from") {
|
||||
$createdItem = $this->createOne($this->serializeStateDefinition([
|
||||
"aired" => new CarbonDateRange($fieldValueGenerator($i), null)
|
||||
]));
|
||||
} else if ($orderByField === "aired.to") {
|
||||
$createdItem = $this->createOne($this->serializeStateDefinition([
|
||||
"aired" => new CarbonDateRange(null, $fieldValueGenerator($i))
|
||||
]));
|
||||
} else {
|
||||
$createdItem = $this->createOne([
|
||||
$orderByField => $fieldValueGenerator($i)
|
||||
]);
|
||||
}
|
||||
$items->add($createdItem);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function getRandomRating(): string
|
||||
{
|
||||
return $this->faker->randomElement([
|
||||
"G - All Ages",
|
||||
"PG - Children",
|
||||
"PG-13 - Teens 13 or older",
|
||||
"R - 17+ (violence & profanity)",
|
||||
"R+ - Mild Nudity",
|
||||
"Rx - Hentai"
|
||||
]);
|
||||
}
|
||||
|
||||
private function isScoreValueValid($score): bool
|
||||
{
|
||||
return $score <= 9.99 && $score >= 0.0;
|
||||
}
|
||||
|
||||
private function getOppositeOverridesFromQueryStringParameters(Collection $additionalParams): array
|
||||
{
|
||||
$overrides = [];
|
||||
|
||||
if ($additionalParams->has("type")) {
|
||||
$types = [
|
||||
"ova" => "OVA",
|
||||
"movie" => "Movie",
|
||||
"tv" => "TV"
|
||||
];
|
||||
$overrides["type"] = $this->faker->randomElement(array_diff(array_keys($types), [$additionalParams["type"]]));
|
||||
}
|
||||
|
||||
if ($additionalParams->has("letter")) {
|
||||
$alphabet = array_filter(range("a", "z"), fn ($elem) => $elem !== $additionalParams["letter"]);
|
||||
$title = $this->faker->randomElement($alphabet) . $this->createTitle();
|
||||
$a = [
|
||||
"titles" => [
|
||||
[
|
||||
"type" => "Default",
|
||||
"title" => $title
|
||||
]
|
||||
],
|
||||
"title" => $title,
|
||||
"title_english" => $title,
|
||||
"title_japanese" => $title,
|
||||
"title_synonyms" => [$title],
|
||||
];
|
||||
$overrides = [...$overrides, ...$a];
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
if ($this->isScoreValueValid($min_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$max_score = $additionalParams["max_score"];
|
||||
if ($this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99);
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($min_score) && $this->isScoreValueValid($max_score))
|
||||
{
|
||||
$overrides["score"] = $this->faker->randomElement([
|
||||
$this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"])),
|
||||
$this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("status")) {
|
||||
$statuses = [
|
||||
"complete" => "Finished Airing",
|
||||
"airing" => "Currently Airing",
|
||||
"upcoming" => "Not yet aired"
|
||||
];
|
||||
|
||||
$rndKey = $this->faker->randomElement(array_diff(array_keys($statuses), [strtolower($additionalParams["status"])]));
|
||||
$overrides["status"] = $statuses[$rndKey];
|
||||
}
|
||||
|
||||
if ($additionalParams->has("rating")) {
|
||||
$ratings = [
|
||||
"g" => "G - All Ages",
|
||||
"pg" => "PG - Children",
|
||||
"pg13" => "PG-13 - Teens 13 or older",
|
||||
"r17" => "R - 17+ (violence & profanity)",
|
||||
"r" => "R+ - Mild Nudity",
|
||||
"rx" => "Rx - Hentai",
|
||||
];
|
||||
$rndKey = $this->faker->randomElement(array_diff(array_keys($ratings), [strtolower($additionalParams["rating"])]));
|
||||
$overrides["rating"] = $ratings[$rndKey];
|
||||
}
|
||||
|
||||
if (($additionalParams->has("genres") && $additionalParams->has("genres_exclude")) || (
|
||||
!$additionalParams->has("genres") && $additionalParams->has("genres_exclude")
|
||||
) ) {
|
||||
$overrides["genres"] = [];
|
||||
// use the "genres_exclude" values to add genres to the anime item
|
||||
$genreIds = explode(",", $additionalParams["genres_exclude"]);
|
||||
if (count($genreIds) > 1) {
|
||||
$genreId = $this->faker->randomElement($genreIds);
|
||||
} else {
|
||||
$genreId = $genreIds[0];
|
||||
}
|
||||
|
||||
$m = $this->ensureGenreExists($genreId);
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => "anime",
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
} else if ($additionalParams->has("genres")) {
|
||||
$overrides["genres"] = [];
|
||||
// add such genres which are not in the "genres" param
|
||||
$genreIds = explode(",", $additionalParams["genres"]);
|
||||
$numberOfGenresToAdd = $this->faker->numberBetween(0, 4);
|
||||
for ($numberOfGenresAdded = 0; $numberOfGenresAdded <= $numberOfGenresToAdd; $numberOfGenresAdded++) {
|
||||
$outboundsGenreId = $this->faker->numberBetween(0, 74);
|
||||
while (in_array($outboundsGenreId, $genreIds)) {
|
||||
$outboundsGenreId = $this->faker->numberBetween(0, 74);
|
||||
}
|
||||
|
||||
$m = $this->ensureGenreExists($outboundsGenreId);
|
||||
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => "anime",
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
|
||||
&& !$additionalParams->has("end_date")) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$dt = Carbon::parse($startDate)->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides["aired"] = new CarbonDateRange($dt, null);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
|
||||
&& !$additionalParams->has("start_date")) {
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate)->addDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides["aired"] = new CarbonDateRange($from, $to);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && $additionalParams->has("end_date")
|
||||
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
|
||||
$originalFrom = Carbon::parse($this->adaptDateString($additionalParams["start_date"]));
|
||||
$originalTo = Carbon::parse($this->adaptDateString($additionalParams["end_date"]));
|
||||
$interval = $originalTo->diff($originalFrom);
|
||||
$afterOrBefore = $this->faker->randomElement(["after", "before"]);
|
||||
|
||||
$randomDayIntervalValue = $this->faker->numberBetween(8, 90) + $interval->days;
|
||||
|
||||
[$artificialFrom, $artificialTo] = match ($afterOrBefore) {
|
||||
"after" => [$originalFrom->addDays($randomDayIntervalValue), $originalTo->addDays($randomDayIntervalValue)],
|
||||
"before" => [$originalFrom->subDays($randomDayIntervalValue), $originalTo->subDays($randomDayIntervalValue)]
|
||||
};
|
||||
|
||||
$overrides["aired"] = new CarbonDateRange($artificialFrom, $artificialTo);
|
||||
}
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
private function getOverridesFromQueryStringParameters(Collection $additionalParams): array
|
||||
{
|
||||
$overrides = [];
|
||||
// let's make all database items the same type
|
||||
if ($additionalParams->has("type")) {
|
||||
$overrides["type"] = match ($additionalParams["type"]) {
|
||||
"ova" => "OVA",
|
||||
"movie" => "Movie",
|
||||
default => "TV"
|
||||
};
|
||||
}
|
||||
|
||||
if ($additionalParams->has("letter")) {
|
||||
$title = $additionalParams["letter"] . $this->createTitle();
|
||||
$a = [
|
||||
"titles" => [
|
||||
[
|
||||
"type" => "Default",
|
||||
"title" => $title
|
||||
]
|
||||
],
|
||||
"title" => $title,
|
||||
"title_english" => $title,
|
||||
"title_japanese" => $title,
|
||||
"title_synonyms" => [$title],
|
||||
];
|
||||
$overrides = [...$overrides, ...$a];
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
if ($this->isScoreValueValid($min_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), 9.99);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["max_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has(["min_score", "max_score"])) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($min_score) && $this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), floatval($additionalParams["max_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("status")) {
|
||||
$overrides["status"] = match (strtolower($additionalParams["status"])) {
|
||||
"complete" => "Finished Airing",
|
||||
"airing" => "Currently Airing",
|
||||
"upcoming" => "Not yet aired",
|
||||
default => $this->faker->randomElement([
|
||||
"Finished Airing",
|
||||
"Currently Airing",
|
||||
"Not yet aired"
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
if ($additionalParams->has("rating")) {
|
||||
$overrides["rating"] = match (strtolower($additionalParams["rating"])) {
|
||||
"g" => "G - All Ages",
|
||||
"pg" => "PG - Children",
|
||||
"pg13" => "PG-13 - Teens 13 or older",
|
||||
"r17" => "R - 17+ (violence & profanity)",
|
||||
"r" => "R+ - Mild Nudity",
|
||||
"rx" => "Rx - Hentai",
|
||||
default => $this->getRandomRating()
|
||||
};
|
||||
}
|
||||
|
||||
if ($additionalParams->has("genres")) {
|
||||
$overrides["genres"] = [];
|
||||
$genreIds = explode(",", $additionalParams["genres"]);
|
||||
$genreIds = $this->faker->randomElements($genreIds, $this->faker->numberBetween(0, count($genreIds)));
|
||||
foreach ($genreIds as $genreId) {
|
||||
$m = $this->ensureGenreExists($genreId);
|
||||
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => "anime",
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
|
||||
&& !$additionalParams->has("end_date")) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$dt = Carbon::parse($startDate)->addDays($this->faker->numberBetween(0, 25));
|
||||
$overrides["aired"] = new CarbonDateRange($dt, null);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
|
||||
&& !$additionalParams->has("start_date")) {
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate);
|
||||
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides["aired"] = new CarbonDateRange($from, $to->subDays($this->faker->numberBetween(0, 25)));
|
||||
}
|
||||
|
||||
if ($additionalParams->has(["start_date", "end_date"])
|
||||
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$from = Carbon::parse($startDate);
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate);
|
||||
|
||||
$overrides["aired"] = new CarbonDateRange($from, $to);
|
||||
}
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
private function ensureGenreExists(int $genreId): GenreAnime
|
||||
{
|
||||
$m = GenreAnime::query()->firstWhere("mal_id", $genreId);
|
||||
if ($m == null) {
|
||||
$f = GenreAnime::factory();
|
||||
$m = $f->createOne([
|
||||
"mal_id" => $genreId
|
||||
]);
|
||||
}
|
||||
|
||||
return $m;
|
||||
}
|
||||
|
||||
private function adaptDateString($dateStr): string
|
||||
{
|
||||
$parts = explode("-", $dateStr);
|
||||
if (count($parts) === 1) {
|
||||
return $parts[0] . "-01-01";
|
||||
}
|
||||
|
||||
return $dateStr;
|
||||
}
|
||||
}
|
||||
|
46
database/factories/AnimeModelFactoryDescriptor.php
Normal file
46
database/factories/AnimeModelFactoryDescriptor.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\GenreAnime;
|
||||
use App\Http\QueryBuilder\AnimeSearchQueryBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class AnimeModelFactoryDescriptor implements MediaModelFactoryDescriptor
|
||||
{
|
||||
public function activityMarkerKeyName(): string
|
||||
{
|
||||
return "aired";
|
||||
}
|
||||
|
||||
public function typeParamMap(): array
|
||||
{
|
||||
return AnimeSearchQueryBuilder::MAP_TYPES;
|
||||
}
|
||||
|
||||
public function statusParamMap(): array
|
||||
{
|
||||
return AnimeSearchQueryBuilder::MAP_STATUS;
|
||||
}
|
||||
|
||||
public function hasRatingParam(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mediaName(): string
|
||||
{
|
||||
return "anime";
|
||||
}
|
||||
|
||||
public function genreQueryBuilder(): Builder
|
||||
{
|
||||
return GenreAnime::query();
|
||||
}
|
||||
|
||||
public function genreFactory(?int $count = null, $state = []): Factory
|
||||
{
|
||||
return GenreAnime::factory($count, $state);
|
||||
}
|
||||
}
|
430
database/factories/JikanMediaModelFactory.php
Normal file
430
database/factories/JikanMediaModelFactory.php
Normal file
@ -0,0 +1,430 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\CarbonDateRange;
|
||||
use App\Http\QueryBuilder\AnimeSearchQueryBuilder;
|
||||
use App\Testing\JikanDataGenerator;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
abstract class JikanMediaModelFactory extends JikanModelFactory implements MediaModelFactory
|
||||
{
|
||||
use JikanDataGenerator;
|
||||
|
||||
protected ?MediaModelFactoryDescriptor $descriptor;
|
||||
|
||||
protected function configureInternal(): self
|
||||
{
|
||||
// contextual binding / service location
|
||||
App::call([$this, "withMediaModelFactoryDescriptor"]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function configure()
|
||||
{
|
||||
return $this->configureInternal();
|
||||
}
|
||||
|
||||
protected function newInstance(array $arguments = []): self
|
||||
{
|
||||
return parent::newInstance($arguments)->configureInternal();
|
||||
}
|
||||
|
||||
public function withMediaModelFactoryDescriptor(MediaModelFactoryDescriptor $descriptor): void
|
||||
{
|
||||
$this->descriptor = $descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for overriding fields of the model factory based on query string parameters.
|
||||
*
|
||||
* @param array $additionalParams
|
||||
* @param bool $doOpposite
|
||||
* @return self
|
||||
*/
|
||||
public function overrideFromQueryStringParameters(array $additionalParams, bool $doOpposite = false): self
|
||||
{
|
||||
$additionalParams = collect($additionalParams);
|
||||
|
||||
if ($doOpposite) {
|
||||
$overrides = $this->getOppositeOverridesFromQueryStringParameters($additionalParams);
|
||||
}
|
||||
else {
|
||||
$overrides = $this->getOverridesFromQueryStringParameters($additionalParams);
|
||||
}
|
||||
|
||||
return $this->state($this->serializeStateDefinition($overrides));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create records in db in sequential order.
|
||||
* The records are created with an increasing value for the specified field.
|
||||
* @param string $orderByField The field to order items by. Used to generate increasing value for the field.
|
||||
* @return Collection The created items/records
|
||||
*/
|
||||
public function createManyWithOrder(string $orderByField): Collection
|
||||
{
|
||||
$count = $this->count ?? 3;
|
||||
$items = collect();
|
||||
|
||||
$activityMarkerKeyName = $this->descriptor->activityMarkerKeyName();
|
||||
$fromActivityMarkerKeyPath = "$activityMarkerKeyName.from";
|
||||
$toActivityMarkerKeyPath = "$activityMarkerKeyName.to";
|
||||
|
||||
$fieldValueGenerator = match($orderByField) {
|
||||
$fromActivityMarkerKeyPath, $toActivityMarkerKeyPath => ((function() {
|
||||
$randomDate = $this->createRandomDateTime("-5 years");
|
||||
return fn($i) => $randomDate->copy()->addDays($i);
|
||||
})()),
|
||||
"rating" => ((function() {
|
||||
$validRatingItems = array_values(AnimeSearchQueryBuilder::MAP_RATING);
|
||||
$validRatingItemsCount = count($validRatingItems);
|
||||
return fn($i) => $validRatingItems[$i % $validRatingItemsCount];
|
||||
})()),
|
||||
"title" => ((function() {
|
||||
$alphabet = range("a", "z");
|
||||
$alphabetCount = count($alphabet);
|
||||
return fn($i) => $alphabet[$i % $alphabetCount];
|
||||
})()),
|
||||
"type" => ((function() {
|
||||
$types = array_values(AnimeSearchQueryBuilder::MAP_TYPES);
|
||||
$typesCount = count($types);
|
||||
return fn($i) => $types[$i % $typesCount];
|
||||
})()),
|
||||
default => fn($i) => $i,
|
||||
};
|
||||
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
if ($orderByField === $fromActivityMarkerKeyPath) {
|
||||
$createdItem = $this->createOne($this->serializeStateDefinition([
|
||||
$activityMarkerKeyName => new CarbonDateRange($fieldValueGenerator($i), null)
|
||||
]));
|
||||
} else if ($orderByField === $toActivityMarkerKeyPath) {
|
||||
$createdItem = $this->createOne($this->serializeStateDefinition([
|
||||
$activityMarkerKeyName => new CarbonDateRange(null, $fieldValueGenerator($i))
|
||||
]));
|
||||
} else {
|
||||
$createdItem = $this->createOne([
|
||||
$orderByField => $fieldValueGenerator($i)
|
||||
]);
|
||||
}
|
||||
$items->add($createdItem);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
protected function getOverridesFromQueryStringParameters(Collection $additionalParams): array
|
||||
{
|
||||
$overrides = [];
|
||||
$activityMarkerKeyName = $this->descriptor->activityMarkerKeyName();
|
||||
// let's make all database items the same type
|
||||
if ($additionalParams->has("type")) {
|
||||
$typeOverride = collect($this->descriptor->typeParamMap())->get(strtolower($additionalParams["type"]));
|
||||
if (!is_null($typeOverride)) {
|
||||
$overrides["type"] = $typeOverride;
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("letter")) {
|
||||
$title = $additionalParams["letter"] . $this->createTitle();
|
||||
$a = [
|
||||
"titles" => [
|
||||
[
|
||||
"type" => "Default",
|
||||
"title" => $title
|
||||
]
|
||||
],
|
||||
"title" => $title,
|
||||
"title_english" => $title,
|
||||
"title_japanese" => $title,
|
||||
"title_synonyms" => [$title],
|
||||
];
|
||||
$overrides = [...$overrides, ...$a];
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
if ($this->isScoreValueValid($min_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), 9.99);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["max_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has(["min_score", "max_score"])) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($min_score) && $this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["min_score"]), floatval($additionalParams["max_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("status")) {
|
||||
$statusParamMap = $this->descriptor->statusParamMap();
|
||||
$statusOverride = collect($statusParamMap)->get(strtolower($additionalParams["status"]));
|
||||
if (!is_null($statusOverride)) {
|
||||
$overrides["status"] = $statusOverride;
|
||||
}
|
||||
else {
|
||||
$overrides["status"] = $this->faker->randomElement(array_values($statusParamMap));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->descriptor->hasRatingParam() && $additionalParams->has("rating")) {
|
||||
$overrides["rating"] = match (strtolower($additionalParams["rating"])) {
|
||||
"g" => "G - All Ages",
|
||||
"pg" => "PG - Children",
|
||||
"pg13" => "PG-13 - Teens 13 or older",
|
||||
"r17" => "R - 17+ (violence & profanity)",
|
||||
"r" => "R+ - Mild Nudity",
|
||||
"rx" => "Rx - Hentai",
|
||||
default => $this->getRandomRating()
|
||||
};
|
||||
}
|
||||
|
||||
if ($additionalParams->has("genres")) {
|
||||
$overrides["genres"] = [];
|
||||
$genreIds = explode(",", $additionalParams["genres"]);
|
||||
$genreIds = $this->faker->randomElements($genreIds, $this->faker->numberBetween(0, count($genreIds)));
|
||||
foreach ($genreIds as $genreId) {
|
||||
$m = $this->ensureGenreExists($genreId);
|
||||
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => $this->descriptor->mediaName(),
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
|
||||
&& !$additionalParams->has("end_date")) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$dt = Carbon::parse($startDate)->addDays($this->faker->numberBetween(0, 25));
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($dt, null);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
|
||||
&& !$additionalParams->has("start_date")) {
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate);
|
||||
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($from, $to->subDays($this->faker->numberBetween(0, 25)));
|
||||
}
|
||||
|
||||
if ($additionalParams->has(["start_date", "end_date"])
|
||||
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$from = Carbon::parse($startDate);
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate);
|
||||
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($from, $to);
|
||||
}
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
protected function getOppositeOverridesFromQueryStringParameters(Collection $additionalParams): array
|
||||
{
|
||||
$overrides = [];
|
||||
$activityMarkerKeyName = $this->descriptor->activityMarkerKeyName();
|
||||
|
||||
if ($additionalParams->has("type")) {
|
||||
$types = $this->descriptor->typeParamMap();
|
||||
$overrides["type"] = $this->faker->randomElement(array_diff(array_keys($types), [$additionalParams["type"]]));
|
||||
}
|
||||
|
||||
if ($additionalParams->has("letter")) {
|
||||
$alphabet = array_filter(range("a", "z"), fn ($elem) => $elem !== $additionalParams["letter"]);
|
||||
$title = $this->faker->randomElement($alphabet) . $this->createTitle();
|
||||
$a = [
|
||||
"titles" => [
|
||||
[
|
||||
"type" => "Default",
|
||||
"title" => $title
|
||||
]
|
||||
],
|
||||
"title" => $title,
|
||||
"title_english" => $title,
|
||||
"title_japanese" => $title,
|
||||
"title_synonyms" => [$title],
|
||||
];
|
||||
$overrides = [...$overrides, ...$a];
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && !$additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
if ($this->isScoreValueValid($min_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"]));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$max_score = $additionalParams["max_score"];
|
||||
if ($this->isScoreValueValid($max_score)) {
|
||||
$overrides["score"] = $this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99);
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("min_score") && $additionalParams->has("max_score")) {
|
||||
$min_score = floatval($additionalParams["min_score"]);
|
||||
$max_score = floatval($additionalParams["max_score"]);
|
||||
|
||||
if ($this->isScoreValueValid($min_score) && $this->isScoreValueValid($max_score))
|
||||
{
|
||||
$overrides["score"] = $this->faker->randomElement([
|
||||
$this->faker->randomFloat(2, 1.00, floatval($additionalParams["min_score"])),
|
||||
$this->faker->randomFloat(2, floatval($additionalParams["max_score"]), 9.99)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("status")) {
|
||||
$statuses = $this->descriptor->statusParamMap();
|
||||
|
||||
$rndKey = $this->faker->randomElement(array_diff(array_keys($statuses), [strtolower($additionalParams["status"])]));
|
||||
$overrides["status"] = $statuses[$rndKey];
|
||||
}
|
||||
|
||||
if ($this->descriptor->hasRatingParam() && $additionalParams->has("rating")) {
|
||||
$ratings = [
|
||||
"g" => "G - All Ages",
|
||||
"pg" => "PG - Children",
|
||||
"pg13" => "PG-13 - Teens 13 or older",
|
||||
"r17" => "R - 17+ (violence & profanity)",
|
||||
"r" => "R+ - Mild Nudity",
|
||||
"rx" => "Rx - Hentai",
|
||||
];
|
||||
$rndKey = $this->faker->randomElement(array_diff(array_keys($ratings), [strtolower($additionalParams["rating"])]));
|
||||
$overrides["rating"] = $ratings[$rndKey];
|
||||
}
|
||||
|
||||
if (($additionalParams->has("genres") && $additionalParams->has("genres_exclude")) || (
|
||||
!$additionalParams->has("genres") && $additionalParams->has("genres_exclude")
|
||||
) ) {
|
||||
$overrides["genres"] = [];
|
||||
// use the "genres_exclude" values to add genres to the anime item
|
||||
$genreIds = explode(",", $additionalParams["genres_exclude"]);
|
||||
if (count($genreIds) > 1) {
|
||||
$genreId = $this->faker->randomElement($genreIds);
|
||||
} else {
|
||||
$genreId = $genreIds[0];
|
||||
}
|
||||
|
||||
$m = $this->ensureGenreExists($genreId);
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => $this->descriptor->mediaName(),
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
} else if ($additionalParams->has("genres")) {
|
||||
$overrides["genres"] = [];
|
||||
// add such genres which are not in the "genres" param
|
||||
$genreIds = explode(",", $additionalParams["genres"]);
|
||||
$numberOfGenresToAdd = $this->faker->numberBetween(0, 4);
|
||||
for ($numberOfGenresAdded = 0; $numberOfGenresAdded <= $numberOfGenresToAdd; $numberOfGenresAdded++) {
|
||||
$outboundsGenreId = $this->faker->numberBetween(0, 74);
|
||||
while (in_array($outboundsGenreId, $genreIds)) {
|
||||
$outboundsGenreId = $this->faker->numberBetween(0, 74);
|
||||
}
|
||||
|
||||
$m = $this->ensureGenreExists($outboundsGenreId);
|
||||
|
||||
$overrides["genres"][] = [
|
||||
"mal_id" => $m->mal_id,
|
||||
"type" => $this->descriptor->mediaName(),
|
||||
"name" => $m->name,
|
||||
"url" => $m->url
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && !empty($additionalParams["start_date"])
|
||||
&& !$additionalParams->has("end_date")) {
|
||||
$startDate = $this->adaptDateString($additionalParams["start_date"]);
|
||||
$dt = Carbon::parse($startDate)->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($dt, null);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("end_date") && !empty($additionalParams["end_date"])
|
||||
&& !$additionalParams->has("start_date")) {
|
||||
$endDate = $this->adaptDateString($additionalParams["end_date"]);
|
||||
$to = Carbon::parse($endDate)->addDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$from = $to->copy()->subDays($this->faker->randomElement([30, 60, 90, 120, 180]));
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($from, $to);
|
||||
}
|
||||
|
||||
if ($additionalParams->has("start_date") && $additionalParams->has("end_date")
|
||||
&& !empty($additionalParams["start_date"]) && !empty($additionalParams["end_date"])) {
|
||||
$originalFrom = Carbon::parse($this->adaptDateString($additionalParams["start_date"]));
|
||||
$originalTo = Carbon::parse($this->adaptDateString($additionalParams["end_date"]));
|
||||
$interval = $originalTo->diff($originalFrom);
|
||||
$afterOrBefore = $this->faker->randomElement(["after", "before"]);
|
||||
|
||||
$randomDayIntervalValue = $this->faker->numberBetween(8, 90) + $interval->days;
|
||||
|
||||
[$artificialFrom, $artificialTo] = match ($afterOrBefore) {
|
||||
"after" => [$originalFrom->addDays($randomDayIntervalValue), $originalTo->addDays($randomDayIntervalValue)],
|
||||
"before" => [$originalFrom->subDays($randomDayIntervalValue), $originalTo->subDays($randomDayIntervalValue)]
|
||||
};
|
||||
|
||||
$overrides[$activityMarkerKeyName] = new CarbonDateRange($artificialFrom, $artificialTo);
|
||||
}
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
protected function ensureGenreExists(int $genreId): Model
|
||||
{
|
||||
$m = $this->descriptor->genreQueryBuilder()->firstWhere("mal_id", $genreId);
|
||||
if ($m == null) {
|
||||
$f = $this->descriptor->genreFactory();
|
||||
$m = $f->createOne([
|
||||
"mal_id" => $genreId
|
||||
]);
|
||||
}
|
||||
|
||||
return $m;
|
||||
}
|
||||
|
||||
protected function adaptDateString($dateStr): string
|
||||
{
|
||||
$parts = explode("-", $dateStr);
|
||||
if (count($parts) === 1) {
|
||||
return $parts[0] . "-01-01";
|
||||
}
|
||||
|
||||
return $dateStr;
|
||||
}
|
||||
|
||||
protected function isScoreValueValid($score): bool
|
||||
{
|
||||
return $score <= 9.99 && $score >= 0.0;
|
||||
}
|
||||
|
||||
protected function getRandomRating(): string
|
||||
{
|
||||
return $this->faker->randomElement([
|
||||
"G - All Ages",
|
||||
"PG - Children",
|
||||
"PG-13 - Teens 13 or older",
|
||||
"R - 17+ (violence & profanity)",
|
||||
"R+ - Mild Nudity",
|
||||
"Rx - Hentai"
|
||||
]);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use App\Testing\JikanDataGenerator;
|
||||
use App\Manga;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
class MangaFactory extends JikanModelFactory
|
||||
class MangaFactory extends JikanMediaModelFactory
|
||||
{
|
||||
use JikanDataGenerator;
|
||||
|
||||
|
47
database/factories/MangaModelFactoryDescriptor.php
Normal file
47
database/factories/MangaModelFactoryDescriptor.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\GenreManga;
|
||||
use App\Http\QueryBuilder\MangaSearchQueryBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class MangaModelFactoryDescriptor implements MediaModelFactoryDescriptor
|
||||
{
|
||||
|
||||
public function activityMarkerKeyName(): string
|
||||
{
|
||||
return "published";
|
||||
}
|
||||
|
||||
public function typeParamMap(): array
|
||||
{
|
||||
return MangaSearchQueryBuilder::MAP_TYPES;
|
||||
}
|
||||
|
||||
public function statusParamMap(): array
|
||||
{
|
||||
return MangaSearchQueryBuilder::MAP_STATUS;
|
||||
}
|
||||
|
||||
public function hasRatingParam(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function mediaName(): string
|
||||
{
|
||||
return "manga";
|
||||
}
|
||||
|
||||
public function genreQueryBuilder(): Builder
|
||||
{
|
||||
return GenreManga::query();
|
||||
}
|
||||
|
||||
public function genreFactory(?int $count = null, $state = []): Factory
|
||||
{
|
||||
return GenreManga::factory($count, $state);
|
||||
}
|
||||
}
|
18
database/factories/MediaModelFactory.php
Normal file
18
database/factories/MediaModelFactory.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface MediaModelFactory
|
||||
{
|
||||
public function overrideFromQueryStringParameters(array $additionalParams, bool $doOpposite = false): self;
|
||||
|
||||
/**
|
||||
* Helper function to create records in db in sequential order.
|
||||
* The records are created with an increasing value for the specified field.
|
||||
* @param string $orderByField The field to order items by. Used to generate increasing value for the field.
|
||||
* @return Collection The created items/records
|
||||
*/
|
||||
public function createManyWithOrder(string $orderByField): Collection;
|
||||
}
|
23
database/factories/MediaModelFactoryDescriptor.php
Normal file
23
database/factories/MediaModelFactoryDescriptor.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
interface MediaModelFactoryDescriptor
|
||||
{
|
||||
public function activityMarkerKeyName(): string;
|
||||
|
||||
public function typeParamMap(): array;
|
||||
|
||||
public function statusParamMap(): array;
|
||||
|
||||
public function hasRatingParam(): bool;
|
||||
|
||||
public function mediaName(): string;
|
||||
|
||||
public function genreQueryBuilder(): Builder;
|
||||
|
||||
public function genreFactory(?int $count = null, $state = []): Factory;
|
||||
}
|
477
tests/Integration/MangaSearchEndpointTest.php
Normal file
477
tests/Integration/MangaSearchEndpointTest.php
Normal file
@ -0,0 +1,477 @@
|
||||
<?php
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests\Integration;
|
||||
|
||||
use App\CarbonDateRange;
|
||||
use App\Http\QueryBuilder\MangaSearchQueryBuilder;
|
||||
use App\Http\QueryBuilder\MediaSearchQueryBuilder;
|
||||
use App\Manga;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MangaSearchEndpointTest extends TestCase
|
||||
{
|
||||
use SyntheticMongoDbTransaction;
|
||||
use ScoutFlush;
|
||||
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
$this->searchIndexModelCleanupList = ["App\\Manga"];
|
||||
}
|
||||
|
||||
protected function getBaseUri(): string
|
||||
{
|
||||
return "/v4/manga";
|
||||
}
|
||||
|
||||
private function generateFiveSpecificAndTenRandomElementsInDb(array $params): array
|
||||
{
|
||||
// 10 random elements
|
||||
Manga::factory(10)
|
||||
->overrideFromQueryStringParameters($params, true)
|
||||
->create();
|
||||
// 5 specific elements
|
||||
$f = Manga::factory(5)
|
||||
->overrideFromQueryStringParameters($params);
|
||||
|
||||
$f->create();
|
||||
|
||||
return $f->raw()[0];
|
||||
}
|
||||
|
||||
public function limitParameterCombinationsProvider(): array
|
||||
{
|
||||
return [
|
||||
[5, []],
|
||||
[5, ["type" => "manga"]],
|
||||
[5, ["type" => "novel", "min_score" => 7]],
|
||||
[5, ["type" => "manga", "max_score" => 6]],
|
||||
[5, ["type" => "manga", "status" => "complete", "max_score" => 8]],
|
||||
[5, ["type" => "oneshot", "status" => "complete", "max_score" => 8]]
|
||||
];
|
||||
}
|
||||
|
||||
public function startDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2022"]],
|
||||
[["start_date" => "2012-05"]],
|
||||
[["start_date" => "2012-05-12"]],
|
||||
[["start_date" => "2012-04-01"]],
|
||||
[["start_date" => "2012-04-28"]],
|
||||
[["start_date" => "2012-06-05", "page" => 1]],
|
||||
];
|
||||
}
|
||||
|
||||
public function endDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["end_date" => "2022"]],
|
||||
[["end_date" => "2012-05"]],
|
||||
[["end_date" => "2012-05-12"]],
|
||||
[["end_date" => "2012-05-12", "page" => 1]],
|
||||
];
|
||||
}
|
||||
|
||||
public function startAndEndDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2021", "end_date" => "2022"]],
|
||||
[["start_date" => "2021-01", "end_date" => "2021-02"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22", "page" => 1]],
|
||||
];
|
||||
}
|
||||
|
||||
public function genresParameterCombinationsProvider(): array
|
||||
{
|
||||
return [
|
||||
[["genres" => "1,2"]],
|
||||
[["genres_exclude" => "4,5", "type" => "tv"]],
|
||||
[["genres" => "1,2", "genres_exclude" => "3", "min_score" => 8, "type" => "tv", "status" => "complete", "page" => 1]],
|
||||
];
|
||||
}
|
||||
|
||||
public function emptyDateRangeProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => ""]],
|
||||
[["end_date" => ""]],
|
||||
[["end_date" => "", "start_date" => ""]],
|
||||
];
|
||||
}
|
||||
|
||||
public function commonParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["status" => "publishing"]],
|
||||
[["status" => "complete"]],
|
||||
[["status" => "upcoming"]],
|
||||
[["status" => "Publishing"]],
|
||||
[["status" => "Complete"]],
|
||||
[["status" => "Upcoming"]],
|
||||
[["max_score" => "8"]],
|
||||
[["min_score" => "6"]],
|
||||
[["max_score" => "7", "min_score" => "3"]]
|
||||
];
|
||||
}
|
||||
|
||||
public function invalidScoreParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["max_score" => "634638"], 15],
|
||||
[["min_score" => "673473"], 0],
|
||||
[["max_score" => "72344", "min_score" => "3532325"], 0],
|
||||
[["max_score" => 1, "min_score" => 5], 0],
|
||||
];
|
||||
}
|
||||
|
||||
public function orderByFieldMappingProvider(): array
|
||||
{
|
||||
$orderByFieldMappings = array_merge(MediaSearchQueryBuilder::ORDER_BY, MangaSearchQueryBuilder::ORDER_BY);
|
||||
$params = [];
|
||||
|
||||
foreach ($orderByFieldMappings as $paramName => $orderByField) {
|
||||
$params[] = [$paramName, $orderByField];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
public function letterParameterProvider(): array
|
||||
{
|
||||
$letters = range("a", "f");
|
||||
$result = [];
|
||||
foreach ($letters as $letter) {
|
||||
$result[] = [["letter" => $letter], 5];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shouldReturnMethodNotAllowedResponseIfMethodNotAllowed()
|
||||
{
|
||||
$this->json("POST", "/v4/anime", ["title" => "Dum"])
|
||||
->seeStatusCode(405);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider limitParameterCombinationsProvider
|
||||
*/
|
||||
public function testLimitParameter(int $limitCount, array $additionalParams)
|
||||
{
|
||||
Manga::factory( 25)
|
||||
->overrideFromQueryStringParameters($additionalParams)
|
||||
->create();
|
||||
|
||||
$content = $this->getJsonResponse([
|
||||
"limit" => $limitCount,
|
||||
...$additionalParams
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($limitCount, 25, $limitCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($limitCount, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider emptyDateRangeProvider
|
||||
*/
|
||||
public function testSearchByEmptyDatesShouldDoNothing($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertCount(15, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider startDatesParameterProvider
|
||||
*/
|
||||
public function testSearchByStartDate($params)
|
||||
{
|
||||
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$actualStartDate = Carbon::parse(data_get($content, "data.0.published.from"));
|
||||
$paramStartDate = Carbon::parse($overrides["published"]["from"]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertGreaterThanOrEqual(0, $paramStartDate->diff($actualStartDate)->days);
|
||||
// we created 5 elements according to parameters, so we expect 5 of them.
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider endDatesParameterProvider
|
||||
*/
|
||||
public function testSearchByEndDate($params)
|
||||
{
|
||||
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$actualEndDate = Carbon::parse(data_get($content, "data.0.published.to"));
|
||||
$paramEndDate = Carbon::parse($overrides["published"]["to"]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertLessThanOrEqual(0, $actualEndDate->diff($paramEndDate)->days);
|
||||
// we created 5 elements according to parameters, so we expect 5 of them.
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider startAndEndDatesParameterProvider
|
||||
*/
|
||||
public function testSearchByStartAndEndDate($params)
|
||||
{
|
||||
$overrides = $this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$actualStartDate = Carbon::parse(data_get($content, "data.0.published.from"));
|
||||
$paramStartDate = Carbon::parse($overrides["published"]["from"]);
|
||||
$actualEndDate = Carbon::parse(data_get($content, "data.0.published.to"));
|
||||
$paramEndDate = Carbon::parse($overrides["published"]["to"]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertGreaterThanOrEqual(0, $paramStartDate->diff($actualStartDate)->days);
|
||||
$this->assertLessThanOrEqual(0, $actualEndDate->diff($paramEndDate)->days);
|
||||
// we created 5 elements according to parameters, so we expect 5 of them.
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
public function testSearchWithStartDateEqualToParam()
|
||||
{
|
||||
// we test here whether the filtering works by start date
|
||||
// if the start date parameter's value exactly matches
|
||||
// with one item in the database.
|
||||
// this is mainly focused on mongodb features
|
||||
$startDate = "2015-02-01";
|
||||
$carbonStartDate = Carbon::parse($startDate);
|
||||
Manga::factory(5)->create();
|
||||
$f = Manga::factory(1);
|
||||
$f->create($f->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange($carbonStartDate, null)
|
||||
]));
|
||||
|
||||
$content = $this->getJsonResponse(["start_date" => $startDate]);
|
||||
$actualStartDate = Carbon::parse(data_get($content, "data.0.published.from"));
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(1);
|
||||
$this->assertEquals(0, $carbonStartDate->diff($actualStartDate)->days);
|
||||
$this->assertCount(1, $content["data"]);
|
||||
}
|
||||
|
||||
public function testSearchWithEndDateEqualToParam()
|
||||
{
|
||||
// we test here whether the filtering works by start date
|
||||
// if the start date parameter's value exactly matches
|
||||
// with one item in the database.
|
||||
// this is mainly focused on mongodb features
|
||||
$endDate = "2015-03-28";
|
||||
$carbonEndDate = Carbon::parse($endDate);
|
||||
Manga::factory(5)->create();
|
||||
$f = Manga::factory(1);
|
||||
$f->create($f->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange(Carbon::parse("2015-01-05"), $carbonEndDate)
|
||||
]));
|
||||
|
||||
$content = $this->getJsonResponse(["end_date" => $endDate]);
|
||||
$actualEndDate = Carbon::parse(data_get($content, "data.0.published.to"));
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(1);
|
||||
$this->assertEquals(0, $carbonEndDate->diff($actualEndDate)->days);
|
||||
$this->assertCount(1, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider genresParameterCombinationsProvider
|
||||
*/
|
||||
public function testSearchByGenres($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertIsArray($content["data"]);
|
||||
// we created 5 elements according to parameters, so we expect 5 of them.
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
public function testSearchByInvalidStatusParameter()
|
||||
{
|
||||
$params = [
|
||||
"status" => "gibberish"
|
||||
];
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertIsArray($content["data"]);
|
||||
// it should return all, and disregard the gibberish filter
|
||||
$this->assertCount(15, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidScoreParameterProvider
|
||||
*/
|
||||
public function testSearchByInvalidScoreParameters($params, $expectedCount)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider commonParameterProvider
|
||||
*/
|
||||
public function testSearchByCommonParams($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertIsArray($content["data"]);
|
||||
// we created 5 elements according to parameters, so we expect 5 of them.
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
public function testSearchByExplicitDefaultMinMaxScores()
|
||||
{
|
||||
// test for https://github.com/jikan-me/jikan-rest/issues/309
|
||||
Manga::factory(5)
|
||||
->overrideFromQueryStringParameters([
|
||||
"genres" => "1,2"
|
||||
])
|
||||
->create();
|
||||
$content = $this->getJsonResponse([
|
||||
"genres" => "1,2",
|
||||
"min_score" => "0.0",
|
||||
"max_score" => "10.0"
|
||||
]);
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(5);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount(5, $content["data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider orderByFieldMappingProvider
|
||||
*/
|
||||
public function testOrderByQueryStringParameter(string $paramName, string $orderByField)
|
||||
{
|
||||
$expectedCount = 3;
|
||||
$f = Manga::factory($expectedCount);
|
||||
/**
|
||||
* @var Collection $items
|
||||
*/
|
||||
$items = $f->createManyWithOrder($orderByField);
|
||||
$content = $this->getJsonResponse([
|
||||
"order_by" => $paramName
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$expectedItems = $items->map(fn($elem) => data_get($elem, $orderByField));
|
||||
$actualItems = collect($content["data"])->map(fn($elem) => data_get($elem, $orderByField));
|
||||
|
||||
if ($actualItems->first() instanceof Carbon && $expectedItems->first() instanceof Carbon) {
|
||||
$expectedItems = $expectedItems->map(fn(Carbon $elem) => $elem->getTimestamp());
|
||||
$actualItems = $actualItems->map(fn(Carbon $elem) => $elem->getTimestamp());
|
||||
}
|
||||
|
||||
$this->assertEquals(0, $expectedItems->diff($actualItems)->count());
|
||||
$this->assertTrue($expectedItems->toArray() === $actualItems->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider letterParameterProvider
|
||||
*/
|
||||
public function testSearchByLetter($params, $expectedCount)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
}
|
||||
|
||||
public function testSearchByInvalidLetterParameter()
|
||||
{
|
||||
$expectedCount = 0;
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb([
|
||||
"letter" => "a"
|
||||
]);
|
||||
$content = $this->getJsonResponse([
|
||||
"letter" => "asd"
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
}
|
||||
|
||||
public function testTypeSenseSearchPagination()
|
||||
{
|
||||
// this should test https://github.com/jikan-me/jikan-rest/issues/298
|
||||
$title = "awesome manga";
|
||||
// typesense api only returns 250 hits max on one page
|
||||
Manga::factory(255)->create([
|
||||
"titles" => [
|
||||
[
|
||||
"type" => "Default",
|
||||
"title" => $title
|
||||
]
|
||||
],
|
||||
"title" => $title,
|
||||
"title_english" => $title,
|
||||
"title_japanese" => $title,
|
||||
"title_synonyms" => [$title],
|
||||
]);
|
||||
Manga::factory(5)->create();
|
||||
|
||||
$content = $this->getJsonResponse([
|
||||
"q" => "awesome",
|
||||
"page" => 2
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(25, 255);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount(25, $content["data"]);
|
||||
// https://github.com/jikan-me/jikan-rest/issues/298 shows that the array indexes start at 25, not from 0
|
||||
$this->assertArrayHasKey(0, $content["data"]);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ namespace Tests;
|
||||
use App\Testing\Concerns\MakesHttpRequestsEx;
|
||||
use App\Testing\ScoutFlush;
|
||||
use App\Testing\SyntheticMongoDbTransaction;
|
||||
use Faker\Factory;
|
||||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Laravel\Lumen\Testing\TestCase as LumenTestCase;
|
||||
@ -17,7 +17,7 @@ abstract class TestCase extends LumenTestCase
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->faker = Factory::create();
|
||||
$this->faker = FakerFactory::create();
|
||||
$this->maxResultsPerPage = env("MAX_RESULTS_PER_PAGE", 25);
|
||||
}
|
||||
|
||||
@ -31,6 +31,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->register(TestServiceProvider::class);
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
31
tests/TestServiceProvider.php
Normal file
31
tests/TestServiceProvider.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
namespace Tests;
|
||||
|
||||
use Database\Factories\AnimeFactory;
|
||||
use Database\Factories\AnimeModelFactoryDescriptor;
|
||||
use Database\Factories\MangaFactory;
|
||||
use Database\Factories\MangaModelFactoryDescriptor;
|
||||
use Database\Factories\MediaModelFactoryDescriptor;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class TestServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(MediaModelFactoryDescriptor::class, AnimeModelFactoryDescriptor::class);
|
||||
$this->app->singleton(MediaModelFactoryDescriptor::class, MangaModelFactoryDescriptor::class);
|
||||
|
||||
$this->app->when(AnimeFactory::class)
|
||||
->needs(MediaModelFactoryDescriptor::class)
|
||||
->give(AnimeModelFactoryDescriptor::class);
|
||||
|
||||
$this->app->when(MangaFactory::class)
|
||||
->needs(MediaModelFactoryDescriptor::class)
|
||||
->give(MangaModelFactoryDescriptor::class);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user