from __future__ import annotations import pytest from models import MiningOperationType, Project, Scenario, ScenarioStatus, User from services.authorization import ( ensure_project_access, ensure_scenario_access, ensure_scenario_in_project, ) from services.exceptions import AuthorizationError, EntityNotFoundError from services.unit_of_work import UnitOfWork def _create_user_with_roles( uow: UnitOfWork, *, email: str, username: str, roles: set[str], ) -> User: assert uow.users is not None assert uow.roles is not None user = User( email=email, username=username, password_hash=User.hash_password("secret"), is_active=True, ) uow.users.create(user) uow.ensure_default_roles() for role_name in roles: role = uow.roles.get_by_name(role_name) assert role is not None, f"Role {role_name} should exist" uow.users.assign_role(user_id=user.id, role_id=role.id) return uow.users.get(user.id, with_roles=True) def _create_project(uow: UnitOfWork, name: str) -> Project: assert uow.projects is not None project = Project( name=name, location=None, operation_type=MiningOperationType.OTHER, description=None, ) uow.projects.create(project) return project def _create_scenario(uow: UnitOfWork, project: Project, name: str) -> Scenario: assert uow.scenarios is not None scenario = Scenario( project_id=project.id, name=name, description=None, status=ScenarioStatus.DRAFT, ) uow.scenarios.create(scenario) return scenario def test_ensure_project_access_allows_view_roles(unit_of_work_factory) -> None: with unit_of_work_factory() as uow: project = _create_project(uow, "Project A") user = _create_user_with_roles( uow, email="viewer@example.com", username="viewer", roles={"viewer"}, ) resolved = ensure_project_access( uow, project_id=project.id, user=user, ) assert resolved.id == project.id with pytest.raises(AuthorizationError): ensure_project_access( uow, project_id=project.id, user=user, require_manage=True, ) def test_ensure_project_access_allows_manage_roles(unit_of_work_factory) -> None: with unit_of_work_factory() as uow: project = _create_project(uow, "Project B") user = _create_user_with_roles( uow, email="manager@example.com", username="manager", roles={"project_manager"}, ) resolved = ensure_project_access( uow, project_id=project.id, user=user, require_manage=True, ) assert resolved.id == project.id def test_ensure_scenario_access(unit_of_work_factory) -> None: with unit_of_work_factory() as uow: project = _create_project(uow, "Project C") scenario = _create_scenario(uow, project, "Scenario C1") user = _create_user_with_roles( uow, email="analyst@example.com", username="analyst", roles={"analyst"}, ) resolved = ensure_scenario_access( uow, scenario_id=scenario.id, user=user, ) assert resolved.id == scenario.id with pytest.raises(AuthorizationError): ensure_scenario_access( uow, scenario_id=scenario.id, user=user, require_manage=True, ) def test_ensure_scenario_in_project_validates_membership(unit_of_work_factory) -> None: with unit_of_work_factory() as uow: project_one = _create_project(uow, "Project D") project_two = _create_project(uow, "Project E") scenario = _create_scenario(uow, project_one, "Scenario D1") user = _create_user_with_roles( uow, email="manager2@example.com", username="manager2", roles={"project_manager"}, ) resolved = ensure_scenario_in_project( uow, project_id=project_one.id, scenario_id=scenario.id, user=user, require_manage=True, ) assert resolved.id == scenario.id with pytest.raises(EntityNotFoundError): ensure_scenario_in_project( uow, project_id=project_two.id, scenario_id=scenario.id, user=user, )