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 ( CAPABILITY_FORM_DEFINITIONS, CAPABILITY_SERVICE_DEFINITIONS, FormConditionExpression, FormDefinition, FormFieldDefinition, InstitutionalReference, ServiceBinding, ServiceDefinition, ServiceLaunchRequest, ServiceLaunchResult, TemporalRevision, ) from govoplan_forms.backend.db.models import FormDefinitionRevision from govoplan_forms.backend.service import ( SqlFormDefinitionProvider, record_form_definition, ) from govoplan_forms_runtime.backend.db.models import ( FormHandoffEffect, FormInstanceEvent, FormInstanceIdentity, FormInstanceRevision, ) from govoplan_forms_runtime.backend.service import ( FormRuntimeError, FormRuntimeService, FormsServiceLauncher, ) from govoplan_forms_runtime.backend.handoffs import FormHandoffService NOW = datetime(2026, 8, 1, 12, 0, tzinfo=UTC) @dataclass class Principal: tenant_id: str = "tenant-1" account_id: str = "account-1" class Registry: def __init__(self, provider: object, **capabilities: object) -> None: self.capabilities = { CAPABILITY_FORM_DEFINITIONS: provider, **capabilities, } def has_capability(self, name: str) -> bool: return name in self.capabilities def require_capability(self, name: str) -> object: return self.capabilities[name] def form_definition( *, form_id: str = "permit-form", revision: str = "1", policy_refs: tuple[str, ...] = (), ) -> FormDefinition: return FormDefinition( reference=InstitutionalReference( kind="form", owner_module="forms", object_id=form_id, tenant_id="tenant-1", version=revision, ), key=form_id, temporal=TemporalRevision( revision=revision, recorded_at=NOW + timedelta(minutes=int(revision) - 2), change_reason=("Initial schema." if revision == "1" else "Revise schema."), ), title="Permit form", fields=( FormFieldDefinition( key="name", label="Name", required=True, constraints={"min_length": 2}, ), FormFieldDefinition( key="delivery", label="Delivery", value_type="choice", options=("portal", "mail"), ), ), publication_state="published", allow_drafts=True, handoff_kinds=("case",), policy_refs=policy_refs, ) class FormsRuntimeTests(unittest.TestCase): def setUp(self) -> None: self.engine = create_engine("sqlite+pysqlite:///:memory:") for table in ( FormDefinitionRevision.__table__, FormInstanceIdentity.__table__, FormInstanceRevision.__table__, FormInstanceEvent.__table__, FormHandoffEffect.__table__, ): table.create(self.engine) self.session = Session(self.engine) self.principal = Principal() self.definition = record_form_definition( self.session, self.principal, definition=form_definition(), ) self.registry = Registry(SqlFormDefinitionProvider()) self.runtime = FormRuntimeService(self.registry) def tearDown(self) -> None: self.session.close() self.engine.dispose() def test_draft_submit_occ_replay_and_status_history(self) -> None: draft = self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={}, idempotency_key="create-1", recorded_at=NOW, ) self.assertEqual("draft", draft.status) self.assertEqual("warning", draft.validation_results[0]["severity"]) saved = self.runtime.update_draft( self.session, self.principal, instance_id=draft.instance_id, expected_revision=1, values={"name": "Ada", "delivery": "portal"}, attachment_refs=(), signature_refs=(), idempotency_key="save-1", recorded_at=NOW + timedelta(minutes=1), change_reason="Complete required values.", ) submitted = self.runtime.submit_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=2, values=saved.values, attachment_refs=(), signature_refs=(), idempotency_key="submit-1", recorded_at=NOW + timedelta(minutes=2), ) replay = self.runtime.submit_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=2, values=saved.values, attachment_refs=(), signature_refs=(), idempotency_key="submit-1", recorded_at=NOW + timedelta(minutes=2), ) self.assertEqual("submitted", submitted.status) self.assertIsNotNone(submitted.receipt_id) self.assertTrue(replay.replayed) self.assertEqual( [3, 2, 1], [ item.revision for item in self.runtime.history( self.session, self.principal, instance_id=draft.instance_id, ) ], ) with self.assertRaisesRegex(FormRuntimeError, "stale"): self.runtime.transition_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=2, status="validated", idempotency_key="transition-stale", recorded_at=NOW + timedelta(minutes=3), change_reason="Review complete.", ) def test_validation_policy_tenant_and_handoff_fail_closed(self) -> None: draft = self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={}, idempotency_key="create-2", recorded_at=NOW, ) with self.assertRaisesRegex(FormRuntimeError, "failed validation"): self.runtime.submit_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=1, values={"name": "A"}, attachment_refs=(), signature_refs=(), idempotency_key="invalid-submit", recorded_at=NOW + timedelta(minutes=1), ) with self.assertRaisesRegex(PermissionError, "denied"): self.runtime.get_instance( self.session, Principal(account_id="account-2"), instance_id=draft.instance_id, ) submitted = self.runtime.submit_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=1, values={"name": "Ada"}, attachment_refs=(), signature_refs=(), idempotency_key="valid-submit", recorded_at=NOW + timedelta(minutes=2), ) with self.assertRaisesRegex(FormRuntimeError, "cross tenants"): self.runtime.handoff_instance( self.session, self.principal, instance_id=submitted.instance_id, expected_revision=2, target_ref=InstitutionalReference( kind="case", owner_module="cases", object_id="case-1", tenant_id="tenant-2", ), idempotency_key="handoff-invalid", recorded_at=NOW + timedelta(minutes=3), change_reason="Create case.", ) current = self.runtime.get_instance( self.session, self.principal, instance_id=draft.instance_id, ) self.assertEqual(2, current.revision if current else None) protected = record_form_definition( self.session, self.principal, definition=form_definition( form_id="protected-form", policy_refs=("policy:protected-intake",), ), ) with self.assertRaisesRegex(PermissionError, "policy evaluator"): self.runtime.create_instance( self.session, self.principal, definition_ref=protected.reference, values={"name": "Ada"}, idempotency_key="protected-start", recorded_at=NOW + timedelta(minutes=4), ) def test_service_launcher_retains_exact_service_form_and_replay(self) -> None: binding = ServiceBinding(kind="form", reference="permit-form/1") service = ServiceDefinition( reference=InstitutionalReference( kind="service", owner_module="services", object_id="permit-service", tenant_id="tenant-1", version="4", ), key="permit-service", temporal=TemporalRevision( revision="4", recorded_at=NOW - timedelta(minutes=2), change_reason="Publish form entry.", ), title="Apply for permit", audience=("resident",), bindings=(binding,), publication_state="published", ) request = ServiceLaunchRequest( service_ref=service.reference, binding=binding, idempotency_key="portal-1", requested_at=NOW, parameters={}, ) launcher = FormsServiceLauncher(self.registry) first = launcher.launch_service( self.session, self.principal, definition=service, request=request, ) replay = launcher.launch_service( self.session, self.principal, definition=service, request=request, ) self.assertEqual( "form_submission", first.target_ref.kind if first.target_ref else None ) self.assertEqual("1", first.metadata["form_definition_revision"]) self.assertTrue(replay.replayed) self.assertEqual(first.target_ref.object_id, replay.target_ref.object_id) def test_create_replay_is_actor_bound_and_survives_schema_supersession( self, ) -> None: first = self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="create-replay", recorded_at=NOW, ) record_form_definition( self.session, self.principal, definition=form_definition(revision="2"), expected_revision="1", ) replay = self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="create-replay", recorded_at=NOW, ) self.assertTrue(replay.replayed) self.assertEqual(first.instance_id, replay.instance_id) with self.assertRaisesRegex(FormRuntimeError, "another actor"): self.runtime.create_instance( self.session, Principal(account_id="account-2"), definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="create-replay", recorded_at=NOW, ) def test_runtime_rejects_a_provider_returning_another_exact_revision(self) -> None: class MismatchedProvider: def get_form_definition( self, session, principal, *, reference, effective_at=None, ): return form_definition(revision="2") def list_form_definitions( self, session, principal, *, tenant_id, query="", limit=100, ): return () runtime = FormRuntimeService(Registry(MismatchedProvider())) with self.assertRaisesRegex(FormRuntimeError, "different definition"): runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="mismatched-provider", recorded_at=NOW, ) def test_client_supplied_instance_id_cannot_replace_existing_state(self) -> None: self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="fixed-instance-first", recorded_at=NOW, instance_id="fixed-instance", ) with self.assertRaisesRegex(FormRuntimeError, "instance id is already"): self.runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Grace"}, idempotency_key="fixed-instance-second", recorded_at=NOW, instance_id="fixed-instance", ) def test_hidden_required_fields_are_not_required_or_persisted(self) -> None: conditional = replace( form_definition(form_id="conditional-form"), fields=( FormFieldDefinition( key="include_details", label="Include details", value_type="boolean", ), FormFieldDefinition( key="details", label="Details", required=True, visibility_condition=FormConditionExpression( kind="predicate", field_key="include_details", operator="eq", value=True, ), ), ), ) stored = record_form_definition( self.session, self.principal, definition=conditional, ) hidden = self.runtime.create_instance( self.session, self.principal, definition_ref=stored.reference, values={"include_details": False, "details": "stale secret"}, idempotency_key="conditional-hidden", recorded_at=NOW, ) self.assertNotIn("details", hidden.values) submitted = self.runtime.submit_instance( self.session, self.principal, instance_id=hidden.instance_id, expected_revision=1, values=hidden.values, attachment_refs=(), signature_refs=(), idempotency_key="conditional-submit", recorded_at=NOW + timedelta(minutes=1), ) self.assertEqual("submitted", submitted.status) shown = self.runtime.create_instance( self.session, self.principal, definition_ref=stored.reference, values={"include_details": True}, idempotency_key="conditional-shown", recorded_at=NOW, ) with self.assertRaisesRegex(FormRuntimeError, "required"): self.runtime.submit_instance( self.session, self.principal, instance_id=shown.instance_id, expected_revision=1, values=shown.values, attachment_refs=(), signature_refs=(), idempotency_key="conditional-shown-submit", recorded_at=NOW + timedelta(minutes=1), ) def test_native_handoff_is_durable_replay_safe_and_reconciles_unknown(self) -> None: form_binding = ServiceBinding(kind="form", reference="permit-form/1") case_binding = ServiceBinding(kind="case", reference="permit-case") service = ServiceDefinition( reference=InstitutionalReference( kind="service", owner_module="services", object_id="permit-service", tenant_id="tenant-1", version="4", ), key="permit-service", temporal=TemporalRevision( revision="4", recorded_at=NOW - timedelta(minutes=2), change_reason="Publish intake targets.", ), title="Apply for permit", audience=("resident",), bindings=(form_binding, case_binding), publication_state="published", ) class Services: def get_service_definition( self, session, principal, *, reference, effective_at=None ): return service if reference == service.reference else None def list_service_definitions( self, session, principal, *, tenant_id, query="", limit=100 ): return (service,) class Cases: fail_unknown = False def launch_service(self, session, principal, *, definition, request): if self.fail_unknown: raise ConnectionError("Provider response was lost.") return ServiceLaunchResult( service_ref=definition.reference, binding=request.binding, state="started", target_ref=InstitutionalReference( kind="case", owner_module="cases", object_id="case-1", tenant_id="tenant-1", version="1", ), href="/cases/case-1", replayed=request.idempotency_key.startswith("known"), metadata={"case_number": "CASE-1"}, ) cases = Cases() registry = Registry( SqlFormDefinitionProvider(), **{ CAPABILITY_SERVICE_DEFINITIONS: Services(), "cases.service_launcher": cases, }, ) runtime = FormRuntimeService(registry) draft = runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Ada"}, idempotency_key="native-create", recorded_at=NOW, service_ref=service.reference, service_binding=form_binding, ) submitted = runtime.submit_instance( self.session, self.principal, instance_id=draft.instance_id, expected_revision=1, values=draft.values, attachment_refs=(), signature_refs=(), idempotency_key="native-submit", recorded_at=NOW + timedelta(minutes=1), ) handoffs = FormHandoffService(registry) prepared = handoffs.prepare( self.session, self.principal, instance_id=submitted.instance_id, expected_revision=2, binding_kind="case", binding_reference="permit-case", idempotency_key="native-handoff", requested_at=NOW + timedelta(minutes=2), allow_all=True, ) self.session.commit() self.assertEqual("requested", prepared.state) self.assertEqual( "requested", self.session.query(FormHandoffEffect).one().state, ) accepted, revised = handoffs.execute( self.session, self.principal, effect_id=prepared.effect_id, executed_at=NOW + timedelta(minutes=2), allow_all=True, ) self.assertEqual("accepted", accepted.state) self.assertEqual("handed_off", revised.status) replay = handoffs.prepare( self.session, self.principal, instance_id=submitted.instance_id, expected_revision=2, binding_kind="case", binding_reference="permit-case", idempotency_key="native-handoff", requested_at=NOW + timedelta(minutes=2), allow_all=True, ) self.assertEqual("accepted", replay.state) second = runtime.create_instance( self.session, self.principal, definition_ref=self.definition.reference, values={"name": "Grace"}, idempotency_key="unknown-create", recorded_at=NOW, service_ref=service.reference, service_binding=form_binding, ) second = runtime.submit_instance( self.session, self.principal, instance_id=second.instance_id, expected_revision=1, values=second.values, attachment_refs=(), signature_refs=(), idempotency_key="unknown-submit", recorded_at=NOW + timedelta(minutes=1), ) unknown = handoffs.prepare( self.session, self.principal, instance_id=second.instance_id, expected_revision=2, binding_kind="case", binding_reference=None, idempotency_key="unknown-handoff", requested_at=NOW + timedelta(minutes=2), allow_all=True, ) self.session.commit() cases.fail_unknown = True unknown, _ = handoffs.execute( self.session, self.principal, effect_id=unknown.effect_id, executed_at=NOW + timedelta(minutes=2), allow_all=True, ) self.assertEqual("outcome_unknown", unknown.state) with self.assertRaisesRegex(FormRuntimeError, "must be reconciled"): handoffs.execute( self.session, self.principal, effect_id=unknown.effect_id, executed_at=NOW + timedelta(minutes=3), allow_all=True, ) cases.fail_unknown = False reconciled, revised = handoffs.execute( self.session, self.principal, effect_id=unknown.effect_id, executed_at=NOW + timedelta(minutes=4), allow_all=True, reconcile=True, ) self.assertEqual("reconciled", reconciled.state) self.assertEqual("handed_off", revised.status) if __name__ == "__main__": unittest.main()