from __future__ import annotations

from dataclasses import replace
from datetime import UTC, datetime, timedelta
import unittest

from govoplan_core.core.automation import ActionExecutionRequest, AutomationInvocation
from govoplan_core.core.events import PlatformEvent
from govoplan_core.core.external_references import ExternalObjectReference
from govoplan_core.core.institutional import (
    ActorRepresentationReference,
    DecisionEffectReference,
    EvidenceReference,
    ExternalSourceReference,
    FormDefinition,
    FormFieldDefinition,
    FormalDecision,
    GovernedContextEnvelope,
    InformationGovernanceReference,
    InstitutionalContextError,
    InstitutionalReference,
    LegalBasisReference,
    MandateDefinition,
    MandateResolution,
    MandateResolutionRequest,
    PartyRepresentation,
    PartySubjectReference,
    ProcedureParty,
    ServiceBinding,
    ServiceAvailabilityRequirement,
    ServiceDefinition,
    ServiceLaunchRequest,
    ServiceLaunchResult,
    TemporalRevision,
    derive_service_restriction,
    resolve_mandate_candidates,
    revise_procedure_party,
    revise_formal_decision,
    revise_mandate_definition,
    revoke_party_representation,
)


NOW = datetime(2026, 8, 1, 10, 0, tzinfo=UTC)


def _reference(
    kind: str,
    object_id: str,
    *,
    tenant_id: str = "tenant-1",
) -> InstitutionalReference:
    owners = {
        "institution": "tenancy",
        "organization_unit": "organizations",
        "function": "organizations",
        "function_assignment": "idm",
        "mandate": "cases",
        "jurisdiction": "cases",
        "service": "portal",
        "form": "forms",
        "form_submission": "forms_runtime",
        "case": "cases",
        "party": "cases",
        "decision": "committee",
        "work_item": "workflow_engine",
        "workflow": "workflow_engine",
        "approval": "workflow_engine",
        "record": "audit",
    }
    return InstitutionalReference(
        kind=kind,
        owner_module=owners[kind],
        object_id=object_id,
        tenant_id=tenant_id,
        version="3",
        valid_at=NOW,
        label=f"Protected label for {object_id}",
    )


def _context() -> GovernedContextEnvelope:
    external = ExternalObjectReference(
        system="gis.example",
        object_type="administrative_area",
        object_id="area-7",
        maturity="read",
        authority_mode="external_mirror",
        canonical_url="https://gis.example/areas/7",
        observed_at=NOW,
    )
    return GovernedContextEnvelope(
        tenant_id="tenant-1",
        temporal=TemporalRevision(
            revision="7",
            valid_from=NOW - timedelta(days=1),
            valid_to=NOW + timedelta(days=1),
            recorded_at=NOW,
            change_reason="Case state changed.",
        ),
        actor=ActorRepresentationReference(
            tenant_id="tenant-1",
            account_id="account-1",
            identity_id="identity-1",
            represented_function_ref=_reference("function", "function-1"),
            function_assignment_ref=_reference(
                "function_assignment", "assignment-1"
            ),
            mandate_ref=_reference("mandate", "mandate-1"),
        ),
        institution_ref=_reference("institution", "institution-1"),
        organization_unit_ref=_reference("organization_unit", "unit-1"),
        function_ref=_reference("function", "function-1"),
        mandate_ref=_reference("mandate", "mandate-1"),
        jurisdiction_refs=(_reference("jurisdiction", "jurisdiction-1"),),
        service_ref=_reference("service", "service-1"),
        case_ref=_reference("case", "case-1"),
        party_refs=(_reference("party", "party-1"),),
        work_item_ref=_reference("work_item", "work-item-1"),
        workflow_ref=_reference("workflow", "workflow-1"),
        approval_refs=(_reference("approval", "approval-1"),),
        decision_ref=_reference("decision", "decision-1"),
        record_refs=(_reference("record", "record-1"),),
        legal_bases=(
            LegalBasisReference(
                kind="law",
                authority="Example legislature",
                reference="law-1/section-3",
                version="2026-01",
                effective_from=NOW - timedelta(days=30),
                title="Protected legal title",
                inspection_url="https://law.example/section-3",
            ),
        ),
        evidence=(
            EvidenceReference(
                kind="document",
                owner_module="files",
                evidence_id="file-1",
                tenant_id="tenant-1",
                version="4",
                checksum="sha256:abcd",
                captured_at=NOW,
                inspection_url="https://govoplan.example/files/file-1",
            ),
        ),
        information_governance=InformationGovernanceReference(
            classification="confidential",
            purposes=("case decision",),
            legal_basis_refs=("law-1/section-3",),
            retention_policy_ref="records:case-default",
            disclosure_state="restricted",
        ),
        external_sources=(
            ExternalSourceReference(
                reference=external,
                authority_mode="external_mirror",
                maturity="read",
                freshness_state="current",
                health_state="ok",
            ),
        ),
    )


class InstitutionalContractTests(unittest.TestCase):
    def test_context_round_trip_preserves_versioned_references(self) -> None:
        context = _context()

        restored = GovernedContextEnvelope.from_mapping(
            context.to_dict(include_inspection=True)
        )

        self.assertEqual(context.tenant_id, restored.tenant_id)
        self.assertEqual("mandate-1", restored.mandate_ref.object_id)
        self.assertEqual(
            "external_mirror", restored.external_sources[0].authority_mode
        )
        self.assertEqual("workflow-1", restored.workflow_ref.object_id)
        self.assertEqual("approval-1", restored.approval_refs[0].object_id)
        self.assertEqual("record-1", restored.record_refs[0].object_id)
        self.assertTrue(restored.temporal.effective_at(NOW))

    def test_cross_tenant_reference_is_rejected(self) -> None:
        with self.assertRaisesRegex(InstitutionalContextError, "different tenant"):
            GovernedContextEnvelope(
                tenant_id="tenant-1",
                temporal=TemporalRevision(revision="1", recorded_at=NOW),
                case_ref=_reference("case", "case-2", tenant_id="tenant-2"),
            )

    def test_default_serialization_redacts_labels_and_inspection_links(self) -> None:
        payload = _context().to_dict()

        self.assertIsNone(payload["case_ref"]["label"])
        self.assertIsNone(payload["legal_bases"][0]["title"])
        self.assertIsNone(payload["legal_bases"][0]["inspection_url"])
        self.assertIsNone(payload["evidence"][0]["inspection_url"])

    def test_event_and_action_envelopes_carry_reference_context(self) -> None:
        context = _context()
        event = PlatformEvent(
            type="committee.decision.recorded",
            module_id="committee",
            institutional_context=context,
        )
        action = ActionExecutionRequest(
            tenant_id="tenant-1",
            action_key="committee.decision.publish",
            input={"decision_ref": "decision-1"},
            idempotency_key="decision-1:publish:1",
            invocation=AutomationInvocation(kind="workflow"),
            institutional_context=context,
        )

        self.assertEqual(
            "decision-1",
            event.to_dict()["institutional_context"]["decision_ref"]["object_id"],
        )
        self.assertIs(context, action.institutional_context)

    def test_formal_decision_redacts_reasoning_but_keeps_evidence_refs(self) -> None:
        context = _context()
        decision = FormalDecision(
            reference=_reference("decision", "decision-1"),
            temporal=context.temporal,
            decision_type="permit",
            subject_refs=(_reference("case", "case-1"),),
            state="decided",
            authority_context=context,
            fact_evidence=context.evidence,
            legal_bases=context.legal_bases,
            operative_result="Permit granted.",
            reasoning="Protected reasoning.",
        )

        redacted = decision.to_dict()
        protected = decision.to_dict(include_protected=True)

        self.assertIsNone(redacted["reasoning"])
        self.assertEqual("Protected reasoning.", protected["reasoning"])
        self.assertEqual(
            "file-1", redacted["fact_evidence"][0]["evidence_id"]
        )

        restored = FormalDecision.from_mapping(protected)
        self.assertEqual("Protected reasoning.", restored.reasoning)
        self.assertEqual("decision-1", restored.reference.object_id)

    def test_semantic_provider_dtos_round_trip_without_domain_tables(self) -> None:
        context = _context()
        mandate = MandateDefinition(
            reference=_reference("mandate", "mandate-1"),
            temporal=context.temporal,
            task_types=("committee.formal_decision",),
            authority_types=("permit",),
            organization_unit_refs=(
                _reference("organization_unit", "unit-1"),
            ),
            function_refs=(_reference("function", "function-1"),),
            jurisdiction_refs=context.jurisdiction_refs,
            legal_bases=context.legal_bases,
            evidence=context.evidence,
        )
        resolution = MandateResolution(
            competent=True,
            mandates=(mandate,),
            explanation="Authority matched.",
            evidence=context.evidence,
        )
        service = ServiceDefinition(
            reference=_reference("service", "service-1"),
            key="permit.service",
            temporal=context.temporal,
            title="Permit service",
            audience=("residents",),
            legal_bases=context.legal_bases,
            channels=("portal", "mail"),
            responsible_organization_ref=_reference(
                "organization_unit", "unit-1"
            ),
            responsible_function_ref=_reference("function", "function-1"),
            mandate_ref=_reference("mandate", "mandate-1"),
            jurisdiction_refs=context.jurisdiction_refs,
            bindings=(ServiceBinding("workflow", "workflow:permit"),),
            publication_state="published",
        )
        represented = _reference("party", "party-applicant")
        representative = _reference("party", "party-agent")
        party = ProcedureParty(
            reference=represented,
            procedure_ref=_reference("case", "case-1"),
            role="applicant",
            subject=PartySubjectReference(
                kind="identity",
                provider="identity",
                subject_id="identity-1",
                tenant_id="tenant-1",
                version="2",
            ),
            temporal=context.temporal,
            preferred_channels=("portal",),
            permitted_channels=("portal", "mail"),
            delivery_recipient=True,
            representations=(
                PartyRepresentation(
                    representative_party_ref=representative,
                    represented_party_ref=represented,
                    power_ref="power-1",
                    permitted_actions=("submit", "receive"),
                    temporal=context.temporal,
                    evidence=context.evidence,
                ),
            ),
            contact_snapshot_refs=("addresses:snapshot-1",),
            evidence=context.evidence,
        )

        restored_resolution = MandateResolution.from_mapping(
            resolution.to_dict(include_inspection=True)
        )
        restored_service = ServiceDefinition.from_mapping(service.to_dict())
        restored_party = ProcedureParty.from_mapping(
            party.to_dict(include_inspection=True)
        )

        self.assertTrue(restored_resolution.competent)
        self.assertEqual(
            "committee.formal_decision",
            restored_resolution.mandates[0].task_types[0],
        )
        self.assertEqual("workflow:permit", restored_service.bindings[0].reference)
        self.assertEqual("identity-1", restored_party.subject.subject_id)
        self.assertEqual(
            "party-agent",
            restored_party.representations[0].representative_party_ref.object_id,
        )

    def test_invalid_semantic_states_are_rejected(self) -> None:
        with self.assertRaisesRegex(InstitutionalContextError, "disclosure state"):
            InformationGovernanceReference(
                classification="internal",
                purposes=("test",),
                disclosure_state="invalid",  # type: ignore[arg-type]
            )

    def test_service_launch_contract_requires_exact_same_tenant_targets(self) -> None:
        service_ref = _reference("service", "service-1")
        binding = ServiceBinding("case", "permit")
        request = ServiceLaunchRequest(
            service_ref=service_ref,
            binding=binding,
            idempotency_key="launch-1",
            requested_at=NOW,
            parameters={"title": "Permit application"},
        )
        result = ServiceLaunchResult(
            service_ref=service_ref,
            binding=binding,
            state="started",
            target_ref=_reference("case", "case-1"),
            href="/cases/case-1",
            metadata={"case_number": "PERMIT-1"},
        )

        self.assertEqual("launch-1", request.idempotency_key)
        self.assertEqual("case-1", result.to_dict()["target_ref"]["object_id"])
        with self.assertRaisesRegex(InstitutionalContextError, "another tenant"):
            ServiceLaunchResult(
                service_ref=service_ref,
                binding=binding,
                state="started",
                target_ref=_reference("case", "case-2", tenant_id="tenant-2"),
                metadata={},
            )

    def test_form_definition_contract_round_trips_exact_schema(self) -> None:
        definition = FormDefinition(
            reference=_reference("form", "permit-application"),
            key="permit-application",
            temporal=TemporalRevision(
                revision="3",
                recorded_at=NOW,
                change_reason="Add delivery preference.",
            ),
            title="Permit application",
            description="Apply for a permit.",
            fields=(
                FormFieldDefinition(
                    key="email",
                    label="Email address",
                    value_type="email",
                    required=True,
                    constraints={"max_length": 255},
                ),
                FormFieldDefinition(
                    key="delivery",
                    label="Delivery",
                    value_type="choice",
                    options=("portal", "mail"),
                ),
            ),
            max_attachments=3,
            signature_requirement="optional",
            policy_refs=("policy:permit-intake",),
            handoff_kinds=("case",),
        )

        restored = FormDefinition.from_mapping(definition.to_dict())

        self.assertEqual("3", restored.reference.version)
        self.assertEqual("email", restored.fields[0].key)
        self.assertEqual(("portal", "mail"), restored.fields[1].options)
        with self.assertRaisesRegex(InstitutionalContextError, "unique"):
            replace(restored, fields=(restored.fields[0], restored.fields[0]))

    def test_service_launch_redirect_rejects_credentialed_urls(self) -> None:
        with self.assertRaisesRegex(InstitutionalContextError, "credential-free"):
            ServiceLaunchResult(
                service_ref=_reference("service", "service-1"),
                binding=ServiceBinding("external", "external-entry"),
                state="redirect",
                href="https://user:secret@example.test/start",
                metadata={},
            )

        with self.assertRaisesRegex(InstitutionalContextError, "must be a boolean"):
            ServiceBinding.from_mapping(
                {
                    "kind": "workflow",
                    "reference": "workflow:permit",
                    "required": "false",
                }
            )

    def test_service_derivation_can_only_tighten_parent_constraints(self) -> None:
        parent = ServiceDefinition(
            reference=_reference("service", "permit-template"),
            key="permit.apply",
            temporal=TemporalRevision(
                revision="7",
                valid_from=NOW - timedelta(days=1),
                valid_to=NOW + timedelta(days=10),
                recorded_at=NOW - timedelta(days=2),
            ),
            title="Permit service",
            audience=("resident", "business"),
            prerequisites=("registered-address",),
            required_evidence_types=("application",),
            channels=("portal", "mail"),
            bindings=(ServiceBinding("case", "permit-case"),),
            availability_requirements=(
                ServiceAvailabilityRequirement("policy", "permit-access"),
            ),
            publication_state="published",
        )
        temporal = TemporalRevision(
            revision="8",
            valid_from=NOW,
            valid_to=NOW + timedelta(days=5),
            recorded_at=NOW,
            change_reason="Tenant specialization.",
        )
        reference = replace(
            _reference("service", "permit-tenant"),
            version="8",
        )

        derived = derive_service_restriction(
            parent,
            reference=reference,
            temporal=temporal,
            audience=("resident",),
            channels=("portal",),
            add_prerequisites=("tenant-residency",),
            add_required_evidence_types=("identity",),
            add_bindings=(ServiceBinding("workflow", "permit-review"),),
        )

        self.assertEqual(parent.reference, derived.derived_from_ref)
        self.assertEqual(("resident",), derived.audience)
        self.assertEqual(("portal",), derived.channels)
        self.assertEqual(
            ("registered-address", "tenant-residency"),
            derived.prerequisites,
        )
        self.assertIn(ServiceBinding("case", "permit-case"), derived.bindings)
        self.assertEqual(
            parent.availability_requirements,
            derived.availability_requirements,
        )
        self.assertEqual(
            parent.availability_requirements,
            ServiceDefinition.from_mapping(
                derived.to_dict()
            ).availability_requirements,
        )
        with self.assertRaisesRegex(InstitutionalContextError, "widen.*audience"):
            derive_service_restriction(
                parent,
                reference=reference,
                temporal=temporal,
                audience=("resident", "visitor"),
            )
        with self.assertRaisesRegex(InstitutionalContextError, "beyond"):
            derive_service_restriction(
                parent,
                reference=reference,
                temporal=replace(temporal, valid_to=None),
            )
        with self.assertRaisesRegex(
            InstitutionalContextError,
            "publication state",
        ):
            derive_service_restriction(
                replace(parent, publication_state="suspended"),
                reference=reference,
                temporal=temporal,
                publication_state="published",
            )

    def test_mandate_resolution_is_effective_scoped_and_deterministic(self) -> None:
        context = _context()
        mandate = MandateDefinition(
            reference=_reference("mandate", "mandate-1"),
            temporal=TemporalRevision(
                revision="1",
                valid_from=NOW - timedelta(days=1),
                valid_to=NOW + timedelta(days=1),
                recorded_at=NOW - timedelta(days=2),
            ),
            task_types=("committee.formal_decision",),
            authority_types=("permit",),
            organization_unit_refs=(
                _reference("organization_unit", "unit-1"),
            ),
            function_refs=(_reference("function", "function-1"),),
            jurisdiction_refs=context.jurisdiction_refs,
            subject_types=("permit",),
            evidence=context.evidence,
        )
        request = MandateResolutionRequest(
            tenant_id="tenant-1",
            effective_at=NOW,
            task_type="committee.formal_decision",
            authority_type="permit",
            organization_unit_ref=replace(
                _reference("organization_unit", "unit-1"), version="9"
            ),
            function_ref=replace(
                _reference("function", "function-1"), version="9"
            ),
            jurisdiction_refs=tuple(
                replace(item, version="9") for item in context.jurisdiction_refs
            ),
            subject_type="permit",
        )

        resolution = resolve_mandate_candidates(
            request,
            (
                replace(
                    mandate,
                    reference=_reference("mandate", "retired"),
                    status="retired",
                ),
                mandate,
            ),
        )

        self.assertTrue(resolution.competent)
        self.assertEqual("mandate-1", resolution.mandates[0].reference.object_id)
        self.assertEqual("file-1", resolution.evidence[0].evidence_id)

        ambiguous = resolve_mandate_candidates(
            request,
            (
                mandate,
                replace(
                    mandate,
                    reference=_reference("mandate", "mandate-2"),
                ),
            ),
        )
        self.assertFalse(ambiguous.competent)
        self.assertEqual(2, len(ambiguous.conflict_refs))

        missing_jurisdiction = resolve_mandate_candidates(
            replace(request, jurisdiction_refs=()),
            (mandate,),
        )
        self.assertFalse(missing_jurisdiction.competent)

    def test_mandate_resolution_rejects_cross_tenant_candidates(self) -> None:
        request = MandateResolutionRequest(
            tenant_id="tenant-1",
            effective_at=NOW,
            task_type="committee.formal_decision",
            authority_type="permit",
        )
        candidate = MandateDefinition(
            reference=_reference("mandate", "mandate-2", tenant_id="tenant-2"),
            temporal=TemporalRevision(revision="1", recorded_at=NOW),
            task_types=("committee.formal_decision",),
            authority_types=("permit",),
        )

        with self.assertRaisesRegex(InstitutionalContextError, "cross tenants"):
            resolve_mandate_candidates(request, (candidate,))

    def test_mandate_lifecycle_revisions_are_linked_by_stable_identity(self) -> None:
        current = MandateDefinition(
            reference=_reference("mandate", "mandate-1"),
            temporal=TemporalRevision(revision="1", recorded_at=NOW),
            task_types=("committee.formal_decision",),
            authority_types=("permit",),
            status="active",
        )
        temporal = TemporalRevision(
            revision="2",
            recorded_at=NOW + timedelta(minutes=1),
            change_reason="Authority temporarily suspended.",
        )

        suspended = revise_mandate_definition(
            current,
            expected_revision="1",
            temporal=temporal,
            status="suspended",
            suspension_reason="Delegation is under review.",
        )

        self.assertEqual("active", current.status)
        self.assertEqual("suspended", suspended.status)
        self.assertEqual("2", suspended.reference.version)
        self.assertEqual(current.reference.object_id, suspended.reference.object_id)
        with self.assertRaisesRegex(InstitutionalContextError, "stale"):
            revise_mandate_definition(
                current,
                expected_revision="0",
                temporal=temporal,
                status="suspended",
                suspension_reason="Review.",
            )
        with self.assertRaisesRegex(InstitutionalContextError, "not allowed"):
            revise_mandate_definition(
                suspended,
                expected_revision="2",
                temporal=replace(
                    temporal,
                    revision="3",
                    change_reason="Invalid reset.",
                ),
                status="draft",
            )

    def test_formal_decision_revisions_are_linked_and_occ_guarded(self) -> None:
        context = _context()
        reference = replace(
            _reference("decision", "decision-1"),
            version=context.temporal.revision,
        )
        context = replace(context, decision_ref=reference)
        current = FormalDecision(
            reference=reference,
            temporal=context.temporal,
            decision_type="permit",
            subject_refs=(_reference("case", "case-1"),),
            state="decided",
            authority_context=context,
            fact_evidence=context.evidence,
            legal_bases=context.legal_bases,
            operative_result="Permit granted.",
            reasoning="Protected reasoning.",
            requested_effects=(
                DecisionEffectReference("notify", "requested"),
            ),
        )
        with self.assertRaisesRegex(
            InstitutionalContextError,
            "service account",
        ):
            replace(current, assurance_level="automated_under_mandate")
        revision = TemporalRevision(
            revision="8",
            valid_from=NOW + timedelta(minutes=1),
            recorded_at=NOW + timedelta(minutes=1),
            change_reason="Decision entered into force.",
        )

        effective = revise_formal_decision(
            current,
            expected_revision="7",
            temporal=revision,
            state="effective",
        )

        self.assertEqual("decided", current.state)
        self.assertEqual("8", effective.reference.version)
        self.assertEqual("7", effective.supersedes_ref.version)
        self.assertEqual(
            effective.reference,
            effective.authority_context.decision_ref,
        )
        self.assertEqual(current.requested_effects, effective.requested_effects)

        with self.assertRaisesRegex(InstitutionalContextError, "stale"):
            revise_formal_decision(
                current,
                expected_revision="6",
                temporal=revision,
                state="effective",
            )
        with self.assertRaisesRegex(InstitutionalContextError, "not allowed"):
            revise_formal_decision(
                effective,
                expected_revision="8",
                temporal=replace(
                    revision,
                    revision="9",
                    change_reason="Invalid regression.",
                ),
                state="proposed",
            )

        corrected = revise_formal_decision(
            effective,
            expected_revision="8",
            temporal=replace(
                revision,
                revision="9",
                recorded_at=NOW + timedelta(minutes=2),
                change_reason="Corrected operative wording.",
            ),
            state="corrected",
            operative_result="Permit granted subject to condition A.",
        )
        self.assertEqual(effective.reference, corrected.correction_of_ref)
        self.assertEqual(effective.reference, corrected.supersedes_ref)

    def test_party_corrections_and_representation_revocation_are_versioned(self) -> None:
        context = _context()
        represented = _reference("party", "party-applicant")
        representative = _reference("party", "party-agent")
        representation = PartyRepresentation(
            representative_party_ref=representative,
            represented_party_ref=represented,
            power_ref="power-1",
            permitted_actions=("submit", "receive"),
            temporal=TemporalRevision(
                revision="1",
                valid_from=NOW - timedelta(days=1),
                recorded_at=NOW - timedelta(days=1),
            ),
            evidence=context.evidence,
        )
        current = ProcedureParty(
            reference=represented,
            procedure_ref=_reference("case", "case-1"),
            role="applicant",
            subject=PartySubjectReference(
                kind="identity",
                provider="identity",
                subject_id="identity-1",
                tenant_id="tenant-1",
                version="2",
            ),
            temporal=TemporalRevision(revision="1", recorded_at=NOW),
            permitted_channels=("mail", "postbox"),
            preferred_channels=("mail",),
            delivery_recipient=True,
            representations=(representation,),
            contact_snapshot_refs=("addresses:snapshot-1",),
            evidence=context.evidence,
        )
        revision = TemporalRevision(
            revision="2",
            valid_from=NOW,
            recorded_at=NOW + timedelta(minutes=1),
            change_reason="Correct preferred delivery channel.",
        )

        corrected = revise_procedure_party(
            current,
            expected_revision="1",
            temporal=revision,
            preferred_channels=("postbox",),
        )
        revoked_power = revoke_party_representation(
            representation,
            expected_revision="1",
            temporal=TemporalRevision(
                revision="2",
                valid_from=NOW - timedelta(days=1),
                valid_to=NOW + timedelta(minutes=2),
                recorded_at=NOW + timedelta(minutes=2),
                change_reason="Power was revoked.",
            ),
            revoked_at=NOW + timedelta(minutes=2),
        )

        self.assertEqual(("mail",), current.preferred_channels)
        self.assertEqual(("postbox",), corrected.preferred_channels)
        self.assertEqual("2", corrected.reference.version)
        self.assertIsNone(representation.revoked_at)
        self.assertEqual(NOW + timedelta(minutes=2), revoked_power.revoked_at)
        with self.assertRaisesRegex(InstitutionalContextError, "stale"):
            revise_procedure_party(
                current,
                expected_revision="0",
                temporal=revision,
            )


if __name__ == "__main__":
    unittest.main()
