boardExistedBefore = File::isDirectory($activePath) && File::exists($activePath.'/module.php'); // 미설치 시 _bundled에서 복사 (테스트 실행을 위한 준비) if (! $this->boardExistedBefore && File::isDirectory($bundledPath)) { File::copyDirectory($bundledPath, $activePath); } // 확장 디렉토리 보호 활성화 (이후 모든 deleteDirectory 호출 차단) $this->setUpExtensionProtection(); $this->moduleManager = app(ModuleManager::class); $this->moduleManager->loadModules(); // 기본 admin role 생성 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, ]); // manager role 생성 (sirsoft-board 권한에서 참조) 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 { // 확장 디렉토리 보호 해제 (원래 File 파사드 복원) $this->tearDownExtensionProtection(); // 테스트에서 생성한 활성 디렉토리만 정리 (기존 설치분은 건드리지 않음) if (! $this->boardExistedBefore) { $activePath = base_path('modules/sirsoft-board'); if (File::isDirectory($activePath)) { File::deleteDirectory($activePath); } } parent::tearDown(); } /** * 모듈 설치 시 권한이 생성되는지 테스트 * * 계층형 구조로 권한이 생성됨: * - 1레벨: 모듈 노드 (sirsoft-board) * - 2레벨: 카테고리 노드 (sirsoft-board.boards, sirsoft-board.settings, sirsoft-board.reports) * - 3레벨: 개별 권한 (boards: read/create/update/delete, settings: read/update, reports: view/manage) */ public function test_module_installation_creates_permissions(): void { $this->moduleManager->installModule('sirsoft-board'); // 권한이 생성되었는지 확인 (계층형 구조: 모듈 1 + 카테고리 4 + 개별 권한 10 = 15개) // 카테고리: boards, settings, identity.policies (beta.4 추가), reports $permissions = Permission::where('extension_type', ExtensionOwnerType::Module) ->where('extension_identifier', 'sirsoft-board')->get(); $this->assertCount(15, $permissions); $identifiers = $permissions->pluck('identifier')->toArray(); // 1레벨: 모듈 노드 $this->assertContains('sirsoft-board', $identifiers); // 2레벨: 카테고리 노드 $this->assertContains('sirsoft-board.boards', $identifiers); $this->assertContains('sirsoft-board.settings', $identifiers); $this->assertContains('sirsoft-board.identity.policies', $identifiers); $this->assertContains('sirsoft-board.reports', $identifiers); // 3레벨: 개별 권한 - boards $this->assertContains('sirsoft-board.boards.read', $identifiers); $this->assertContains('sirsoft-board.boards.create', $identifiers); $this->assertContains('sirsoft-board.boards.update', $identifiers); $this->assertContains('sirsoft-board.boards.delete', $identifiers); // 3레벨: 개별 권한 - settings $this->assertContains('sirsoft-board.settings.read', $identifiers); $this->assertContains('sirsoft-board.settings.update', $identifiers); // 3레벨: 개별 권한 - identity.policies (beta.4) $this->assertContains('sirsoft-board.identity.policies.read', $identifiers); $this->assertContains('sirsoft-board.identity.policies.update', $identifiers); // 3레벨: 개별 권한 - reports $this->assertContains('sirsoft-board.reports.view', $identifiers); $this->assertContains('sirsoft-board.reports.manage', $identifiers); } /** * 모듈 설치 시 권한이 지정된 role에 할당되는지 테스트 */ public function test_module_installation_assigns_permissions_to_roles(): void { $this->moduleManager->installModule('sirsoft-board'); // admin role에 모든 개별 권한(10개)이 할당되었는지 확인 // (boards 4 + settings 2 + identity.policies 2 + reports 2 = 10, beta.4 부터) $adminRole = Role::where('identifier', 'admin')->first(); $adminPermissions = $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->get(); $this->assertCount(10, $adminPermissions); // manager role에 할당된 권한 확인 (boards: read, create, update + reports: view = 4개) $managerRole = Role::where('identifier', 'manager')->first(); $managerPermissions = $managerRole->permissions()->where('extension_identifier', 'sirsoft-board')->get(); $this->assertCount(4, $managerPermissions); $managerIdentifiers = $managerPermissions->pluck('identifier')->toArray(); $this->assertContains('sirsoft-board.boards.read', $managerIdentifiers); $this->assertContains('sirsoft-board.boards.create', $managerIdentifiers); $this->assertContains('sirsoft-board.boards.update', $managerIdentifiers); $this->assertContains('sirsoft-board.reports.view', $managerIdentifiers); } /** * 기존 role에 새 권한 추가 시 기존 권한이 유지되는지 테스트 */ public function test_existing_role_permissions_are_preserved(): void { // admin role에 기존 권한 추가 $adminRole = Role::where('identifier', 'admin')->first(); $existingPermission = Permission::create([ 'identifier' => 'core.test.permission', 'name' => ['ko' => '테스트 권한', 'en' => 'Test Permission'], 'description' => ['ko' => '테스트용 권한', 'en' => 'Test permission'], 'extension_type' => ExtensionOwnerType::Core, 'extension_identifier' => 'core', 'type' => PermissionType::Admin, ]); $adminRole->permissions()->attach($existingPermission->id); // 모듈 설치 $this->moduleManager->installModule('sirsoft-board'); // 기존 권한이 유지되는지 확인 $adminRole->refresh(); $this->assertTrue($adminRole->permissions()->where('identifier', 'core.test.permission')->exists()); // 새 권한도 추가되었는지 확인 $this->assertTrue($adminRole->permissions()->where('identifier', 'sirsoft-board.boards.read')->exists()); } /** * 모듈 제거 (deleteData=true) 시 권한이 role에서 분리되는지 테스트 * * 운영 정책(#716eef99d, 2026-04-20): deleteData=true 경로에서만 권한 detach + 삭제. * deleteData=false 경로는 권한 보존 (재설치 시 사용자 역할 할당 복원 목적). */ public function test_module_uninstallation_detaches_permissions_from_roles(): void { $this->moduleManager->installModule('sirsoft-board'); // admin role에 권한이 있는지 확인 $adminRole = Role::where('identifier', 'admin')->first(); $this->assertTrue($adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->exists()); // 모듈 제거 — deleteData=true 로 권한 detach + 삭제 경로 사용 $this->moduleManager->uninstallModule('sirsoft-board', deleteData: true); // 권한이 분리되었는지 확인 $adminRole->refresh(); $this->assertFalse($adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->exists()); } /** * 모듈 제거 (deleteData=true) 시 권한이 삭제되는지 테스트 */ public function test_module_uninstallation_deletes_permissions(): void { $this->moduleManager->installModule('sirsoft-board'); // 권한이 존재하는지 확인 $this->assertTrue(Permission::where('extension_identifier', 'sirsoft-board')->exists()); // 모듈 제거 — deleteData=true 로 권한 전수 삭제 경로 사용 $this->moduleManager->uninstallModule('sirsoft-board', deleteData: true); // 권한이 삭제되었는지 확인 $this->assertFalse(Permission::where('extension_identifier', 'sirsoft-board')->exists()); } /** * 시스템 role은 모듈 제거 시에도 삭제되지 않는지 테스트 */ public function test_system_roles_are_not_deleted_on_uninstall(): void { $this->moduleManager->installModule('sirsoft-board'); $this->moduleManager->uninstallModule('sirsoft-board'); // 시스템 admin role은 삭제되지 않음 $this->assertTrue(Role::where('identifier', 'admin')->exists()); } /** * getPermissions() 메서드가 roles 필드를 포함하는지 테스트 * * 계층형 구조에서 개별 권한(3레벨)에 roles 필드가 있는지 확인 */ public function test_get_permissions_includes_roles_field(): void { $this->moduleManager->installModule('sirsoft-board'); $module = $this->moduleManager->getModule('sirsoft-board'); $permissionConfig = $module->getPermissions(); $this->assertIsArray($permissionConfig); $this->assertArrayHasKey('categories', $permissionConfig); $this->assertNotEmpty($permissionConfig['categories']); // 계층형 구조에서 개별 권한 검증 foreach ($permissionConfig['categories'] as $category) { $this->assertArrayHasKey('identifier', $category); $this->assertArrayHasKey('name', $category); $this->assertArrayHasKey('description', $category); $this->assertArrayHasKey('permissions', $category); foreach ($category['permissions'] as $permission) { $this->assertArrayHasKey('action', $permission); $this->assertArrayHasKey('name', $permission); $this->assertArrayHasKey('description', $permission); $this->assertArrayHasKey('roles', $permission); $this->assertIsArray($permission['roles']); } } // boards 카테고리의 권한 확인 $boardsCategory = collect($permissionConfig['categories'])->firstWhere('identifier', 'boards'); $this->assertNotNull($boardsCategory); // read 권한은 admin과 manager 둘 다에게 할당 $readPermission = collect($boardsCategory['permissions'])->firstWhere('action', 'read'); $this->assertContains('admin', $readPermission['roles']); $this->assertContains('manager', $readPermission['roles']); // delete 권한은 admin에게만 할당 $deletePermission = collect($boardsCategory['permissions'])->firstWhere('action', 'delete'); $this->assertContains('admin', $deletePermission['roles']); $this->assertNotContains('manager', $deletePermission['roles']); } /** * roles 필드가 없는 권한은 role에 할당되지 않는지 테스트 * * 계층형 구조에서 개별 권한(3레벨)만 role에 할당됨. * 모듈 노드(1레벨)와 카테고리 노드(2레벨)는 할당되지 않음. */ public function test_permissions_without_roles_field_are_not_assigned(): void { $this->moduleManager->installModule('sirsoft-board'); // 계층형 구조에서 개별 권한(3레벨)만 role에 할당됨 // 모듈의 getPermissions()에서 정의된 개별 권한 10개만 할당되어야 함 (beta.4 부터) $adminRole = Role::where('identifier', 'admin')->first(); // admin role에 할당된 모듈 권한 개수 확인 (개별 권한 10개) $assignedPermissions = $adminRole->permissions()->where('extension_identifier', 'sirsoft-board')->count(); $this->assertEquals(10, $assignedPermissions); // 전체 모듈 권한 개수 확인 (모듈 1 + 카테고리 4 + 개별 10 = 15개) $totalModulePermissions = Permission::where('extension_identifier', 'sirsoft-board')->count(); $this->assertEquals(15, $totalModulePermissions); // 개별 권한만 role에 할당되었는지 확인 $assignedIdentifiers = $adminRole->permissions() ->where('extension_identifier', 'sirsoft-board') ->pluck('identifier') ->toArray(); $this->assertContains('sirsoft-board.boards.read', $assignedIdentifiers); $this->assertContains('sirsoft-board.boards.create', $assignedIdentifiers); $this->assertContains('sirsoft-board.boards.update', $assignedIdentifiers); $this->assertContains('sirsoft-board.boards.delete', $assignedIdentifiers); $this->assertContains('sirsoft-board.settings.read', $assignedIdentifiers); $this->assertContains('sirsoft-board.settings.update', $assignedIdentifiers); $this->assertContains('sirsoft-board.reports.view', $assignedIdentifiers); $this->assertContains('sirsoft-board.reports.manage', $assignedIdentifiers); // 모듈 노드와 카테고리 노드는 할당되지 않음 $this->assertNotContains('sirsoft-board', $assignedIdentifiers); $this->assertNotContains('sirsoft-board.boards', $assignedIdentifiers); $this->assertNotContains('sirsoft-board.settings', $assignedIdentifiers); $this->assertNotContains('sirsoft-board.reports', $assignedIdentifiers); } }