211 lines
7.1 KiB
PHP
211 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\Admin;
|
|
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Laravel\Sanctum\PersonalAccessToken;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use Tests\TestCase;
|
|
|
|
class AdminAuthTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* 관리자 역할 생성 및 할당
|
|
*
|
|
* isAdmin() 은 role 에 type='admin' permission 이 하나라도 있어야 true.
|
|
* 테스트 통과를 위해 admin.default permission 을 기본 생성하여 role 에 연결.
|
|
*/
|
|
private function createAdminUser(): User
|
|
{
|
|
$user = User::factory()->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();
|
|
}
|
|
}
|