From 0dd7eba81474d2ef26bf2d983852bfe965edef47 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Sat, 8 Aug 2026 18:34:47 +0900 Subject: [PATCH 1/3] feat: add getFavorites to FavoriteController and route --- .../Controller/FavoriteController.php | 25 +++++++++++++++++++ src/routes/api.php | 1 + 2 files changed, 26 insertions(+) diff --git a/src/app/Packages/Features/Controller/FavoriteController.php b/src/app/Packages/Features/Controller/FavoriteController.php index 34561f3..10004a5 100644 --- a/src/app/Packages/Features/Controller/FavoriteController.php +++ b/src/app/Packages/Features/Controller/FavoriteController.php @@ -4,6 +4,7 @@ use App\Http\Controllers\Controller; use App\Packages\Features\CommandUseCases\UseCase\Favorite\AddFavoriteUseCase; +use App\Packages\Features\QueryUseCases\UseCase\Favorite\GetFavoriteHeritagesUseCase; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -11,6 +12,30 @@ class FavoriteController extends Controller { + public function getFavorites( + Request $request, + GetFavoriteHeritagesUseCase $useCase, + ): JsonResponse { + try { + $dtoCollection = $useCase->handle($request->user()->id); + + return response()->json([ + 'status' => 'success', + 'data' => $dtoCollection->toSummaryArray(), + ], 200); + } catch (Throwable $throwable) { + Log::error('Failed to get favorites', [ + 'message' => $throwable->getMessage(), + 'trace' => $throwable->getTraceAsString(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ], 500); + } + } + public function addFavorite( Request $request, AddFavoriteUseCase $useCase, diff --git a/src/routes/api.php b/src/routes/api.php index 73b2795..4c58d27 100644 --- a/src/routes/api.php +++ b/src/routes/api.php @@ -29,6 +29,7 @@ }); Route::controller(FavoriteController::class)->prefix('favorites')->middleware('auth:sanctum')->group(function (): void { + Route::get('/', 'getFavorites'); Route::post('/', 'addFavorite'); }); }); \ No newline at end of file From 206baef635a015ee1e2463190349f2681c8a3efe Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Sat, 8 Aug 2026 18:34:47 +0900 Subject: [PATCH 2/3] test: cover getFavorites in FavoriteControllerTest --- .../Features/Tests/FavoriteControllerTest.php | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/app/Packages/Features/Tests/FavoriteControllerTest.php b/src/app/Packages/Features/Tests/FavoriteControllerTest.php index f734f4b..99f360c 100644 --- a/src/app/Packages/Features/Tests/FavoriteControllerTest.php +++ b/src/app/Packages/Features/Tests/FavoriteControllerTest.php @@ -6,6 +6,8 @@ use App\Models\WorldHeritage; use App\Packages\Domains\Favorite\Interface\FavoriteRepositoryInterface; use App\Packages\Features\CommandUseCases\UseCase\Favorite\AddFavoriteUseCase; +use App\Packages\Features\QueryUseCases\QueryServiceInterface\WorldHeritageQueryServiceInterface; +use App\Packages\Features\QueryUseCases\UseCase\Favorite\GetFavoriteHeritagesUseCase; use Illuminate\Support\Facades\DB; use Mockery; use RuntimeException; @@ -110,4 +112,68 @@ public function test_addFavorite_returns_500_on_unexpected_error(): void 'message' => 'Internal Server Error', ]); } + + public function test_getFavorites_returns_200_with_favorite_heritages_when_authenticated(): void + { + $user = $this->seedUser(); + $worldHeritage1 = $this->seedWorldHeritage(1); + $worldHeritage2 = $this->seedWorldHeritage(2); + $token = $user->createToken('auth-token')->plainTextToken; + + $user->favorites()->attach($worldHeritage1->id); + $user->favorites()->attach($worldHeritage2->id); + + $response = $this->withToken($token)->getJson('/api/v1/favorites'); + + $response->assertStatus(200) + ->assertJsonFragment(['status' => 'success']) + ->assertJsonCount(2, 'data'); + + $ids = array_column($response->json('data'), 'id'); + $this->assertEqualsCanonicalizing([$worldHeritage1->id, $worldHeritage2->id], $ids); + } + + public function test_getFavorites_returns_200_with_empty_array_when_user_has_no_favorites(): void + { + $user = $this->seedUser(); + $token = $user->createToken('auth-token')->plainTextToken; + + $response = $this->withToken($token)->getJson('/api/v1/favorites'); + + $response->assertStatus(200) + ->assertJsonFragment(['status' => 'success']) + ->assertJsonCount(0, 'data'); + } + + public function test_getFavorites_returns_401_when_unauthenticated(): void + { + $response = $this->getJson('/api/v1/favorites'); + + $response->assertStatus(401); + } + + public function test_getFavorites_returns_500_on_unexpected_error(): void + { + $user = $this->seedUser(); + $token = $user->createToken('auth-token')->plainTextToken; + + $favoriteRepository = Mockery::mock(FavoriteRepositoryInterface::class); + $favoriteRepository->shouldReceive('getFavoriteWorldHeritageIds') + ->andThrow(new RuntimeException('Unexpected error')); + + $worldHeritageQueryService = Mockery::mock(WorldHeritageQueryServiceInterface::class); + + $this->app->instance( + GetFavoriteHeritagesUseCase::class, + new GetFavoriteHeritagesUseCase($favoriteRepository, $worldHeritageQueryService), + ); + + $response = $this->withToken($token)->getJson('/api/v1/favorites'); + + $response->assertStatus(500) + ->assertJsonFragment([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ]); + } } From 0c4e887991255f74e0d2062be4b8f82f3831d432 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Sat, 8 Aug 2026 18:53:34 +0900 Subject: [PATCH 3/3] test: stub WorldHeritageSearchPort in FavoriteControllerTest to avoid real Algolia client resolution --- .../Features/Tests/FavoriteControllerTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/Packages/Features/Tests/FavoriteControllerTest.php b/src/app/Packages/Features/Tests/FavoriteControllerTest.php index 99f360c..b9f6a76 100644 --- a/src/app/Packages/Features/Tests/FavoriteControllerTest.php +++ b/src/app/Packages/Features/Tests/FavoriteControllerTest.php @@ -5,6 +5,8 @@ use App\Models\User; use App\Models\WorldHeritage; use App\Packages\Domains\Favorite\Interface\FavoriteRepositoryInterface; +use App\Packages\Domains\WorldHeritage\Ports\Dto\HeritageSearchResult; +use App\Packages\Domains\WorldHeritage\Ports\WorldHeritageSearchPort; use App\Packages\Features\CommandUseCases\UseCase\Favorite\AddFavoriteUseCase; use App\Packages\Features\QueryUseCases\QueryServiceInterface\WorldHeritageQueryServiceInterface; use App\Packages\Features\QueryUseCases\UseCase\Favorite\GetFavoriteHeritagesUseCase; @@ -19,6 +21,15 @@ protected function setUp(): void { parent::setUp(); $this->truncate(); + + $this->app->bind(WorldHeritageSearchPort::class, static function () { + return new class implements WorldHeritageSearchPort { + public function search($query, int $currentPage, int $perPage): HeritageSearchResult + { + return new HeritageSearchResult(ids: [], total: 0, currentPage: 1, perPage: $perPage, lastPage: 0); + } + }; + }); } protected function tearDown(): void