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()); } }