353 lines
12 KiB
PHP
353 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Helpers;
|
|
|
|
use App\Enums\PermissionType;
|
|
use App\Enums\ScheduleType;
|
|
use App\Enums\ScopeType;
|
|
use App\Helpers\PermissionHelper;
|
|
use App\Models\Menu;
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\Schedule;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class PermissionHelperScopeTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Permission $permission;
|
|
|
|
private User $scopeUser;
|
|
|
|
private User $otherUser;
|
|
|
|
private User $sameRoleUser;
|
|
|
|
private Role $role;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// static 캐시 초기화
|
|
$this->clearPermissionCache();
|
|
|
|
// 기본 권한 생성
|
|
$this->permission = Permission::create([
|
|
'identifier' => 'test.scope.read',
|
|
'name' => ['ko' => '스코프 테스트 조회', 'en' => 'Scope Test Read'],
|
|
'type' => PermissionType::Admin,
|
|
'resource_route_key' => 'menu',
|
|
'owner_key' => 'created_by',
|
|
]);
|
|
|
|
// 사용자 생성
|
|
$this->scopeUser = User::factory()->create();
|
|
$this->otherUser = User::factory()->create();
|
|
$this->sameRoleUser = User::factory()->create();
|
|
|
|
// 역할 생성 및 할당
|
|
$this->role = Role::create([
|
|
'identifier' => 'test_scope_role',
|
|
'name' => ['ko' => '스코프 역할', 'en' => 'Scope Role'],
|
|
'is_active' => true,
|
|
]);
|
|
$this->role->permissions()->attach($this->permission->id, ['scope_type' => ScopeType::Self]);
|
|
$this->scopeUser->roles()->attach($this->role->id);
|
|
$this->sameRoleUser->roles()->attach($this->role->id);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
$this->clearPermissionCache();
|
|
parent::tearDown();
|
|
}
|
|
|
|
// ========================================================================
|
|
// applyPermissionScope — 코어: Menu (owner_key='created_by')
|
|
// ========================================================================
|
|
|
|
/**
|
|
* Menu — scope=null → 전체 메뉴 조회 (쿼리 변경 없음)
|
|
*/
|
|
public function test_apply_scope_null_returns_all_menus(): void
|
|
{
|
|
$this->setScopeType(null);
|
|
|
|
$this->createMenu(['created_by' => $this->scopeUser->id]);
|
|
$this->createMenu(['created_by' => $this->otherUser->id]);
|
|
$this->createMenu(['created_by' => $this->sameRoleUser->id]);
|
|
|
|
$query = Menu::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.scope.read', $this->scopeUser);
|
|
|
|
$this->assertSame(3, $query->count());
|
|
}
|
|
|
|
/**
|
|
* Menu — scope=self → 자기가 만든 메뉴만 조회
|
|
*/
|
|
public function test_apply_scope_self_filters_own_menus(): void
|
|
{
|
|
$this->createMenu(['created_by' => $this->scopeUser->id]);
|
|
$this->createMenu(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Menu::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.scope.read', $this->scopeUser);
|
|
|
|
$this->assertSame(1, $query->count());
|
|
}
|
|
|
|
/**
|
|
* Menu — scope=role → 동일 역할 사용자가 만든 메뉴만 조회
|
|
*/
|
|
public function test_apply_scope_role_filters_same_role_menus(): void
|
|
{
|
|
$this->setScopeType(ScopeType::Role);
|
|
|
|
$this->createMenu(['created_by' => $this->scopeUser->id]);
|
|
$this->createMenu(['created_by' => $this->sameRoleUser->id]);
|
|
$this->createMenu(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Menu::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.scope.read', $this->scopeUser);
|
|
|
|
$this->assertSame(2, $query->count());
|
|
}
|
|
|
|
// ========================================================================
|
|
// applyPermissionScope — 코어: User (owner_key='id')
|
|
// ========================================================================
|
|
|
|
/**
|
|
* User — scope=null → 전체 사용자 조회
|
|
*/
|
|
public function test_apply_scope_null_returns_all_users(): void
|
|
{
|
|
$userPerm = $this->createPermissionWithScope('test.users.read', 'user', 'id', null);
|
|
|
|
$query = User::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.users.read', $this->scopeUser);
|
|
|
|
// setUp에서 3명 + 생성 안 해도 3명은 존재
|
|
$this->assertSame(User::count(), $query->count());
|
|
}
|
|
|
|
/**
|
|
* User — scope=self → 자기 자신만 조회 (WHERE id = user_id)
|
|
*/
|
|
public function test_apply_scope_self_filters_own_user(): void
|
|
{
|
|
$this->createPermissionWithScope('test.users.read', 'user', 'id', ScopeType::Self);
|
|
|
|
$query = User::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.users.read', $this->scopeUser);
|
|
|
|
$this->assertSame(1, $query->count());
|
|
$this->assertEquals($this->scopeUser->id, $query->first()->id);
|
|
}
|
|
|
|
/**
|
|
* User — scope=role → 동일 역할 사용자만 조회
|
|
*/
|
|
public function test_apply_scope_role_filters_same_role_users(): void
|
|
{
|
|
$this->createPermissionWithScope('test.users.read', 'user', 'id', ScopeType::Role);
|
|
|
|
$query = User::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.users.read', $this->scopeUser);
|
|
|
|
// scopeUser + sameRoleUser = 2명
|
|
$this->assertSame(2, $query->count());
|
|
$userIds = $query->pluck('id')->toArray();
|
|
$this->assertContains($this->scopeUser->id, $userIds);
|
|
$this->assertContains($this->sameRoleUser->id, $userIds);
|
|
}
|
|
|
|
// ========================================================================
|
|
// applyPermissionScope — 코어: Schedule (owner_key='created_by')
|
|
// ========================================================================
|
|
|
|
/**
|
|
* Schedule — scope=null → 전체 스케줄 조회
|
|
*/
|
|
public function test_apply_scope_null_returns_all_schedules(): void
|
|
{
|
|
$this->createPermissionWithScope('test.schedules.read', 'schedule', 'created_by', null);
|
|
|
|
$this->createSchedule(['created_by' => $this->scopeUser->id]);
|
|
$this->createSchedule(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Schedule::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.schedules.read', $this->scopeUser);
|
|
|
|
$this->assertSame(2, $query->count());
|
|
}
|
|
|
|
/**
|
|
* Schedule — scope=self → 자기가 만든 스케줄만 조회
|
|
*/
|
|
public function test_apply_scope_self_filters_own_schedules(): void
|
|
{
|
|
$this->createPermissionWithScope('test.schedules.read', 'schedule', 'created_by', ScopeType::Self);
|
|
|
|
$this->createSchedule(['created_by' => $this->scopeUser->id]);
|
|
$this->createSchedule(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Schedule::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.schedules.read', $this->scopeUser);
|
|
|
|
$this->assertSame(1, $query->count());
|
|
}
|
|
|
|
/**
|
|
* Schedule — scope=role → 동일 역할 사용자가 만든 스케줄만 조회
|
|
*/
|
|
public function test_apply_scope_role_filters_same_role_schedules(): void
|
|
{
|
|
$this->createPermissionWithScope('test.schedules.read', 'schedule', 'created_by', ScopeType::Role);
|
|
|
|
$this->createSchedule(['created_by' => $this->scopeUser->id]);
|
|
$this->createSchedule(['created_by' => $this->sameRoleUser->id]);
|
|
$this->createSchedule(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Schedule::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.schedules.read', $this->scopeUser);
|
|
|
|
$this->assertSame(2, $query->count());
|
|
}
|
|
|
|
// ========================================================================
|
|
// applyPermissionScope — owner_key=null 또는 비인증 사용자
|
|
// ========================================================================
|
|
|
|
/**
|
|
* owner_key=null → 쿼리 변경 없음 (스코프 체크 스킵)
|
|
*/
|
|
public function test_apply_scope_no_owner_key_does_not_modify_query(): void
|
|
{
|
|
Permission::create([
|
|
'identifier' => 'test.system.read',
|
|
'name' => ['ko' => '시스템 조회', 'en' => 'System Read'],
|
|
'type' => PermissionType::Admin,
|
|
'resource_route_key' => null,
|
|
'owner_key' => null,
|
|
]);
|
|
|
|
$this->createMenu(['created_by' => $this->scopeUser->id]);
|
|
$this->createMenu(['created_by' => $this->otherUser->id]);
|
|
|
|
$query = Menu::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.system.read', $this->scopeUser);
|
|
|
|
$this->assertSame(2, $query->count());
|
|
}
|
|
|
|
/**
|
|
* 비인증 사용자 → 쿼리 변경 없음
|
|
*/
|
|
public function test_apply_scope_unauthenticated_does_not_modify_query(): void
|
|
{
|
|
$this->createMenu(['created_by' => $this->scopeUser->id]);
|
|
|
|
$query = Menu::query();
|
|
PermissionHelper::applyPermissionScope($query, 'test.scope.read', null);
|
|
|
|
$this->assertSame(1, $query->count());
|
|
}
|
|
|
|
// ========================================================================
|
|
// 헬퍼 메서드
|
|
// ========================================================================
|
|
|
|
/**
|
|
* 테스트용 Menu 생성 헬퍼
|
|
*
|
|
* @param array $overrides 오버라이드할 필드
|
|
* @return Menu 생성된 메뉴
|
|
*/
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* 테스트용 Schedule 생성 헬퍼
|
|
*
|
|
* @param array $overrides 오버라이드할 필드
|
|
* @return Schedule 생성된 스케줄
|
|
*/
|
|
private function createSchedule(array $overrides = []): Schedule
|
|
{
|
|
return Schedule::create(array_merge([
|
|
'name' => 'Test Schedule '.uniqid(),
|
|
'type' => ScheduleType::Artisan,
|
|
'command' => 'test:command',
|
|
'expression' => '* * * * *',
|
|
'frequency' => 'everyMinute',
|
|
], $overrides));
|
|
}
|
|
|
|
/**
|
|
* 기본 permission의 scope_type 변경
|
|
*
|
|
* @param ScopeType|null $scopeType scope_type 값
|
|
* @return void
|
|
*/
|
|
private function setScopeType(?ScopeType $scopeType): void
|
|
{
|
|
$this->role->permissions()->syncWithoutDetaching([
|
|
$this->permission->id => ['scope_type' => $scopeType],
|
|
]);
|
|
$this->clearPermissionCache();
|
|
}
|
|
|
|
/**
|
|
* 별도 권한 생성 및 역할에 할당하는 헬퍼
|
|
*
|
|
* @param string $identifier 권한 식별자
|
|
* @param string|null $routeKey resource_route_key
|
|
* @param string|null $ownerKey owner_key
|
|
* @param ScopeType|null $scopeType scope_type
|
|
* @return Permission 생성된 권한
|
|
*/
|
|
private function createPermissionWithScope(string $identifier, ?string $routeKey, ?string $ownerKey, ?ScopeType $scopeType): Permission
|
|
{
|
|
$perm = Permission::create([
|
|
'identifier' => $identifier,
|
|
'name' => ['ko' => $identifier, 'en' => $identifier],
|
|
'type' => PermissionType::Admin,
|
|
'resource_route_key' => $routeKey,
|
|
'owner_key' => $ownerKey,
|
|
]);
|
|
|
|
$this->role->permissions()->attach($perm->id, ['scope_type' => $scopeType]);
|
|
$this->clearPermissionCache();
|
|
|
|
return $perm;
|
|
}
|
|
|
|
/**
|
|
* PermissionHelper static 캐시 초기화
|
|
*
|
|
* @return void
|
|
*/
|
|
private function clearPermissionCache(): void
|
|
{
|
|
$reflection = new \ReflectionClass(PermissionHelper::class);
|
|
$prop = $reflection->getProperty('permissionCache');
|
|
$prop->setAccessible(true);
|
|
$prop->setValue(null, []);
|
|
}
|
|
}
|