From 8f102d2b566df41a6cc01726d239c221da3802a1 Mon Sep 17 00:00:00 2001 From: pushrbx Date: Sun, 4 Dec 2022 11:38:11 +0000 Subject: [PATCH] added the foundations for fixture based tests --- .editorconfig | 1 - app/Anime.php | 3 + app/Character.php | 3 + app/GenreAnime.php | 3 + app/GenreManga.php | 3 + app/Manga.php | 3 + app/Person.php | 2 + composer.json | 1 + composer.lock | 71 +++++++++++- database/factories/AnimeFactory.php | 102 +++++++++++++++++ database/factories/CharacterFactory.php | 31 ++++++ database/factories/GenreAnimeFactory.php | 9 ++ database/factories/GenreFactory.php | 24 ++++ database/factories/GenreMangaFactory.php | 9 ++ database/factories/JikanDataGenerator.php | 129 ++++++++++++++++++++++ database/factories/MangaFactory.php | 85 ++++++++++++++ database/factories/PersonFactory.php | 39 +++++++ tests/AnimeSearchEndpointTest.php | 57 ++++++++++ tests/MakesHttpRequestsEx.php | 15 +++ tests/TestCase.php | 2 + 20 files changed, 589 insertions(+), 3 deletions(-) create mode 100644 database/factories/AnimeFactory.php create mode 100644 database/factories/CharacterFactory.php create mode 100644 database/factories/GenreAnimeFactory.php create mode 100644 database/factories/GenreFactory.php create mode 100644 database/factories/GenreMangaFactory.php create mode 100644 database/factories/JikanDataGenerator.php create mode 100644 database/factories/MangaFactory.php create mode 100644 database/factories/PersonFactory.php create mode 100644 tests/AnimeSearchEndpointTest.php create mode 100644 tests/MakesHttpRequestsEx.php diff --git a/.editorconfig b/.editorconfig index fed8ba8..ad772f4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,6 @@ indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true -end_of_line = lf [*.md] max_line_length = off diff --git a/app/Anime.php b/app/Anime.php index 4469a7c..f440f47 100644 --- a/app/Anime.php +++ b/app/Anime.php @@ -5,9 +5,12 @@ namespace App; use App\Http\HttpHelper; use Jikan\Jikan; use Jikan\Request\Anime\AnimeRequest; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Anime extends JikanApiSearchableModel { + use HasFactory; + // note that here we skip "score", "min_score", "max_score", "rating" and others because they need special logic // to set the correct filtering on the ORM. protected array $filters = ["order_by", "status", "type", "sort"]; diff --git a/app/Character.php b/app/Character.php index 4327711..9d8ce50 100644 --- a/app/Character.php +++ b/app/Character.php @@ -4,9 +4,12 @@ namespace App; use Jikan\Jikan; use Jikan\Request\Character\CharacterRequest; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Character extends JikanApiSearchableModel { + use HasFactory; + protected array $filters = ["order_by", "sort"]; /** diff --git a/app/GenreAnime.php b/app/GenreAnime.php index 451cb13..b234651 100644 --- a/app/GenreAnime.php +++ b/app/GenreAnime.php @@ -4,6 +4,7 @@ namespace App; use Jenssegers\Mongodb\Eloquent\Model; use Jikan\Request\Genre\AnimeGenresRequest; +use Illuminate\Database\Eloquent\Factories\HasFactory; /** * Class Magazine @@ -11,6 +12,8 @@ use Jikan\Request\Genre\AnimeGenresRequest; */ class GenreAnime extends JikanApiSearchableModel { + use HasFactory; + protected array $filters = ["order_by", "sort"]; /** diff --git a/app/GenreManga.php b/app/GenreManga.php index ee69da3..e5bf934 100644 --- a/app/GenreManga.php +++ b/app/GenreManga.php @@ -4,6 +4,7 @@ namespace App; use Jenssegers\Mongodb\Eloquent\Model; use Jikan\Request\Genre\AnimeGenresRequest; +use Illuminate\Database\Eloquent\Factories\HasFactory; /** * Class Magazine @@ -11,6 +12,8 @@ use Jikan\Request\Genre\AnimeGenresRequest; */ class GenreManga extends JikanApiSearchableModel { + use HasFactory; + protected array $filters = ["order_by", "sort"]; /** diff --git a/app/Manga.php b/app/Manga.php index 2966f22..90e23fa 100644 --- a/app/Manga.php +++ b/app/Manga.php @@ -5,9 +5,12 @@ namespace App; use App\Http\HttpHelper; use Jikan\Jikan; use Jikan\Request\Manga\MangaRequest; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Manga extends JikanApiSearchableModel { + use HasFactory; + // note that here we skip "score", "min_score", "max_score", "rating" and others because they need special logic // to set the correct filtering on the ORM. protected array $filters = ["order_by", "status", "type", "sort"]; diff --git a/app/Person.php b/app/Person.php index e390586..73e5335 100644 --- a/app/Person.php +++ b/app/Person.php @@ -5,9 +5,11 @@ namespace App; use Jikan\Jikan; use Jikan\Request\Person\PersonRequest; use function Symfony\Component\Translation\t; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Person extends JikanApiSearchableModel { + use HasFactory; protected array $filters = ["order_by", "sort"]; /** diff --git a/composer.json b/composer.json index ee04541..38783b7 100644 --- a/composer.json +++ b/composer.json @@ -39,6 +39,7 @@ "zircote/swagger-php": "3.*" }, "require-dev": { + "fakerphp/faker": "^1.20", "mockery/mockery": "^1.3.1", "phpunit/phpunit": "^8.5" }, diff --git a/composer.lock b/composer.lock index 134c779..dcbf4de 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "cf4c0d150bce75398ce4300c27d1cdf6", + "content-hash": "3471a91d2125954a5be97999b1c3ce12", "packages": [ { "name": "amphp/amp", @@ -10945,6 +10945,73 @@ } ], "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "37f751c67a5372d4e26353bd9384bc03744ec77b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/37f751c67a5372d4e26353bd9384bc03744ec77b", + "reference": "37f751c67a5372d4e26353bd9384bc03744ec77b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "symfony/phpunit-bridge": "^4.4 || ^5.2" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.20-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.20.0" + }, + "time": "2022-07-20T13:12:54+00:00" + }, { "name": "hamcrest/hamcrest-php", "version": "v2.0.1", @@ -12426,5 +12493,5 @@ "ext-mongodb": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/database/factories/AnimeFactory.php b/database/factories/AnimeFactory.php new file mode 100644 index 0000000..c72ae50 --- /dev/null +++ b/database/factories/AnimeFactory.php @@ -0,0 +1,102 @@ +createMalId(); + $title = $this->createTitle(); + $status = $this->faker->randomElement(["Currently Airing", "Completed", "Upcoming"]); + [$aired_from, $aired_to] = $this->createActiveDateRange($status, "Currently Airing"); + + return [ + "mal_id" => $mal_id, + "url" => $this->createUrl($mal_id, "anime"), + "titles" => [ + [ + "type" => "Default", + "title" => $title + ] + ], + "title" => $title, + "title_english" => $title, + "title_japanese" => $title, + "title_synonyms" => [$title], + "type" => $this->faker->randomElement(["TV", "Movie", "OVA"]), + "source" => $this->faker->randomElement(["Manga", "Original", "Novel"]), + "episodes" => $this->faker->randomElement([1, 12, 13, 16, 24, 48, 96, 128, 366]), + "status" => $status, + "airing" => $status == "Currently Airing", + "aired" => [ + "from" => $aired_from->toAtomString(), + "to" => $aired_to, + ], + "duration" => "", + "rating" => $this->faker->randomElement(["R - 17+ (violence & profanity)", "PG"]), + "score" => $this->faker->randomFloat(2, 1.00, 9.99), + "scored_by" => $this->faker->randomDigitNotNull(), + "rank" => $this->faker->randomDigitNotNull(), + "popularity" => $this->faker->randomDigitNotNull(), + "members" => $this->faker->randomDigitNotNull(), + "favorites" => $this->faker->randomDigitNotNull(), + "synopsis" => "test", + "background" => "test", + "season" => $this->faker->randomElement(["winter", "spring", "fall", "summer"]), + "broadcast" => [ + "day" => "", + "time" => "", + "timezone" => "Asia/Tokyo", + "string" => "Tuesdays at 00:00 (JST)" + ], + "producers" => [ + [ + "mal_id" => 16, + "type" => "anime", + "name" => "TV Tokyo", + "url" => "https://myanimelist.net/anime/producer/16/TV_Tokyo" + ] + ], + "lincesors" => [ + [ + "mal_id" => 119, + "type" => "anime", + "name" => "VIZ Media", + "url" => "https://myanimelist.net/anime/producer/119/VIZ_Media" + ] + ], + "studios" => [], + "genres" => [ + [ + "mal_id" => 1, + "type" => "anime", + "name" => "Action", + "url" => "https://myanimelist.net/anime/genre/1/Action" + ] + ], + "explicit_genres" => [], + "themes" => [], + "demographics" => [ + [ + "mal_id" => 27, + "type" => "anime", + "name" => "Shounen", + "url" => "https://myanimelist.net/anime/genre/27/Shounen" + ] + ] + ]; + } +} diff --git a/database/factories/CharacterFactory.php b/database/factories/CharacterFactory.php new file mode 100644 index 0000000..91f1c27 --- /dev/null +++ b/database/factories/CharacterFactory.php @@ -0,0 +1,31 @@ +createMalId(); + $url = $this->createUrl($mal_id, "character"); + + return [ + "mal_id" => $mal_id, + "url" => $url, + "images" => [], + "name" => $this->faker->name(), + "name_kanji" => "岡", + "nicknames" => [], + "favorites" => $this->faker->randomDigitNotNull(), + "about" => "test" + ]; + } +} diff --git a/database/factories/GenreAnimeFactory.php b/database/factories/GenreAnimeFactory.php new file mode 100644 index 0000000..cff6532 --- /dev/null +++ b/database/factories/GenreAnimeFactory.php @@ -0,0 +1,9 @@ +createMalId(); + $name = $this->getRandomGenreName(); + $url = $this->createUrl($mal_id, $this->mediaType . "/genre"); + + return [ + "mal_id" => $mal_id, + "name" => $name, + "url" => $url, + "count" => $this->faker->randomDigit() + ]; + } +} diff --git a/database/factories/GenreMangaFactory.php b/database/factories/GenreMangaFactory.php new file mode 100644 index 0000000..452c5de --- /dev/null +++ b/database/factories/GenreMangaFactory.php @@ -0,0 +1,9 @@ +faker->numberBetween(1, 99999); + } + + private function createTitle(): string + { + return $this->faker->name(); + } + + private function createRandomDateTime($startDate = "-30 years"): Carbon + { + return Carbon::createFromTimestamp($this->faker->dateTimeBetween($startDate)->getTimestamp()); + } + + private function createActiveDateRange($status, $activeStatus): array + { + $from = $this->createRandomDateTime("-15 years"); + $to = $status != $activeStatus ? $from->addDays($this->faker->numberBetween(1, 368))->toAtomString() : null; + return [$from, $to]; + } + + private function getRandomGenreName(): string + { + return $this->faker->randomElement($this->dummyGenres); + } + + private function getRandomGenreNames($count = 1): array + { + return $this->faker->randomElements($this->dummyGenres, $count); + } +} diff --git a/database/factories/MangaFactory.php b/database/factories/MangaFactory.php new file mode 100644 index 0000000..eb58141 --- /dev/null +++ b/database/factories/MangaFactory.php @@ -0,0 +1,85 @@ +createMalId(); + $title = $this->createTitle(); + $status = $this->faker->randomElement(["Finished", "Publishing", "Upcoming"]); + [$published_from, $published_to] = $this->createActiveDateRange($status, "Publishing"); + + return [ + "mal_id" => $mal_id, + "url" => $this->createUrl($mal_id, "manga"), + "titles" => [ + [ + "type" => "Default", + "title" => $title + ] + ], + "title" => $title, + "title_english" => $title, + "title_japanese" => $title, + "title_synonyms" => [$title], + "type" => $this->faker->randomElement(["Manga", "Light Novel", "Web Novel"]), + "chapters" => $this->faker->numberBetween(1, 255), + "volumes" => $this->faker->numberBetween(0, 55), + "status" => $status, + "publishing" => $status === "Finished", + "published" => [ + "from" => $published_from->toAtomString(), + "to" => $published_to + ], + "score" => $this->faker->randomFloat(2, 1.00, 9.99), + "scored_by" => $this->faker->randomDigitNotNull(), + "rank" => $this->faker->randomDigitNotNull(), + "popularity" => $this->faker->randomDigitNotNull(), + "members" => $this->faker->randomDigitNotNull(), + "favorites" => $this->faker->randomDigitNotNull(), + "synopsis" => "test", + "background" => "test", + "authors" => [ + [ + "mal_id" => 1874, + "type" => "people", + "name" => "Arakawa, Hiromu", + "url" => "https://myanimelist.net/people/1847/Hiromu_Arakawa" + ] + ], + "serializations" => [ + [ + "mal_id" => 13, + "type" => "manga", + "name" => "Shounen Gangan", + "url" => "https://myanimelist.net/manga/magazine/13/Shounen_Gangan" + ] + ], + "genres" => [ + [ + "mal_id" => 1, + "type" => "anime", + "name" => "Action", + "url" => "https://myanimelist.net/anime/genre/1/Action" + ] + ], + "explicit_genres" => [], + "themes" => [], + "demographics" => [ + [ + "mal_id" => 27, + "type" => "manga", + "name" => "Shounen", + "url" => "https://myanimelist.net/manga/genre/27/Shounen" + ] + ] + ]; + } +} diff --git a/database/factories/PersonFactory.php b/database/factories/PersonFactory.php new file mode 100644 index 0000000..2d0c40d --- /dev/null +++ b/database/factories/PersonFactory.php @@ -0,0 +1,39 @@ +createMalId(); + $name = $this->faker->name(); + $given_name = $this->faker->firstName(); + $family_name = $this->faker->lastName(); + + return [ + "mal_id" => $mal_id, + "url" => $this->createUrl($mal_id, "people"), + "website_url" => "https://webiste.example", + "images" => [], + "name" => $name, + "given_name" => $given_name, + "family_name" => $family_name, + "alternate_names" => [], + "birthday" => $this->createRandomDateTime("-80 years")->toAtomString(), + "favorites" => $this->faker->randomDigitNotNull(), + "about" => "test" + ]; + } +} diff --git a/tests/AnimeSearchEndpointTest.php b/tests/AnimeSearchEndpointTest.php new file mode 100644 index 0000000..2e9bcb5 --- /dev/null +++ b/tests/AnimeSearchEndpointTest.php @@ -0,0 +1,57 @@ + "tv"]], + [5, ["type" => "tv", "min_score" => 7]], + [5, ["type" => "tv", "max_score" => 6]], + [5, ["type" => "tv", "status" => "complete", "max_score" => 8]], + [5, ["type" => "movie", "status" => "complete", "max_score" => 8]] + ]; + } + + /** + * @test + */ + public function shouldReturnMethodNotAllowedResponseIfMethodNotAllowed() + { + $this->json("POST", "/v4/anime", ["title" => "Dum"]) + ->seeStatusCode(405); + } + + /** + * @dataProvider limitParameterCombinationsProvider + */ + public function testLimitParameter(int $limitCount, array $additionalParams) + { + $f = Anime::factory()->count(25); + $overrides = []; + // let's make all database items the same type + if (array_key_exists("type", $additionalParams)) { + $overrides["type"] = $additionalParams["type"]; + } + $f->make($overrides); + $parameters = http_build_query([ + "limit" => $limitCount, + ...$additionalParams + ]); + $uri = "/v4/anime?" . $parameters; + $sut = $this->getJson($uri); + $content = $sut->response->json(); + + $sut->seeStatusCode(200); + $sut->response->assertJsonPath("pagination.items.count", $limitCount); + $sut->response->assertJsonPath("pagination.items.total", 25); + $sut->response->assertJsonPath("pagination.items.per_page", $limitCount); + $this->assertIsArray($content["data"]); + $this->assertCount($limitCount, $content["data"]); + } +} diff --git a/tests/MakesHttpRequestsEx.php b/tests/MakesHttpRequestsEx.php new file mode 100644 index 0000000..d9dace8 --- /dev/null +++ b/tests/MakesHttpRequestsEx.php @@ -0,0 +1,15 @@ +json('GET', $uri, [], $headers); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index d548133..07817ed 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,8 @@ abstract class TestCase extends Laravel\Lumen\Testing\TestCase { + use MakesHttpRequestsEx; + /** * Creates the application. *