Files

241 lines
7.7 KiB
Python

from __future__ import annotations
import unittest
from datetime import UTC, datetime, timedelta
from govoplan_core.core.institutional import (
CAPABILITY_DECISION_REGISTRY,
CAPABILITY_MANDATE_RESOLVER,
ActorRepresentationReference,
DecisionEffectReference,
EvidenceReference,
InformationGovernanceReference,
InstitutionalContextError,
InstitutionalReference,
LegalBasisReference,
MandateDefinition,
MandateResolution,
TemporalRevision,
)
from govoplan_committee.backend.decision_path import (
CommitteeDecisionPath,
CommitteeDecisionProposal,
)
NOW = datetime(2026, 8, 1, 10, 0, tzinfo=UTC)
def reference(kind: str, object_id: str) -> InstitutionalReference:
return InstitutionalReference(
kind=kind, # type: ignore[arg-type]
owner_module=(
"committee"
if kind in {"decision", "record"}
else "organizations"
if kind in {"organization_unit", "function", "jurisdiction"}
else "approvals"
if kind == "approval"
else "cases"
),
object_id=object_id,
tenant_id="tenant-1",
valid_at=NOW,
)
def mandate() -> MandateDefinition:
return MandateDefinition(
reference=InstitutionalReference(
kind="mandate",
owner_module="committee",
object_id="mandate-1",
tenant_id="tenant-1",
version="7",
valid_at=NOW,
),
temporal=TemporalRevision(
revision="7",
valid_from=NOW - timedelta(days=30),
valid_to=NOW + timedelta(days=30),
recorded_at=NOW - timedelta(days=40),
),
task_types=("committee.formal_decision",),
authority_types=("committee.resolution",),
organization_unit_refs=(reference("organization_unit", "board-1"),),
function_refs=(reference("function", "chair"),),
jurisdiction_refs=(reference("jurisdiction", "city-1"),),
legal_bases=(legal_basis(),),
evidence=(evidence("mandate-evidence"),),
)
def legal_basis() -> LegalBasisReference:
return LegalBasisReference(
kind="statute",
authority="Example council",
reference="rules:committee:12",
version="2026-01",
effective_from=NOW - timedelta(days=100),
)
def evidence(evidence_id: str) -> EvidenceReference:
return EvidenceReference(
kind="record",
owner_module="committee",
evidence_id=evidence_id,
tenant_id="tenant-1",
version="1",
checksum="sha256:example",
captured_at=NOW,
)
def proposal() -> CommitteeDecisionProposal:
return CommitteeDecisionProposal(
tenant_id="tenant-1",
decision_id="decision-1",
revision="1",
effective_at=NOW,
meeting_ref="meeting-4",
agenda_item_ref="item-7",
decision_type="committee.resolution",
subject_refs=(reference("case", "case-1"),),
organization_unit_ref=reference("organization_unit", "board-1"),
function_ref=reference("function", "chair"),
actor=ActorRepresentationReference(
tenant_id="tenant-1",
account_id="account-1",
identity_id="identity-1",
represented_function_ref=reference("function", "chair"),
mandate_ref=mandate().reference,
),
approval_refs=(reference("approval", "vote-approval-1"),),
fact_evidence=(evidence("minutes-4"),),
legal_bases=(legal_basis(),),
operative_result="The proposal is accepted.",
reasoning="The submitted evidence satisfies the applicable rule.",
case_ref=reference("case", "case-1"),
jurisdiction_refs=(reference("jurisdiction", "city-1"),),
record_refs=(reference("record", "minutes-4"),),
requested_effects=(
DecisionEffectReference(
effect_key="postbox.notify_parties",
state="requested",
resource_refs=("postbox:case-1",),
),
),
review_refs=("review:administrative-court",),
information_governance=InformationGovernanceReference(
classification="restricted",
purposes=("formal_decision",),
legal_basis_refs=("rules:committee:12@2026-01",),
disclosure_state="partly_disclosable",
),
assurance_level="human_reviewed_automation",
automation_preparation_refs=("dataflow:recommendation-1",),
)
class FakeDecisionRegistry:
def __init__(self) -> None:
self.recorded = None
def get_decision(self, session, principal, *, reference):
return self.recorded
def record_decision(self, session, principal, *, decision, expected_revision=None):
self.recorded = decision
return decision
class FakeMandateResolver:
def __init__(self, resolution: MandateResolution) -> None:
self.resolution = resolution
self.request = None
def resolve_mandate(self, session, principal, *, request):
self.request = request
return self.resolution
class FakeRegistry:
def __init__(self, capabilities: dict[str, object]) -> None:
self.capabilities = capabilities
def has_capability(self, name: str) -> bool:
return name in self.capabilities
def capability(self, name: str) -> object:
return self.capabilities[name]
class CommitteeDecisionPathTests(unittest.TestCase):
def test_decision_reconstructs_authority_approval_evidence_and_effects(self) -> None:
registry = FakeDecisionRegistry()
resolution = MandateResolution(
competent=True,
mandates=(mandate(),),
explanation="Chair is competent for this agenda item.",
)
mandate_resolver = FakeMandateResolver(resolution)
path = CommitteeDecisionPath(
FakeRegistry(
{
CAPABILITY_MANDATE_RESOLVER: mandate_resolver,
CAPABILITY_DECISION_REGISTRY: registry,
}
)
)
result = path.decide(None, None, proposal=proposal())
payload = result.reconstruction_payload()
self.assertTrue(result.persisted_by_decision_registry)
self.assertIs(registry.recorded, result.decision)
self.assertEqual("mandate-1", payload["mandate_ref"]["object_id"])
self.assertEqual(
"vote-approval-1",
payload["decision"]["authority_context"]["approval_refs"][0]["object_id"],
)
self.assertEqual(
"postbox.notify_parties",
payload["decision"]["requested_effects"][0]["effect_key"],
)
self.assertEqual(
"The submitted evidence satisfies the applicable rule.",
payload["decision"]["reasoning"],
)
self.assertEqual(
"city-1", mandate_resolver.request.jurisdiction_refs[0].object_id
)
self.assertEqual(
"human_reviewed_automation",
payload["decision"]["assurance_level"],
)
self.assertEqual(
["dataflow:recommendation-1"],
payload["decision"]["automation_preparation_refs"],
)
def test_decision_rejects_ambiguous_or_ineffective_mandate(self) -> None:
accepted = mandate()
with self.assertRaisesRegex(
InstitutionalContextError,
"exactly one effective active mandate",
):
CommitteeDecisionPath().decide(
None,
None,
proposal=proposal(),
mandate_resolution=MandateResolution(
competent=True,
mandates=(accepted, accepted),
),
)
if __name__ == "__main__":
unittest.main()