feat: implement institutional mandate resolver
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, replace
|
||||
from datetime import UTC, datetime, timedelta
|
||||
import unittest
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.core.institutional import (
|
||||
InstitutionalReference,
|
||||
MandateDefinition,
|
||||
MandateResolutionRequest,
|
||||
TemporalRevision,
|
||||
)
|
||||
from govoplan_mandates.backend.db.models import MandateRevision
|
||||
from govoplan_mandates.backend.service import (
|
||||
MandateStoreError,
|
||||
SqlMandateResolver,
|
||||
get_mandate,
|
||||
record_mandate,
|
||||
)
|
||||
|
||||
|
||||
NOW = datetime(2026, 8, 1, 9, 0, tzinfo=UTC)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Principal:
|
||||
tenant_id: str = "tenant-1"
|
||||
account_id: str = "account-1"
|
||||
|
||||
|
||||
def definition(*, revision: str = "1", status: str = "active") -> MandateDefinition:
|
||||
temporal = TemporalRevision(
|
||||
revision=revision,
|
||||
valid_from=NOW,
|
||||
recorded_at=NOW + timedelta(minutes=int(revision) - 1),
|
||||
change_reason="Initial authority." if revision == "1" else "Authority changed.",
|
||||
)
|
||||
return MandateDefinition(
|
||||
reference=InstitutionalReference(
|
||||
kind="mandate",
|
||||
owner_module="mandates",
|
||||
object_id="committee-permit",
|
||||
tenant_id="tenant-1",
|
||||
version=revision,
|
||||
valid_at=NOW,
|
||||
),
|
||||
temporal=temporal,
|
||||
task_types=("committee.formal_decision",),
|
||||
authority_types=("permit",),
|
||||
status=status, # type: ignore[arg-type]
|
||||
suspension_reason="Review pending." if status == "suspended" else None,
|
||||
)
|
||||
|
||||
|
||||
class MandateTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.engine = create_engine("sqlite+pysqlite:///:memory:")
|
||||
MandateRevision.__table__.create(self.engine)
|
||||
self.session = Session(self.engine)
|
||||
self.principal = Principal()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.session.close()
|
||||
self.engine.dispose()
|
||||
|
||||
def test_record_resolve_replay_and_occ(self) -> None:
|
||||
first = definition()
|
||||
recorded = record_mandate(self.session, self.principal, definition=first)
|
||||
self.assertEqual("1", recorded.temporal.revision)
|
||||
replay = record_mandate(self.session, self.principal, definition=first)
|
||||
self.assertEqual("1", replay.temporal.revision)
|
||||
|
||||
request = MandateResolutionRequest(
|
||||
tenant_id="tenant-1",
|
||||
effective_at=NOW + timedelta(hours=1),
|
||||
task_type="committee.formal_decision",
|
||||
authority_type="permit",
|
||||
)
|
||||
resolved = SqlMandateResolver().resolve_mandate(
|
||||
self.session,
|
||||
self.principal,
|
||||
request=request,
|
||||
)
|
||||
self.assertTrue(resolved.competent)
|
||||
self.assertEqual("1", resolved.mandates[0].temporal.revision)
|
||||
|
||||
suspended = definition(revision="2", status="suspended")
|
||||
record_mandate(
|
||||
self.session,
|
||||
self.principal,
|
||||
definition=suspended,
|
||||
expected_revision="1",
|
||||
)
|
||||
self.assertFalse(
|
||||
SqlMandateResolver().resolve_mandate(
|
||||
self.session,
|
||||
self.principal,
|
||||
request=request,
|
||||
).competent
|
||||
)
|
||||
with self.assertRaisesRegex(MandateStoreError, "stale"):
|
||||
stale = replace(
|
||||
suspended,
|
||||
reference=replace(suspended.reference, version="3"),
|
||||
temporal=replace(suspended.temporal, revision="3"),
|
||||
)
|
||||
record_mandate(
|
||||
self.session,
|
||||
self.principal,
|
||||
definition=stale,
|
||||
expected_revision="1",
|
||||
)
|
||||
|
||||
def test_tenant_isolation(self) -> None:
|
||||
record_mandate(self.session, self.principal, definition=definition())
|
||||
self.assertIsNone(
|
||||
get_mandate(
|
||||
self.session,
|
||||
Principal(tenant_id="tenant-2"),
|
||||
mandate_id="committee-permit",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user