From e3d380eadd1bff2af2a3459bcbd83fc6b49cd16c Mon Sep 17 00:00:00 2001 From: "ext.jeremy.guillot@maxicoffee.domains" Date: Mon, 10 Feb 2025 19:21:14 +0100 Subject: [PATCH] feat: GetMangaList endpoint + tests + test db --- .env.test | 5 +- composer.json | 1 + composer.lock | 69 ++++++++++- config/bundles.php | 1 + config/packages/api_platform.yaml | 1 + .../packages/dama_doctrine_test_bundle.yaml | 5 + config/packages/doctrine.yaml | 7 +- phpunit.xml.dist | 1 + .../Manga/Application/Query/GetMangaList.php | 13 ++ .../QueryHandler/GetMangaListHandler.php | 33 +++++ .../Response/MangaListResponse.php | 28 +++++ .../Repository/MangaRepositoryInterface.php | 14 +++ .../Exception/InvalidExternalIdException.php | 7 ++ .../Exception/InvalidMangaIdException.php | 7 ++ .../Exception/InvalidMangaSlugException.php | 7 ++ .../Exception/InvalidMangaTitleException.php | 7 ++ .../Domain/Exception/MangaDomainException.php | 7 ++ src/Domain/Manga/Domain/Model/Manga.php | 80 +++++++++++++ .../Domain/Model/ValueObject/ExternalId.php | 21 ++++ .../Domain/Model/ValueObject/MangaId.php | 26 ++++ .../Domain/Model/ValueObject/MangaSlug.php | 25 ++++ .../Domain/Model/ValueObject/MangaTitle.php | 21 ++++ .../ApiPlatform/Dto/MangaCollection.php | 18 +++ .../ApiPlatform/Dto/MangaListItem.php | 21 ++++ .../Resource/MangaListResource.php | 23 ++++ .../Provider/GetMangaListStateProvider.php | 56 +++++++++ .../Persistence/LegacyMangaRepository.php | 113 ++++++++++++++++++ symfony.lock | 12 ++ .../Manga/Adapter/InMemoryMangaRepository.php | 73 +++++++++++ .../QueryHandler/GetMangaListHandlerTest.php | 102 ++++++++++++++++ tests/Feature/AbstractApiTestCase.php | 25 ++++ tests/Feature/Manga/GetMangaListTest.php | 107 +++++++++++++++++ .../Feature/Scraping/AbstractApiTestCase.php | 18 --- tests/Feature/Scraping/ScrapeChapterTest.php | 1 + 34 files changed, 932 insertions(+), 23 deletions(-) create mode 100644 config/packages/dama_doctrine_test_bundle.yaml create mode 100644 src/Domain/Manga/Application/Query/GetMangaList.php create mode 100644 src/Domain/Manga/Application/QueryHandler/GetMangaListHandler.php create mode 100644 src/Domain/Manga/Application/Response/MangaListResponse.php create mode 100644 src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php create mode 100644 src/Domain/Manga/Domain/Exception/InvalidExternalIdException.php create mode 100644 src/Domain/Manga/Domain/Exception/InvalidMangaIdException.php create mode 100644 src/Domain/Manga/Domain/Exception/InvalidMangaSlugException.php create mode 100644 src/Domain/Manga/Domain/Exception/InvalidMangaTitleException.php create mode 100644 src/Domain/Manga/Domain/Exception/MangaDomainException.php create mode 100644 src/Domain/Manga/Domain/Model/Manga.php create mode 100644 src/Domain/Manga/Domain/Model/ValueObject/ExternalId.php create mode 100644 src/Domain/Manga/Domain/Model/ValueObject/MangaId.php create mode 100644 src/Domain/Manga/Domain/Model/ValueObject/MangaSlug.php create mode 100644 src/Domain/Manga/Domain/Model/ValueObject/MangaTitle.php create mode 100644 src/Domain/Manga/Infrastructure/ApiPlatform/Dto/MangaCollection.php create mode 100644 src/Domain/Manga/Infrastructure/ApiPlatform/Dto/MangaListItem.php create mode 100644 src/Domain/Manga/Infrastructure/ApiPlatform/Resource/MangaListResource.php create mode 100644 src/Domain/Manga/Infrastructure/ApiPlatform/State/Provider/GetMangaListStateProvider.php create mode 100644 src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php create mode 100644 tests/Domain/Manga/Adapter/InMemoryMangaRepository.php create mode 100644 tests/Domain/Manga/Application/QueryHandler/GetMangaListHandlerTest.php create mode 100644 tests/Feature/AbstractApiTestCase.php create mode 100644 tests/Feature/Manga/GetMangaListTest.php delete mode 100644 tests/Feature/Scraping/AbstractApiTestCase.php diff --git a/.env.test b/.env.test index 9422c77..ec9b992 100644 --- a/.env.test +++ b/.env.test @@ -5,5 +5,6 @@ SYMFONY_DEPRECATIONS_HELPER=999999 PANTHER_APP_ENV=panther PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots -POSTGRES_DB=app -POSTGRE_VERSION=16 +# Configuration PostgreSQL pour les tests +POSTGRES_DB=app_test +POSTGRES_VERSION=16 diff --git a/composer.json b/composer.json index ad4371e..3b04b95 100644 --- a/composer.json +++ b/composer.json @@ -105,6 +105,7 @@ } }, "require-dev": { + "dama/doctrine-test-bundle": "^8.2", "dbrekelmans/bdi": "^1.3", "deployer/deployer": "^7.5", "doctrine/doctrine-fixtures-bundle": "^3.5", diff --git a/composer.lock b/composer.lock index 10c137e..d131de6 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": "49014ec06c069804432e6a13701e46a4", + "content-hash": "e84863f24aa342f98beebd3cd364f698", "packages": [ { "name": "api-platform/core", @@ -9436,6 +9436,73 @@ ], "time": "2022-02-25T21:32:43+00:00" }, + { + "name": "dama/doctrine-test-bundle", + "version": "v8.2.2", + "source": { + "type": "git", + "url": "https://github.com/dmaicher/doctrine-test-bundle.git", + "reference": "eefe54fdf00d910f808efea9cfce9cc261064a0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/eefe54fdf00d910f808efea9cfce9cc261064a0a", + "reference": "eefe54fdf00d910f808efea9cfce9cc261064a0a", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^3.3 || ^4.0", + "doctrine/doctrine-bundle": "^2.11.0", + "php": "^7.4 || ^8.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^5.4 || ^6.3 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.3 || ^7.0" + }, + "require-dev": { + "behat/behat": "^3.0", + "friendsofphp/php-cs-fixer": "^3.27", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "symfony/phpunit-bridge": "^7.2", + "symfony/process": "^5.4 || ^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.3 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "DAMA\\DoctrineTestBundle\\": "src/DAMA/DoctrineTestBundle" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Maicher", + "email": "mail@dmaicher.de" + } + ], + "description": "Symfony bundle to isolate doctrine database tests and improve test performance", + "keywords": [ + "doctrine", + "isolation", + "performance", + "symfony", + "testing", + "tests" + ], + "support": { + "issues": "https://github.com/dmaicher/doctrine-test-bundle/issues", + "source": "https://github.com/dmaicher/doctrine-test-bundle/tree/v8.2.2" + }, + "time": "2025-02-04T14:37:36+00:00" + }, { "name": "dbrekelmans/bdi", "version": "1.3.0", diff --git a/config/bundles.php b/config/bundles.php index c087d61..24a8025 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -20,4 +20,5 @@ return [ Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], Symfony\UX\Turbo\TurboBundle::class => ['all' => true], + DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], ]; diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 0cb406f..004f791 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -26,5 +26,6 @@ api_platform: mapping: paths: - '%kernel.project_dir%/src/Domain/Scraping/Infrastructure/ApiPlatform/Dto' + - '%kernel.project_dir%/src/Domain/Manga/Infrastructure/ApiPlatform/Resource' patch_formats: json: ['application/merge-patch+json'] diff --git a/config/packages/dama_doctrine_test_bundle.yaml b/config/packages/dama_doctrine_test_bundle.yaml new file mode 100644 index 0000000..3482cba --- /dev/null +++ b/config/packages/dama_doctrine_test_bundle.yaml @@ -0,0 +1,5 @@ +when@test: + dama_doctrine_test: + enable_static_connection: true + enable_static_meta_data_cache: true + enable_static_query_cache: true diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 8b92ef4..a735038 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -32,8 +32,11 @@ doctrine: when@test: doctrine: dbal: - # "TEST_TOKEN" is typically set by ParaTest - dbname_suffix: '_test%env(default::TEST_TOKEN)%' + connections: + default: + use_savepoints: true + # "TEST_TOKEN" is typically set by ParaTest + dbname_suffix: '_test%env(default::TEST_TOKEN)%' when@prod: doctrine: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6976b90..7cf49e3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,7 @@ + diff --git a/src/Domain/Manga/Application/Query/GetMangaList.php b/src/Domain/Manga/Application/Query/GetMangaList.php new file mode 100644 index 0000000..a656da5 --- /dev/null +++ b/src/Domain/Manga/Application/Query/GetMangaList.php @@ -0,0 +1,13 @@ +mangaRepository->findAll( + page: $query->page, + limit: $query->limit, + sortBy: $query->sortBy, + sortOrder: $query->sortOrder + ); + + $total = $this->mangaRepository->count(); + + return new MangaListResponse( + mangas: $mangas, + total: $total, + page: $query->page, + limit: $query->limit + ); + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Application/Response/MangaListResponse.php b/src/Domain/Manga/Application/Response/MangaListResponse.php new file mode 100644 index 0000000..07c6cfe --- /dev/null +++ b/src/Domain/Manga/Application/Response/MangaListResponse.php @@ -0,0 +1,28 @@ +total / $this->limit); + } + + public function hasNextPage(): bool + { + return $this->page < $this->getTotalPages(); + } + + public function hasPreviousPage(): bool + { + return $this->page > 1; + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php b/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php new file mode 100644 index 0000000..2380756 --- /dev/null +++ b/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php @@ -0,0 +1,14 @@ +id; + } + + public function getTitle(): MangaTitle + { + return $this->title; + } + + public function getSlug(): MangaSlug + { + return $this->slug; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getAuthor(): string + { + return $this->author; + } + + public function getPublicationYear(): int + { + return $this->publicationYear; + } + + public function getGenres(): array + { + return $this->genres; + } + + public function getStatus(): string + { + return $this->status; + } + + public function getExternalId(): ?ExternalId + { + return $this->externalId; + } + + public function getImageUrl(): ?string + { + return $this->imageUrl; + } + + public function getRating(): ?float + { + return $this->rating; + } +} diff --git a/src/Domain/Manga/Domain/Model/ValueObject/ExternalId.php b/src/Domain/Manga/Domain/Model/ValueObject/ExternalId.php new file mode 100644 index 0000000..b487c7d --- /dev/null +++ b/src/Domain/Manga/Domain/Model/ValueObject/ExternalId.php @@ -0,0 +1,21 @@ +value; + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Domain/Model/ValueObject/MangaId.php b/src/Domain/Manga/Domain/Model/ValueObject/MangaId.php new file mode 100644 index 0000000..42a3a35 --- /dev/null +++ b/src/Domain/Manga/Domain/Model/ValueObject/MangaId.php @@ -0,0 +1,26 @@ +value; + } + + public function equals(self $other): bool + { + return $this->value === $other->value; + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Domain/Model/ValueObject/MangaSlug.php b/src/Domain/Manga/Domain/Model/ValueObject/MangaSlug.php new file mode 100644 index 0000000..3c91c76 --- /dev/null +++ b/src/Domain/Manga/Domain/Model/ValueObject/MangaSlug.php @@ -0,0 +1,25 @@ +value; + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Domain/Model/ValueObject/MangaTitle.php b/src/Domain/Manga/Domain/Model/ValueObject/MangaTitle.php new file mode 100644 index 0000000..b6e3c39 --- /dev/null +++ b/src/Domain/Manga/Domain/Model/ValueObject/MangaTitle.php @@ -0,0 +1,21 @@ +value; + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Infrastructure/ApiPlatform/Dto/MangaCollection.php b/src/Domain/Manga/Infrastructure/ApiPlatform/Dto/MangaCollection.php new file mode 100644 index 0000000..1b36ffc --- /dev/null +++ b/src/Domain/Manga/Infrastructure/ApiPlatform/Dto/MangaCollection.php @@ -0,0 +1,18 @@ +handler->handle($query); + + return new MangaCollection( + items: array_map( + fn (Manga $manga) => $this->createMangaListItem($manga), + $response->mangas + ), + total: $response->total, + page: $response->page, + limit: $response->limit, + hasNextPage: $response->hasNextPage(), + hasPreviousPage: $response->hasPreviousPage() + ); + } + + private function createMangaListItem(Manga $manga): MangaListItem + { + return new MangaListItem( + id: $manga->getId()->getValue(), + title: $manga->getTitle()->getValue(), + slug: $manga->getSlug()->getValue(), + imageUrl: $manga->getImageUrl(), + author: $manga->getAuthor(), + publicationYear: $manga->getPublicationYear(), + genres: $manga->getGenres(), + status: $manga->getStatus(), + rating: $manga->getRating() + ); + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php b/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php new file mode 100644 index 0000000..efc192b --- /dev/null +++ b/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php @@ -0,0 +1,113 @@ +entityManager->createQueryBuilder() + ->select('m') + ->from(EntityManga::class, 'm') + ->orderBy("m.$sortBy", $sortOrder) + ->setFirstResult($offset) + ->setMaxResults($limit); + + return array_map( + fn (EntityManga $entity) => $this->toDomain($entity), + $queryBuilder->getQuery()->getResult() + ); + } + + public function count(): int + { + return $this->entityManager->createQueryBuilder() + ->select('COUNT(m.id)') + ->from(EntityManga::class, 'm') + ->getQuery() + ->getSingleScalarResult(); + } + + public function findById(string $id): ?DomainManga + { + $entity = $this->entityManager->find(EntityManga::class, $id); + + return $entity ? $this->toDomain($entity) : null; + } + + public function save(DomainManga $manga): void + { + $entity = $this->entityManager->find(EntityManga::class, $manga->getId()->getValue()) + ?? new EntityManga(); + + $this->updateEntity($entity, $manga); + + $this->entityManager->persist($entity); + $this->entityManager->flush(); + } + + public function delete(DomainManga $manga): void + { + $entity = $this->entityManager->find(EntityManga::class, $manga->getId()->getValue()); + + if ($entity) { + $this->entityManager->remove($entity); + $this->entityManager->flush(); + } + } + + private function toDomain(EntityManga $entity): DomainManga + { + return new DomainManga( + new MangaId((string) $entity->getId()), + new MangaTitle($entity->getTitle()), + new MangaSlug($entity->getSlug()), + $entity->getDescription(), + $entity->getAuthor(), + $entity->getPublicationYear(), + $entity->getGenres(), + $entity->getStatus(), + $entity->getExternalId() ? new ExternalId($entity->getExternalId()) : null, + $entity->getImageUrl(), + $entity->getRating() + ); + } + + private function updateEntity(EntityManga $entity, DomainManga $manga): void + { + $entity->setTitle($manga->getTitle()->getValue()) + ->setSlug($manga->getSlug()->getValue()) + ->setDescription($manga->getDescription()) + ->setAuthor($manga->getAuthor()) + ->setPublicationYear($manga->getPublicationYear()) + ->setGenres($manga->getGenres()) + ->setStatus($manga->getStatus()); + + if ($manga->getExternalId()) { + $entity->setExternalId($manga->getExternalId()->getValue()); + } + + if ($manga->getImageUrl()) { + $entity->setImageUrl($manga->getImageUrl()); + } + + if ($manga->getRating()) { + $entity->setRating($manga->getRating()); + } + } +} \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index ea48aa6..2a29c42 100644 --- a/symfony.lock +++ b/symfony.lock @@ -13,6 +13,18 @@ "src/ApiResource/.gitignore" ] }, + "dama/doctrine-test-bundle": { + "version": "8.2", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "7.2", + "ref": "896306d79d4ee143af9eadf9b09fd34a8c391b70" + }, + "files": [ + "config/packages/dama_doctrine_test_bundle.yaml" + ] + }, "doctrine/doctrine-bundle": { "version": "2.11", "recipe": { diff --git a/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php b/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php new file mode 100644 index 0000000..8ff2f47 --- /dev/null +++ b/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php @@ -0,0 +1,73 @@ +mangas; + + usort($sortedMangas, function (Manga $a, Manga $b) use ($sortBy, $sortOrder) { + $valueA = $this->getPropertyValue($a, $sortBy); + $valueB = $this->getPropertyValue($b, $sortBy); + + return $sortOrder === 'asc' + ? $valueA <=> $valueB + : $valueB <=> $valueA; + }); + + $offset = ($page - 1) * $limit; + + return array_slice($sortedMangas, $offset, $limit); + } + + public function count(): int + { + return count($this->mangas); + } + + public function findById(string $id): ?Manga + { + foreach ($this->mangas as $manga) { + if ($manga->getId()->getValue() === $id) { + return $manga; + } + } + + return null; + } + + public function save(Manga $manga): void + { + $this->mangas[] = $manga; + } + + public function delete(Manga $manga): void + { + $this->mangas = array_filter( + $this->mangas, + fn(Manga $existingManga) => !$existingManga->getId()->equals($manga->getId()) + ); + } + + private function getPropertyValue(Manga $manga, string $property): mixed + { + return match($property) { + 'title' => $manga->getTitle()->getValue(), + 'publicationYear' => $manga->getPublicationYear(), + default => throw new \InvalidArgumentException("Unknown sort property: $property") + }; + } + + public function clear(): void + { + $this->mangas = []; + } +} \ No newline at end of file diff --git a/tests/Domain/Manga/Application/QueryHandler/GetMangaListHandlerTest.php b/tests/Domain/Manga/Application/QueryHandler/GetMangaListHandlerTest.php new file mode 100644 index 0000000..9d81d78 --- /dev/null +++ b/tests/Domain/Manga/Application/QueryHandler/GetMangaListHandlerTest.php @@ -0,0 +1,102 @@ +repository = new InMemoryMangaRepository(); + $this->handler = new GetMangaListHandler($this->repository); + } + + public function testHandleReturnsEmptyListWhenNoMangas(): void + { + $query = new GetMangaList(); + + $response = $this->handler->handle($query); + + $this->assertEmpty($response->mangas); + $this->assertEquals(0, $response->total); + $this->assertEquals(1, $response->page); + $this->assertEquals(20, $response->limit); + $this->assertFalse($response->hasNextPage()); + $this->assertFalse($response->hasPreviousPage()); + } + + public function testHandleReturnsPaginatedList(): void + { + // Arrange + $this->givenMangasExist(25); + + // Act + $query = new GetMangaList(page: 2, limit: 10); + $response = $this->handler->handle($query); + + // Assert + $this->assertCount(10, $response->mangas); + $this->assertEquals(25, $response->total); + $this->assertEquals(2, $response->page); + $this->assertEquals(10, $response->limit); + $this->assertTrue($response->hasNextPage()); + $this->assertTrue($response->hasPreviousPage()); + } + + public function testHandleSortsMangas(): void + { + // Arrange + $this->repository->save($this->createManga('1', 'Manga B', 2020)); + $this->repository->save($this->createManga('2', 'Manga A', 2021)); + $this->repository->save($this->createManga('3', 'Manga C', 2019)); + + // Act + $query = new GetMangaList(sortBy: 'title', sortOrder: 'asc'); + $response = $this->handler->handle($query); + + // Assert + $this->assertCount(3, $response->mangas); + $this->assertEquals('Manga A', $response->mangas[0]->getTitle()->getValue()); + $this->assertEquals('Manga B', $response->mangas[1]->getTitle()->getValue()); + $this->assertEquals('Manga C', $response->mangas[2]->getTitle()->getValue()); + } + + private function givenMangasExist(int $count): void + { + for ($i = 1; $i <= $count; $i++) { + $this->repository->save( + $this->createManga((string)$i, "Manga $i", 2020) + ); + } + } + + private function createManga(string $id, string $title, int $year): Manga + { + return new Manga( + new MangaId($id), + new MangaTitle($title), + new MangaSlug(strtolower(str_replace(' ', '-', $title))), + 'Description', + 'Author', + $year, + [], + 'ongoing' + ); + } + + protected function tearDown(): void + { + $this->repository->clear(); + } +} \ No newline at end of file diff --git a/tests/Feature/AbstractApiTestCase.php b/tests/Feature/AbstractApiTestCase.php new file mode 100644 index 0000000..0f8345e --- /dev/null +++ b/tests/Feature/AbstractApiTestCase.php @@ -0,0 +1,25 @@ +container = static::getContainer(); + $this->entityManager = $this->container->get(EntityManagerInterface::class); + } + + protected function tearDown(): void + { + parent::tearDown(); + } +} diff --git a/tests/Feature/Manga/GetMangaListTest.php b/tests/Feature/Manga/GetMangaListTest.php new file mode 100644 index 0000000..962a968 --- /dev/null +++ b/tests/Feature/Manga/GetMangaListTest.php @@ -0,0 +1,107 @@ +request('GET', '/api/mangas'); + + // Then + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + 'total' => 0, + 'page' => 1, + 'limit' => 20, + 'hasNextPage' => false, + 'hasPreviousPage' => false, + 'items' => [] + ]); + } + + public function testGetMangaListWithPagination(): void + { + // Given + $this->createMangas(25); + + // When + $client = static::createClient(); + $response = $client->request('GET', '/api/mangas', [ + 'query' => [ + 'page' => 2, + 'limit' => 10 + ] + ]); + + // Then + $this->assertResponseIsSuccessful(); + $data = $response->toArray(); + + $this->assertCount(10, $data['items']); + $this->assertEquals(25, $data['total']); + $this->assertEquals(2, $data['page']); + $this->assertEquals(10, $data['limit']); + $this->assertTrue($data['hasNextPage']); + $this->assertTrue($data['hasPreviousPage']); + } + + public function testGetMangaListWithSorting(): void + { + // Given + $this->createManga('Manga B'); + $this->createManga('Manga A'); + $this->createManga('Manga C'); + + // When + $client = static::createClient(); + $response = $client->request('GET', '/api/mangas', [ + 'query' => [ + 'sortBy' => 'title', + 'sortOrder' => 'asc' + ] + ]); + + // Then + $this->assertResponseIsSuccessful(); + $data = $response->toArray(); + + $this->assertCount(3, $data['items']); + $this->assertEquals('Manga A', $data['items'][0]['title']); + $this->assertEquals('Manga B', $data['items'][1]['title']); + $this->assertEquals('Manga C', $data['items'][2]['title']); + } + + private function createMangas(int $count): void + { + for ($i = 1; $i <= $count; $i++) { + $this->createManga("Manga $i"); + } + } + + private function createManga(string $title): void + { + $manga = new Manga(); + $manga->setTitle($title) + ->setSlug(strtolower(str_replace(' ', '-', $title))) + ->setDescription('Description test') + ->setAuthor('Author test') + ->setPublicationYear(2020) + ->setGenres(['action']) + ->setStatus('ongoing') + ->setRating(4.5) + ->setMonitored(false) + ; + + $this->entityManager->persist($manga); + $this->entityManager->flush(); + } +} diff --git a/tests/Feature/Scraping/AbstractApiTestCase.php b/tests/Feature/Scraping/AbstractApiTestCase.php deleted file mode 100644 index 4245ce8..0000000 --- a/tests/Feature/Scraping/AbstractApiTestCase.php +++ /dev/null @@ -1,18 +0,0 @@ -