Files
Gnuboard7/tests/Unit/Helpers/PermissionHelperTest.php
T
2026-04-01 10:30:52 +09:00

275 lines
9.2 KiB
PHP

<?php
namespace Tests\Unit\Helpers;
use App\Enums\PermissionType;
use App\Helpers\PermissionHelper;
use App\Models\Menu;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Tests\TestCase;
class PermissionHelperTest extends TestCase
{
use RefreshDatabase;
private Permission $permission;
private Permission $noScopePermission;
private User $ownerUser;
private User $otherUser;
private User $sharedRoleUser;
protected function setUp(): void
{
parent::setUp();
// static 캐시 초기화
$this->clearPermissionCache();
// 스코프가 있는 권한 (메뉴 관리)
$this->permission = Permission::create([
'identifier' => 'test.menus.update',
'name' => ['ko' => '메뉴 수정', 'en' => 'Update Menu'],
'type' => PermissionType::Admin,
'resource_route_key' => 'menu',
'owner_key' => 'created_by',
]);
// 스코프가 없는 권한 (시스템 리소스)
$this->noScopePermission = Permission::create([
'identifier' => 'test.settings.read',
'name' => ['ko' => '설정 조회', 'en' => 'Read Settings'],
'type' => PermissionType::Admin,
'resource_route_key' => null,
'owner_key' => null,
]);
// 사용자 생성
$this->ownerUser = User::factory()->create();
$this->otherUser = User::factory()->create();
$this->sharedRoleUser = User::factory()->create();
// 역할 생성 및 할당
$role = Role::create([
'identifier' => 'test_manager',
'name' => ['ko' => '매니저', 'en' => 'Manager'],
'is_active' => true,
]);
$role->permissions()->attach($this->permission->id, ['scope_type' => 'self']);
$role->permissions()->attach($this->noScopePermission->id, ['scope_type' => null]);
$this->ownerUser->roles()->attach($role->id);
$this->sharedRoleUser->roles()->attach($role->id);
}
protected function tearDown(): void
{
$this->clearPermissionCache();
parent::tearDown();
}
// ========================================================================
// checkScopeAccess 테스트
// ========================================================================
/**
* scope=null, 타인 리소스 → true (전체 접근)
*/
public function test_check_scope_access_null_scope_allows_all(): void
{
// null scope 역할 생성
$role = Role::create([
'identifier' => 'test_admin_full',
'name' => ['ko' => '전체 접근', 'en' => 'Full Access'],
'is_active' => true,
]);
$role->permissions()->attach($this->permission->id, ['scope_type' => null]);
$user = User::factory()->create();
$user->roles()->attach($role->id);
$menu = $this->createMenu(['created_by' => $this->otherUser->id]);
$this->assertTrue(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', $user));
}
/**
* scope='self', 자기 리소스 → true
*/
public function test_check_scope_access_self_allows_own_resource(): void
{
$menu = $this->createMenu(['created_by' => $this->ownerUser->id]);
$this->assertTrue(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', $this->ownerUser));
}
/**
* scope='self', 타인 리소스 → false
*/
public function test_check_scope_access_self_denies_other_resource(): void
{
$menu = $this->createMenu(['created_by' => $this->otherUser->id]);
$this->assertFalse(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', $this->ownerUser));
}
/**
* scope='role', 동일 역할 사용자의 리소스 → true
*/
public function test_check_scope_access_role_allows_shared_role_resource(): void
{
// role scope 역할
$role = Role::create([
'identifier' => 'test_role_scope',
'name' => ['ko' => '역할 스코프', 'en' => 'Role Scope'],
'is_active' => true,
]);
$role->permissions()->attach($this->permission->id, ['scope_type' => 'role']);
$userA = User::factory()->create();
$userB = User::factory()->create();
$userA->roles()->attach($role->id);
$userB->roles()->attach($role->id);
$menu = $this->createMenu(['created_by' => $userB->id]);
$this->assertTrue(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', $userA));
}
/**
* scope='role', 다른 역할 사용자의 리소스 → false
*/
public function test_check_scope_access_role_denies_different_role_resource(): void
{
$role = Role::create([
'identifier' => 'test_role_scope2',
'name' => ['ko' => '역할 스코프2', 'en' => 'Role Scope2'],
'is_active' => true,
]);
$role->permissions()->attach($this->permission->id, ['scope_type' => 'role']);
$userA = User::factory()->create();
$userA->roles()->attach($role->id);
// otherUser has no role
$menu = $this->createMenu(['created_by' => $this->otherUser->id]);
$this->assertFalse(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', $userA));
}
/**
* resource_route_key=null → true (스코프 체크 스킵)
*/
public function test_check_scope_access_skips_when_no_resource_route_key(): void
{
$menu = $this->createMenu(['created_by' => $this->otherUser->id]);
$this->assertTrue(PermissionHelper::checkScopeAccess($menu, 'test.settings.read', $this->ownerUser));
}
/**
* 비인증 사용자 → true (스코프 체크 스킵)
*/
public function test_check_scope_access_skips_for_unauthenticated_user(): void
{
$menu = $this->createMenu(['created_by' => $this->otherUser->id]);
$this->assertTrue(PermissionHelper::checkScopeAccess($menu, 'test.menus.update', null));
}
// ========================================================================
// applyPermissionScope 테스트
// ========================================================================
/**
* scope=null → 쿼리 변경 없음
*/
public function test_apply_scope_null_does_not_modify_query(): void
{
$role = Role::create([
'identifier' => 'test_full_access',
'name' => ['ko' => '전체', 'en' => 'Full'],
'is_active' => true,
]);
$role->permissions()->attach($this->permission->id, ['scope_type' => null]);
$user = User::factory()->create();
$user->roles()->attach($role->id);
// 메뉴 3개 생성
$this->createMenu(['created_by' => $user->id]);
$this->createMenu(['created_by' => $this->otherUser->id]);
$this->createMenu(['created_by' => $this->ownerUser->id]);
$query = Menu::query();
PermissionHelper::applyPermissionScope($query, 'test.menus.update', $user);
$this->assertSame(3, $query->count());
}
/**
* scope='self' → WHERE owner_key = user_id 추가
*/
public function test_apply_scope_self_filters_own_resources(): void
{
$this->createMenu(['created_by' => $this->ownerUser->id]);
$this->createMenu(['created_by' => $this->otherUser->id]);
$query = Menu::query();
PermissionHelper::applyPermissionScope($query, 'test.menus.update', $this->ownerUser);
$this->assertSame(1, $query->count());
}
/**
* owner_key=null → 쿼리 변경 없음
*/
public function test_apply_scope_no_owner_key_does_not_modify_query(): void
{
$this->createMenu(['created_by' => $this->ownerUser->id]);
$this->createMenu(['created_by' => $this->otherUser->id]);
$query = Menu::query();
PermissionHelper::applyPermissionScope($query, 'test.settings.read', $this->ownerUser);
$this->assertSame(2, $query->count());
}
/**
* 비인증 사용자 → 쿼리 변경 없음
*/
public function test_apply_scope_unauthenticated_does_not_modify_query(): void
{
$this->createMenu(['created_by' => $this->ownerUser->id]);
$query = Menu::query();
PermissionHelper::applyPermissionScope($query, 'test.menus.update', null);
$this->assertSame(1, $query->count());
}
// ========================================================================
// 헬퍼 메서드
// ========================================================================
private function createMenu(array $overrides = []): Menu
{
return Menu::create(array_merge([
'name' => ['ko' => '테스트 메뉴', 'en' => 'Test Menu'],
'slug' => 'test-menu-' . uniqid(),
'url' => '/admin/test',
'icon' => 'fas fa-cog',
'order' => 1,
'is_active' => true,
], $overrides));
}
private function clearPermissionCache(): void
{
$reflection = new \ReflectionClass(PermissionHelper::class);
$prop = $reflection->getProperty('permissionCache');
$prop->setAccessible(true);
$prop->setValue(null, []);
}
}