# 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 mandateCalls = [call for call in db.recordCreate.call_args_list if call[0][1].item == "Mandate"] assert len(mandateCalls) > 0 # Check sysadmin rule for Mandate sysadminMandateCall = [call for call in mandateCalls if call[0][1].roleLabel == "sysadmin"][0] sysadminRule = sysadminMandateCall[0][1] assert sysadminRule.view == True assert sysadminRule.read == AccessLevel.ALL # Check that other roles have view=False for Mandate otherMandateCalls = [call for call in mandateCalls if call[0][1].roleLabel != "sysadmin"] for call in otherMandateCalls: rule = call[0][1] assert rule.view == False 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 existingRules = [] for table in ["ChatWorkflow", "Prompt"]: for role in ["sysadmin", "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