from __future__ import annotations import unittest from datetime import datetime, timedelta, timezone from govoplan_core.core.identity import IdentityAccountLinkRef, IdentityRef from govoplan_core.core.organizations import OrganizationFunctionRef from govoplan_core.db.base import Base from govoplan_core.db.session import configure_database, reset_database from govoplan_identity.backend.db import models as identity_models # noqa: F401 - resolve assignment foreign keys from govoplan_idm.backend.db.models import IdmOrganizationFunctionAssignment from govoplan_idm.backend.directory import SqlIdmDirectory from govoplan_organizations.backend.db import models as organization_models # noqa: F401 - resolve assignment foreign keys class StubIdentityDirectory: def get_identity(self, identity_id: str) -> IdentityRef | None: return None def identity_for_account(self, account_id: str) -> IdentityRef | None: return None def identities_for_accounts(self, account_ids: tuple[str, ...]) -> tuple[IdentityRef, ...]: return () def accounts_for_identity(self, identity_id: str) -> tuple[IdentityAccountLinkRef, ...]: return () class StubOrganizationDirectory: def get_function(self, function_id: str) -> OrganizationFunctionRef | None: return OrganizationFunctionRef( id=function_id, tenant_id="tenant-1", organization_unit_id="unit-1", slug=function_id, name=function_id, status="active", ) def get_organization_unit(self, organization_unit_id: str): return None def organization_units_for_tenant(self, tenant_id: str): return () def functions_for_organization_unit(self, organization_unit_id: str, *, include_subunits: bool = False): return () class IdmDirectoryDelegationTests(unittest.TestCase): def setUp(self) -> None: self.database = configure_database("sqlite:///:memory:") Base.metadata.create_all( self.database.engine, tables=[IdmOrganizationFunctionAssignment.__table__], ) self.directory = SqlIdmDirectory( identities=StubIdentityDirectory(), # type: ignore[arg-type] organizations=StubOrganizationDirectory(), # type: ignore[arg-type] ) def tearDown(self) -> None: reset_database(dispose=True) def _source_and_child( self, *, case: str, child_source: str = "delegated", source_tenant_id: str = "tenant-1", source_function_id: str = "function-1", source_is_active: bool = True, source_valid_from: datetime | None = None, source_valid_until: datetime | None = None, ) -> tuple[IdmOrganizationFunctionAssignment, IdmOrganizationFunctionAssignment]: source = IdmOrganizationFunctionAssignment( id=f"source-{case}", tenant_id=source_tenant_id, identity_id=f"source-identity-{case}", function_id=source_function_id, organization_unit_id="unit-1", source="direct", is_active=source_is_active, valid_from=source_valid_from, valid_until=source_valid_until, settings={}, ) child = IdmOrganizationFunctionAssignment( id=f"child-{case}", tenant_id="tenant-1", identity_id=f"target-identity-{case}", function_id="function-1", organization_unit_id="unit-1", source=child_source, delegated_from_assignment_id=source.id, acting_for_account_id="represented-account" if child_source == "acting_for" else None, is_active=True, settings={}, ) return source, child def test_effective_delegations_require_a_current_matching_source(self) -> None: now = datetime.now(timezone.utc) cases = ( self._source_and_child(case="valid"), self._source_and_child(case="acting", child_source="acting_for"), self._source_and_child(case="revoked", source_is_active=False), self._source_and_child(case="expired", source_valid_until=now - timedelta(days=1)), self._source_and_child(case="future", source_valid_from=now + timedelta(days=1)), self._source_and_child(case="wrong-tenant", source_tenant_id="tenant-2"), self._source_and_child(case="wrong-function", source_function_id="function-2"), ) with self.database.session() as session: for source, child in cases: session.add_all((source, child)) session.commit() expected = { "valid": ("child-valid",), "acting": ("child-acting",), "revoked": (), "expired": (), "future": (), "wrong-tenant": (), "wrong-function": (), } for case, expected_ids in expected.items(): with self.subTest(case=case): assignments = self.directory.organization_function_assignments_for_identity( f"target-identity-{case}", tenant_id="tenant-1", ) self.assertEqual(expected_ids, tuple(item.id for item in assignments)) if __name__ == "__main__": unittest.main()