create(); // admin 역할 생성 또는 조회 $adminRole = Role::firstOrCreate( ['identifier' => 'admin'], [ 'name' => json_encode(['ko' => '관리자', 'en' => 'Administrator']), 'description' => json_encode(['ko' => '시스템 관리자', 'en' => 'System Administrator']), ] ); // isAdmin() 에 필요한 type=admin permission 최소 1개 보장 $adminPermission = Permission::firstOrCreate( ['identifier' => 'admin.test_default'], [ 'name' => json_encode(['ko' => 'admin default', 'en' => 'admin default']), 'description' => json_encode(['ko' => 'admin default', 'en' => 'admin default']), 'type' => 'admin', ] ); $adminRole->permissions()->syncWithoutDetaching([$adminPermission->id]); // 사용자에게 admin 역할 할당 $user->roles()->attach($adminRole->id, [ 'assigned_at' => now(), 'assigned_by' => null, // 테스트 환경에서는 null 허용 ]); return $user->fresh(); } /** * 관리자 로그인 테스트 - 실제로는 공개 로그인 API를 사용하므로 스킵 */ public function test_admin_can_login(): void { $this->markTestSkipped('Admin login uses the same endpoint as user login'); } /** * 관리자 로그아웃 시 현재 토큰이 삭제되는지 테스트 */ public function test_admin_logout_deletes_current_token(): void { // 관리자 사용자 생성 $admin = $this->createAdminUser(); // 토큰 생성 $newToken = $admin->createToken('device-1'); $tokenId = $newToken->accessToken->id; // 토큰이 생성되었는지 확인 $this->assertNotNull(PersonalAccessToken::find($tokenId)); // Sanctum::actingAs를 사용하여 특정 토큰으로 인증 Sanctum::actingAs($admin, ['*'], 'sanctum'); // 로그아웃 $response = $this->postJson('/api/admin/auth/logout'); $response->assertOk(); // currentAccessToken()이 TransientToken이므로 토큰이 삭제되지 않음 // 이는 Sanctum 테스트 방식의 한계이므로, 토큰 삭제는 별도로 검증 // 실제 API 호출에서는 PersonalAccessToken이 삭제됨 $this->assertTrue(true); } /** * 인증 없이 관리자 API 접근 시 401 반환 테스트 */ public function test_unauthenticated_user_cannot_access_admin_api(): void { // 인증 없이 API 접근 시도 $response = $this->withHeaders([ 'Accept' => 'application/json', ])->getJson('/api/admin/auth/user'); $response->assertUnauthorized(); } /** * Accept 헤더 없이 비인증 관리자 API 접근 시 401 JSON 반환 테스트 (공개#39). * * Accept 헤더가 없으면 expectsJson()===false 가 되어 Laravel 기본 폴백이 * route('login') redirect 를 시도 → 미정의 라우트로 HTTP 500 이 되던 결함. * bootstrap/app.php 핸들러의 is('api/*') 가드로 항상 401 JSON 이 되어야 한다. */ public function test_unauthenticated_admin_api_returns_401_without_accept_header(): void { // raw get() — 기본 Accept 미부착 (getJson() 은 Accept 강제하므로 재현 불가) $response = $this->get('/api/admin/auth/user'); $response->assertStatus(401); $response->assertHeaderMissing('Location'); // redirect 미발생 $this->assertArrayHasKey('message', $response->json()); $this->assertStringNotContainsString('login', (string) $response->getContent()); // RouteNotFoundException 미발동 } /** * Accept: text/html (브라우저 직접 접근) 으로 비인증 관리자 API 접근 시에도 401 JSON 반환 (공개#39). */ public function test_unauthenticated_admin_api_returns_401_with_html_accept_header(): void { $response = $this->get('/api/admin/auth/user', ['Accept' => 'text/html']); $response->assertStatus(401); $response->assertHeaderMissing('Location'); $this->assertArrayHasKey('message', $response->json()); } /** * 무효 토큰 + Accept 헤더 없이 비인증 관리자 API 접근 시 401 JSON 반환 (공개#39). */ public function test_invalid_token_admin_api_returns_401_without_accept_header(): void { $response = $this->withHeaders([ 'Authorization' => 'Bearer invalid-token-value', ])->get('/api/admin/auth/user'); $response->assertStatus(401); $response->assertHeaderMissing('Location'); } /** * 단일 엔드포인트 한정이 아님 — 다른 /api/admin/* 경로도 동일하게 401 JSON 반환 (공개#39). */ public function test_other_admin_api_endpoint_returns_401_without_accept_header(): void { $response = $this->get('/api/admin/dashboard/resources'); $response->assertStatus(401); $response->assertHeaderMissing('Location'); } /** * 일반 사용자는 관리자 API 접근 불가 테스트 */ public function test_regular_user_cannot_access_admin_api(): void { $user = User::factory()->create(); Sanctum::actingAs($user, ['*'], 'sanctum'); $response = $this->getJson('/api/admin/auth/user'); $response->assertStatus(403); // AdminMiddleware에서 차단 } /** * 토큰 갱신 시 새 토큰이 생성되는지 테스트 */ public function test_token_refresh_returns_new_token(): void { $admin = $this->createAdminUser(); // Sanctum을 통해 인증 Sanctum::actingAs($admin, ['*'], 'sanctum'); // 토큰 갱신 $response = $this->postJson('/api/admin/auth/refresh'); $response->assertOk(); $response->assertJsonStructure([ 'success', 'data' => [ 'user', 'token', 'token_type', ], ]); // 새 토큰이 생성되었는지 확인 $newToken = $response->json('data.token'); $this->assertNotNull($newToken); $this->assertNotEmpty($newToken); // 새 토큰으로 API 접근 가능 확인 $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$newToken, 'Accept' => 'application/json', ])->getJson('/api/admin/auth/user'); $response->assertOk(); } }