From b0b354ad093807892f926123e35a180f9e498fa9 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Fri, 7 Aug 2026 09:23:09 +0900 Subject: [PATCH 1/2] feat: add FavoriteController and route --- .../Controller/FavoriteController.php | 46 +++++++++++++++++++ src/routes/api.php | 5 ++ 2 files changed, 51 insertions(+) create mode 100644 src/app/Packages/Features/Controller/FavoriteController.php diff --git a/src/app/Packages/Features/Controller/FavoriteController.php b/src/app/Packages/Features/Controller/FavoriteController.php new file mode 100644 index 0000000..34561f3 --- /dev/null +++ b/src/app/Packages/Features/Controller/FavoriteController.php @@ -0,0 +1,46 @@ +handle( + $request->user()->id, + (int) $request->input('world_heritage_id'), + ); + + return response()->json([ + 'status' => 'success', + ], 201); + } catch (Throwable $throwable) { + if ($throwable->getMessage() === 'User not found.') { + return response()->json([ + 'status' => 'error', + 'message' => 'User not found.', + ], 404); + } + + Log::error('Failed to add favorite', [ + 'message' => $throwable->getMessage(), + 'trace' => $throwable->getTraceAsString(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ], 500); + } + } +} diff --git a/src/routes/api.php b/src/routes/api.php index 3d84d52..73b2795 100644 --- a/src/routes/api.php +++ b/src/routes/api.php @@ -1,6 +1,7 @@ prefix('favorites')->middleware('auth:sanctum')->group(function (): void { + Route::post('/', 'addFavorite'); + }); }); \ No newline at end of file From 2f70034c0414b7afd185d4b1108441f1ed52f965 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Fri, 7 Aug 2026 09:24:45 +0900 Subject: [PATCH 2/2] test: cover FavoriteController integration --- .../Features/Tests/FavoriteControllerTest.php | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/app/Packages/Features/Tests/FavoriteControllerTest.php diff --git a/src/app/Packages/Features/Tests/FavoriteControllerTest.php b/src/app/Packages/Features/Tests/FavoriteControllerTest.php new file mode 100644 index 0000000..f734f4b --- /dev/null +++ b/src/app/Packages/Features/Tests/FavoriteControllerTest.php @@ -0,0 +1,113 @@ +truncate(); + } + + protected function tearDown(): void + { + $this->truncate(); + parent::tearDown(); + } + + private function truncate(): void + { + if (env('APP_ENV') === 'testing') { + DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::connection('mysql')->table('user_favorite')->truncate(); + DB::connection('mysql')->table('personal_access_tokens')->truncate(); + WorldHeritage::truncate(); + User::truncate(); + DB::connection('mysql')->statement('SET FOREIGN_KEY_CHECKS=1;'); + } + } + + private function seedUser(): User + { + return User::create([ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'john@example.com', + 'password' => bcrypt('secret123'), + 'age_range' => '20s', + 'subscription_tier' => 'free', + 'subscription_expires_at' => null, + ]); + } + + private function seedWorldHeritage(int $id): WorldHeritage + { + return WorldHeritage::create([ + 'id' => $id, + 'official_name' => 'Example Site', + 'name' => 'Example Site', + 'region' => 'Asia', + 'category' => 'Cultural', + 'criteria' => ['i'], + 'year_inscribed' => 2000, + ]); + } + + public function test_addFavorite_returns_201_and_persists_favorite_when_authenticated(): void + { + $user = $this->seedUser(); + $worldHeritage = $this->seedWorldHeritage(1); + $token = $user->createToken('auth-token')->plainTextToken; + + $response = $this->withToken($token) + ->postJson('/api/v1/favorites', ['world_heritage_id' => $worldHeritage->id]); + + $response->assertStatus(201) + ->assertJsonFragment(['status' => 'success']); + + $this->assertDatabaseHas('user_favorite', [ + 'user_id' => $user->id, + 'world_heritage_site_id' => $worldHeritage->id, + ]); + } + + public function test_addFavorite_returns_401_when_unauthenticated(): void + { + $worldHeritage = $this->seedWorldHeritage(1); + + $response = $this->postJson('/api/v1/favorites', ['world_heritage_id' => $worldHeritage->id]); + + $response->assertStatus(401); + } + + public function test_addFavorite_returns_500_on_unexpected_error(): void + { + $user = $this->seedUser(); + $token = $user->createToken('auth-token')->plainTextToken; + + $repository = Mockery::mock(FavoriteRepositoryInterface::class); + $repository->shouldReceive('addFavorite') + ->andThrow(new RuntimeException('Unexpected error')); + + $this->app->instance(AddFavoriteUseCase::class, new AddFavoriteUseCase($repository)); + + $response = $this->withToken($token) + ->postJson('/api/v1/favorites', ['world_heritage_id' => 1]); + + $response->assertStatus(500) + ->assertJsonFragment([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ]); + } +}