# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Unit tests for RBAC bootstrap initialization. Tests that bootstrap creates correct rules and initial data. """ import pytest from unittest.mock import Mock, MagicMock, patch from modules.interfaces.interfaceBootstrap import ( initBootstrap, initRootMandate, initAdminUser, initEventUser, initRbacRules, createDefaultRoleRules, createTableSpecificRules ) from modules.datamodels.datamodelUam import UserInDB, Mandate, AuthAuthority from modules.datamodels.datamodelRbac import AccessRule, AccessRuleContext from modules.datamodels.datamodelUam import AccessLevel class TestRbacBootstrap: """Test RBAC bootstrap initialization.""" def testInitRootMandateCreatesIfNotExists(self): """Test that initRootMandate creates mandate if it doesn't exist.""" db = Mock() db.getRecordset = Mock(return_value=[]) # No existing mandates db.recordCreate = Mock(return_value={"id": "mandate1", "name": "Root"}) mandateId = initRootMandate(db) assert mandateId == "mandate1" db.recordCreate.assert_called_once() callArgs = db.recordCreate.call_args assert isinstance(callArgs[0][1], Mandate) assert callArgs[0][1].name == "Root" def testInitRootMandateReturnsExisting(self): """Test that initRootMandate returns existing mandate ID.""" db = Mock() db.getRecordset = Mock(return_value=[{"id": "existing_mandate"}]) mandateId = initRootMandate(db) assert mandateId == "existing_mandate" db.recordCreate.assert_not_called() def testInitAdminUserCreatesWithSysadminRole(self): """Test that initAdminUser creates user with sysadmin role.""" db = Mock() db.getRecordset = Mock(return_value=[]) # No existing users db.recordCreate = Mock(return_value={"id": "admin1", "username": "admin"}) with patch('modules.interfaces.interfaceBootstrap._getPasswordHash', return_value="hashed"): userId = initAdminUser(db, "mandate1") assert userId == "admin1" db.recordCreate.assert_called_once() callArgs = db.recordCreate.call_args user = callArgs[0][1] assert isinstance(user, UserInDB) assert user.username == "admin" assert "sysadmin" in user.roleLabels def testInitEventUserCreatesWithSysadminRole(self): """Test that initEventUser creates user with sysadmin role.""" db = Mock() db.getRecordset = Mock(return_value=[]) # No existing users db.recordCreate = Mock(return_value={"id": "event1", "username": "event"}) with patch('modules.interfaces.interfaceBootstrap._getPasswordHash', return_value="hashed"): userId = initEventUser(db, "mandate1") assert userId == "event1" db.recordCreate.assert_called_once() callArgs = db.recordCreate.call_args user = callArgs[0][1] assert isinstance(user, UserInDB) assert user.username == "event" assert "sysadmin" in user.roleLabels def testCreateDefaultRoleRules(self): """Test that createDefaultRoleRules creates correct default rules.""" db = Mock() db.recordCreate = Mock() createDefaultRoleRules(db) # Should create 4 default rules (sysadmin, admin, user, viewer) assert db.recordCreate.call_count == 4 # Check sysadmin rule sysadminCall = [call for call in db.recordCreate.call_args_list if call[0][1].roleLabel == "sysadmin"][0] sysadminRule = sysadminCall[0][1] assert sysadminRule.context == AccessRuleContext.DATA assert sysadminRule.item is None assert sysadminRule.view == True assert sysadminRule.read == AccessLevel.ALL assert sysadminRule.create == AccessLevel.ALL # Check user rule userCall = [call for call in db.recordCreate.call_args_list if call[0][1].roleLabel == "user"][0] userRule = userCall[0][1] assert userRule.read == AccessLevel.MY assert userRule.create == AccessLevel.MY def testCreateTableSpecificRules(self): """Test that createTableSpecificRules creates table-specific rules.""" db = Mock() db.recordCreate = Mock() createTableSpecificRules(db) # Should create multiple rules for different tables assert db.recordCreate.call_count > 0 # Check that Mandate table rules are created with full objectKey mandateCalls = [call for call in db.recordCreate.call_args_list if call[0][1].item == "data.system.Mandate"] assert len(mandateCalls) > 0 # Check that all roles have view=False and no access for Mandate # (SysAdmin bypasses RBAC via isSysAdmin flag, not via roles) for call in mandateCalls: rule = call[0][1] assert rule.view == False assert rule.read == AccessLevel.NONE def testInitRbacRulesSkipsIfExists(self): """Test that initRbacRules skips default rule creation if rules already exist, but adds missing table-specific rules.""" db = Mock() # Mock existing rules - include rules for ChatWorkflow and Prompt to prevent adding missing rules # Need rules for all required roles to fully prevent creation # Using full objectKey format: data.system.{TableName} existingRules = [] for table in ["data.system.ChatWorkflow", "data.system.Prompt"]: for role in ["admin", "user", "viewer"]: existingRules.append({ "id": f"rule_{table}_{role}", "item": table, "context": AccessRuleContext.DATA.value, "roleLabel": role }) db.getRecordset = Mock(return_value=existingRules) db.recordCreate = Mock() initRbacRules(db) # Should not create new rules since all required tables already have rules for all roles db.recordCreate.assert_not_called() def testInitRbacRulesCreatesIfNotExists(self): """Test that initRbacRules creates rules if they don't exist.""" db = Mock() db.getRecordset = Mock(side_effect=[ [], # No existing rules [] # After creating default rules ]) db.recordCreate = Mock() initRbacRules(db) # Should create rules assert db.recordCreate.call_count > 0