From 6e566fa9e73bb1d499fa2dfe5b3173974f439568 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Mon, 20 Jul 2026 17:45:29 +0900 Subject: [PATCH 1/2] feat: add me action to AuthController and register GET /v1/user/me route --- .../Features/Controller/AuthController.php | 31 +++++++++++++++++++ src/routes/api.php | 1 + 2 files changed, 32 insertions(+) diff --git a/src/app/Packages/Features/Controller/AuthController.php b/src/app/Packages/Features/Controller/AuthController.php index 40ef398..1bfcff7 100644 --- a/src/app/Packages/Features/Controller/AuthController.php +++ b/src/app/Packages/Features/Controller/AuthController.php @@ -5,6 +5,8 @@ use App\Http\Controllers\Controller; use App\Packages\Features\CommandUseCases\UseCase\User\LoginUseCase; use App\Packages\Features\CommandUseCases\UseCase\User\LogoutUseCase; +use App\Packages\Features\QueryUseCases\Factory\ViewModel\UserViewModelFactory; +use App\Packages\Features\QueryUseCases\UseCase\User\GetUserByIdUseCase; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -68,4 +70,33 @@ public function logout(Request $request, LogoutUseCase $useCase): JsonResponse ], 500); } } + + public function me(Request $request, GetUserByIdUseCase $useCase): JsonResponse + { + try { + $dto = $useCase->handle($request->user()->id); + + return response()->json([ + 'status' => 'success', + 'data' => UserViewModelFactory::build($dto)->toArray(), + ], 200); + } catch (Throwable $throw) { + if ($throw->getMessage() === 'User not found.') { + return response()->json([ + 'status' => 'error', + 'message' => 'User not found.', + ], 404); + } + + Log::error('Failed to get current user', [ + 'message' => $throw->getMessage(), + 'trace' => $throw->getTraceAsString(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ], 500); + } + } } diff --git a/src/routes/api.php b/src/routes/api.php index 3b734cc..3d84d52 100644 --- a/src/routes/api.php +++ b/src/routes/api.php @@ -18,6 +18,7 @@ Route::controller(AuthController::class)->prefix('user')->group(function (): void { Route::post('/login', 'login'); Route::post('/logout', 'logout')->middleware('auth:sanctum'); + Route::get('/me', 'me')->middleware('auth:sanctum'); }); Route::controller(UserController::class)->prefix('users')->group(function (): void { From 9489ee7997fe2e0a5a4686100f5491c41328d0f8 Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Mon, 20 Jul 2026 17:45:33 +0900 Subject: [PATCH 2/2] test: add integration tests for GET /api/v1/user/me --- src/app/Packages/Features/Tests/MeTest.php | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/app/Packages/Features/Tests/MeTest.php diff --git a/src/app/Packages/Features/Tests/MeTest.php b/src/app/Packages/Features/Tests/MeTest.php new file mode 100644 index 0000000..847c403 --- /dev/null +++ b/src/app/Packages/Features/Tests/MeTest.php @@ -0,0 +1,100 @@ +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('personal_access_tokens')->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, + ]); + } + + public function test_me_returns_200_with_authenticated_user_when_authenticated(): void + { + $user = $this->seedUser(); + $token = $user->createToken('auth-token')->plainTextToken; + + $response = $this->withToken($token) + ->getJson('/api/v1/user/me'); + + $response->assertStatus(200) + ->assertJsonFragment([ + 'status' => 'success', + 'data' => [ + 'id' => $user->id, + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'john@example.com', + 'age_range' => '20s', + 'subscription_tier' => 'free', + 'subscription_expires_at' => null, + ], + ]); + } + + public function test_me_returns_401_when_unauthenticated(): void + { + $response = $this->getJson('/api/v1/user/me'); + + $response->assertStatus(401); + } + + public function test_me_returns_500_on_unexpected_error(): void + { + $user = $this->seedUser(); + $token = $user->createToken('auth-token')->plainTextToken; + + $repository = Mockery::mock(UserRepositroyInterface::class); + $repository->shouldReceive('findById') + ->andThrow(new RuntimeException('Unexpected error')); + + $this->app->instance(GetUserByIdUseCase::class, new GetUserByIdUseCase($repository)); + + $response = $this->withToken($token) + ->getJson('/api/v1/user/me'); + + $response->assertStatus(500) + ->assertJsonFragment([ + 'status' => 'error', + 'message' => 'Internal Server Error', + ]); + } +} \ No newline at end of file