326 lines
14 KiB
PHP
326 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Module;
|
|
|
|
use App\Enums\ExtensionOwnerType;
|
|
use App\Extension\ModuleManager;
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\File;
|
|
use ReflectionMethod;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* 모듈 역할/권한 동기화 통합 테스트
|
|
*
|
|
* ExtensionRoleSyncHelper를 통한 실제 DB 기반 동기화를 검증합니다:
|
|
* - Permission은 항상 확장 정의값으로 덮어쓰기
|
|
* - user_overrides 배열로 유저 커스터마이징 보존
|
|
* - DB 기반 diff로 권한 attach/detach
|
|
* - stale 권한 정리
|
|
*/
|
|
class ModuleRolePermissionSyncTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private ModuleManager $moduleManager;
|
|
|
|
/** @var bool 활성 디렉토리가 테스트 전에 이미 존재했는지 (tearDown에서 정리 판단용) */
|
|
private bool $boardExistedBefore = false;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// 이전 테스트에서 남은 잔존 디렉토리 정리
|
|
$activePath = base_path('modules/sirsoft-board');
|
|
if (File::isDirectory($activePath) && ! File::exists($activePath.'/module.php')) {
|
|
File::deleteDirectory($activePath);
|
|
}
|
|
|
|
$this->boardExistedBefore = File::isDirectory($activePath);
|
|
$this->moduleManager = app(ModuleManager::class);
|
|
$this->moduleManager->loadModules();
|
|
|
|
// 기본 시스템 역할 생성
|
|
Role::create([
|
|
'identifier' => 'admin',
|
|
'name' => ['ko' => '시스템 관리자', 'en' => 'System Administrator'],
|
|
'description' => ['ko' => '모든 권한을 가진 관리자', 'en' => 'Administrator with all permissions'],
|
|
'extension_type' => ExtensionOwnerType::Core,
|
|
'extension_identifier' => 'core',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
Role::create([
|
|
'identifier' => 'manager',
|
|
'name' => ['ko' => '매니저', 'en' => 'Manager'],
|
|
'description' => ['ko' => '관리 권한을 가진 매니저', 'en' => 'Manager with management permissions'],
|
|
'extension_type' => ExtensionOwnerType::Core,
|
|
'extension_identifier' => 'core',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
// 테스트용 사용자 생성
|
|
User::factory()->create(['email' => 'admin@test.com']);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
// 테스트에서 생성한 활성 디렉토리만 정리 (기존에 있었으면 건드리지 않음)
|
|
if (! $this->boardExistedBefore) {
|
|
$activePath = base_path('modules/sirsoft-board');
|
|
if (File::isDirectory($activePath)) {
|
|
File::deleteDirectory($activePath);
|
|
}
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* ModuleManager의 protected 메서드를 호출하는 헬퍼
|
|
*
|
|
* @param string $methodName 메서드명
|
|
* @param array $args 인자
|
|
*/
|
|
private function callProtectedMethod(string $methodName, array $args = []): mixed
|
|
{
|
|
$method = new ReflectionMethod($this->moduleManager, $methodName);
|
|
|
|
return $method->invokeArgs($this->moduleManager, $args);
|
|
}
|
|
|
|
/**
|
|
* 모듈 설치 시 권한이 생성되고 역할에 할당되는지 테스트
|
|
*/
|
|
public function test_module_install_creates_permissions_and_assigns_to_roles(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
// 권한 생성 확인 (모듈 루트 1 + 카테고리 4 + 리프 10 = 15)
|
|
$permissions = Permission::where('extension_type', ExtensionOwnerType::Module)
|
|
->where('extension_identifier', 'sirsoft-board')->get();
|
|
$this->assertCount(15, $permissions);
|
|
|
|
// admin 역할에 할당된 sirsoft-board 권한 확인 (리프 10개)
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
$adminPermissions = $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get();
|
|
$this->assertCount(10, $adminPermissions);
|
|
|
|
// manager 역할에 할당된 sirsoft-board 권한 확인 (리프 4개)
|
|
$managerRole = Role::where('identifier', 'manager')->first();
|
|
$managerPermissions = $managerRole->permissions()->where('extension_identifier', 'sirsoft-board')->get();
|
|
$this->assertCount(4, $managerPermissions);
|
|
}
|
|
|
|
/**
|
|
* Permission은 항상 확장 정의값으로 덮어쓰기되는지 테스트
|
|
*/
|
|
public function test_reinstall_always_overwrites_permission(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
$readPerm = Permission::where('identifier', 'sirsoft-board.boards.read')->first();
|
|
$originalName = $readPerm->name;
|
|
|
|
// 재설치 (업데이트 시뮬레이션)
|
|
$module = $this->moduleManager->getModule('sirsoft-board');
|
|
$this->callProtectedMethod('createModulePermissions', [$module]);
|
|
|
|
$readPerm->refresh();
|
|
|
|
// 항상 확장 정의값으로 덮어씀
|
|
$this->assertEquals($originalName, $readPerm->name);
|
|
}
|
|
|
|
/**
|
|
* 최초 설치 시 역할에 user_overrides가 비어있는지 테스트
|
|
*/
|
|
public function test_no_user_overrides_on_fresh_install(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
$managerRole = Role::where('identifier', 'manager')->first();
|
|
|
|
// 최초 설치 시 user_overrides는 비어있거나 null
|
|
$this->assertTrue(
|
|
empty($adminRole->user_overrides),
|
|
'admin 역할의 user_overrides는 비어있어야 합니다'
|
|
);
|
|
|
|
$this->assertTrue(
|
|
empty($managerRole->user_overrides),
|
|
'manager 역할의 user_overrides는 비어있어야 합니다'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* user_overrides에 개별 권한 식별자가 있으면 해당 권한만 보호되는지 테스트
|
|
*
|
|
* 시나리오:
|
|
* 1. 모듈 설치 → admin에 8개 권한 할당
|
|
* 2. 유저가 admin에서 boards.delete 해제 + user_overrides에 "sirsoft-board.boards.delete" 기록
|
|
* 3. 재설치 (업데이트 시뮬레이션)
|
|
* 4. boards.delete만 보호 (해제 상태 유지), 나머지 권한은 정상 동기화
|
|
*/
|
|
public function test_user_permission_override_preserved_on_reinstall(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
// 초기 상태 확인 (admin에 리프 10개 할당)
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
$this->assertCount(10, $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get());
|
|
|
|
// 유저가 admin에서 boards.delete 권한 해제
|
|
$deletePerm = Permission::where('identifier', 'sirsoft-board.boards.delete')->first();
|
|
$adminRole->permissions()->detach($deletePerm->id);
|
|
$this->assertCount(9, $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get());
|
|
|
|
// user_overrides에 개별 권한 식별자 기록 (실제로는 Listener가 수행)
|
|
$adminRole->update(['user_overrides' => ['sirsoft-board.boards.delete']]);
|
|
|
|
// 재설치 (업데이트 시뮬레이션)
|
|
$module = $this->moduleManager->getModule('sirsoft-board');
|
|
$this->callProtectedMethod('assignPermissionsToRoles', [$module]);
|
|
|
|
// sirsoft-board.boards.delete만 보호 → 해제 상태 유지 → 9개 유지
|
|
$adminRole->refresh();
|
|
$this->assertCount(
|
|
9,
|
|
$adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get(),
|
|
'user_overrides에 개별 권한 식별자가 있으면 해당 권한만 보호되어야 합니다'
|
|
);
|
|
|
|
// boards.delete는 여전히 해제 상태
|
|
$this->assertFalse(
|
|
$adminRole->permissions()->where('identifier', 'sirsoft-board.boards.delete')->exists(),
|
|
'보호된 권한(boards.delete)은 해제 상태가 유지되어야 합니다'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* user_overrides에 없는 권한은 재설치 시 정상 동기화되는지 테스트
|
|
*
|
|
* 시나리오:
|
|
* 1. 모듈 설치 → admin에 8개 권한 할당
|
|
* 2. 유저가 boards.delete 해제 + user_overrides에 기록
|
|
* 3. 유저가 boards.read도 해제 (하지만 user_overrides에 미기록 — 가상 시나리오)
|
|
* 4. 재설치 → boards.delete는 보호, boards.read는 비보호이므로 다시 attach
|
|
*/
|
|
public function test_non_overridden_permissions_synced_normally(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
|
|
// 유저가 boards.delete 해제 + user_overrides 기록
|
|
$deletePerm = Permission::where('identifier', 'sirsoft-board.boards.delete')->first();
|
|
$adminRole->permissions()->detach($deletePerm->id);
|
|
$adminRole->update(['user_overrides' => ['sirsoft-board.boards.delete']]);
|
|
|
|
// boards.read도 해제 (user_overrides 미기록)
|
|
$readPerm = Permission::where('identifier', 'sirsoft-board.boards.read')->first();
|
|
$adminRole->permissions()->detach($readPerm->id);
|
|
|
|
// 리프 10개 중 2개 해제 → 8개
|
|
$this->assertCount(8, $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get());
|
|
|
|
// 재설치
|
|
$module = $this->moduleManager->getModule('sirsoft-board');
|
|
$this->callProtectedMethod('assignPermissionsToRoles', [$module]);
|
|
|
|
$adminRole->refresh();
|
|
|
|
// boards.read는 비보호 → 다시 attach → 9개
|
|
$this->assertTrue(
|
|
$adminRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists(),
|
|
'비보호 권한(boards.read)은 재설치 시 다시 attach되어야 합니다'
|
|
);
|
|
|
|
// boards.delete는 보호 → 해제 상태 유지
|
|
$this->assertFalse(
|
|
$adminRole->permissions()->where('identifier', 'sirsoft-board.boards.delete')->exists(),
|
|
'보호된 권한(boards.delete)은 해제 상태가 유지되어야 합니다'
|
|
);
|
|
|
|
$this->assertCount(
|
|
9,
|
|
$adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get(),
|
|
'비보호 권한만 동기화되고 보호 권한은 유지되어야 합니다'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* user_overrides가 없는 역할은 DB 기반 diff로 새 권한만 attach되는지 테스트
|
|
*
|
|
* 시나리오:
|
|
* 1. 모듈 설치 → manager에 4개 권한 할당
|
|
* 2. 유저가 manager에서 boards.read 해제 (user_overrides 미기록 — Listener 미동작 가정)
|
|
* 3. 재설치
|
|
* 4. DB 기반 diff → boards.read가 이미 없으므로 다시 attach됨
|
|
*/
|
|
public function test_db_based_diff_reattaches_removed_permission_without_user_overrides(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
$managerRole = Role::where('identifier', 'manager')->first();
|
|
$readPerm = Permission::where('identifier', 'sirsoft-board.boards.read')->first();
|
|
|
|
// 유저가 해제 (user_overrides 미기록)
|
|
$managerRole->permissions()->detach($readPerm->id);
|
|
$this->assertFalse($managerRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists());
|
|
|
|
// 재설치 (업데이트 시뮬레이션)
|
|
$module = $this->moduleManager->getModule('sirsoft-board');
|
|
$this->callProtectedMethod('assignPermissionsToRoles', [$module]);
|
|
|
|
// user_overrides 없음 → DB 기반 diff → boards.read가 다시 attach됨
|
|
$this->assertTrue(
|
|
$managerRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists(),
|
|
'user_overrides가 없으면 DB 기반 diff로 누락된 권한이 다시 attach되어야 합니다'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 사용자가 수동으로 추가한 역할 할당이 보존되는지 테스트
|
|
*/
|
|
public function test_user_added_role_assignments_are_preserved(): void
|
|
{
|
|
$this->moduleManager->installModule('sirsoft-board');
|
|
|
|
$customRole = Role::create([
|
|
'identifier' => 'custom-editor',
|
|
'name' => ['ko' => '커스텀 편집자', 'en' => 'Custom Editor'],
|
|
'description' => ['ko' => '사용자 정의 편집자 역할', 'en' => 'Custom editor role'],
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$readPerm = Permission::where('identifier', 'sirsoft-board.boards.read')->first();
|
|
$customRole->permissions()->attach($readPerm->id, [
|
|
'granted_at' => now(),
|
|
]);
|
|
|
|
// 재설치 (업데이트 시뮬레이션)
|
|
$module = $this->moduleManager->getModule('sirsoft-board');
|
|
$this->callProtectedMethod('createModulePermissions', [$module]);
|
|
$this->callProtectedMethod('assignPermissionsToRoles', [$module]);
|
|
|
|
// 사용자가 수동 추가한 할당이 보존됨
|
|
$this->assertTrue(
|
|
$customRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists(),
|
|
'사용자가 수동으로 추가한 역할 할당이 보존되어야 합니다'
|
|
);
|
|
|
|
// 기존 admin/manager 할당도 유지됨
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
$this->assertTrue($adminRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists());
|
|
|
|
$managerRole = Role::where('identifier', 'manager')->first();
|
|
$this->assertTrue($managerRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists());
|
|
}
|
|
}
|