167 lines
5.7 KiB
Python
167 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
import unittest
|
|
|
|
from govoplan_core.core.records import (
|
|
RecordArchiveProviderState,
|
|
RecordArchiveReceipt,
|
|
RecordArchiveTransferRequest,
|
|
RecordContractError,
|
|
RecordFilingRequest,
|
|
RecordSourceLocator,
|
|
RecordSourceReference,
|
|
RecordTransferPackage,
|
|
record_archive_capabilities,
|
|
record_archive_capability,
|
|
record_source_capabilities,
|
|
record_source_capability,
|
|
)
|
|
|
|
|
|
class _Registry:
|
|
def capability_names(self) -> tuple[str, ...]:
|
|
return (
|
|
"mail.delivery",
|
|
"records.archive.simulation",
|
|
"records.source.cases",
|
|
"records.source.files",
|
|
)
|
|
|
|
|
|
class RecordsContractTests(unittest.TestCase):
|
|
def test_exact_source_and_filing_request_are_tenant_bound(self) -> None:
|
|
locator = RecordSourceLocator(
|
|
tenant_id="tenant-1",
|
|
source_module="files",
|
|
resource_type="file_version",
|
|
resource_id="asset-1",
|
|
source_revision="version-7",
|
|
)
|
|
reference = RecordSourceReference(
|
|
locator=locator,
|
|
label="Evidence.pdf",
|
|
content_sha256="a" * 64,
|
|
size_bytes=12,
|
|
recorded_at=datetime(2026, 8, 6, tzinfo=UTC),
|
|
)
|
|
request = RecordFilingRequest(
|
|
tenant_id="tenant-1",
|
|
record_id="record-1",
|
|
source=locator,
|
|
purpose="process application",
|
|
filing_reason="Evidence received with the application.",
|
|
idempotency_key="filing-1",
|
|
)
|
|
|
|
self.assertEqual("version-7", reference.locator.source_revision)
|
|
self.assertEqual("record-1", request.record_id)
|
|
|
|
def test_cross_tenant_filing_and_inexact_sources_are_rejected(self) -> None:
|
|
with self.assertRaisesRegex(RecordContractError, "source_revision"):
|
|
RecordSourceLocator(
|
|
tenant_id="tenant-1",
|
|
source_module="files",
|
|
resource_type="file_version",
|
|
resource_id="asset-1",
|
|
source_revision="",
|
|
)
|
|
|
|
locator = RecordSourceLocator(
|
|
tenant_id="tenant-1",
|
|
source_module="cases",
|
|
resource_type="case_revision",
|
|
resource_id="case-1",
|
|
source_revision="4",
|
|
)
|
|
with self.assertRaisesRegex(RecordContractError, "cross tenants"):
|
|
RecordFilingRequest(
|
|
tenant_id="tenant-2",
|
|
record_id="record-1",
|
|
source=locator,
|
|
purpose="audit",
|
|
filing_reason="Preserve the decision basis.",
|
|
idempotency_key="filing-2",
|
|
)
|
|
|
|
def test_source_capabilities_are_discoverable_without_module_imports(self) -> None:
|
|
self.assertEqual("records.source.files", record_source_capability("files"))
|
|
self.assertEqual(
|
|
"records.source.forms_runtime",
|
|
record_source_capability("forms_runtime"),
|
|
)
|
|
self.assertEqual(
|
|
("records.source.cases", "records.source.files"),
|
|
record_source_capabilities(_Registry()),
|
|
)
|
|
|
|
def test_archive_contract_is_digest_bound_and_discoverable(self) -> None:
|
|
package = RecordTransferPackage(
|
|
tenant_id="tenant-1",
|
|
package_id="package-1",
|
|
record_id="record-1",
|
|
record_revision=3,
|
|
profile="govoplan-simulation-v1",
|
|
manifest_sha256="a" * 64,
|
|
manifest={"record_id": "record-1", "items": []},
|
|
)
|
|
request = RecordArchiveTransferRequest(
|
|
package=package,
|
|
purpose="archive appraisal",
|
|
idempotency_key="transfer-1",
|
|
)
|
|
state = RecordArchiveProviderState(
|
|
provider_id="simulation",
|
|
label="Archive simulation",
|
|
profiles=("govoplan-simulation-v1",),
|
|
authority_modes=("linked_reference",),
|
|
healthy=True,
|
|
checked_at=datetime(2026, 8, 6, tzinfo=UTC),
|
|
simulated=True,
|
|
)
|
|
|
|
self.assertEqual("package-1", request.package.package_id)
|
|
self.assertTrue(state.simulated)
|
|
self.assertEqual(
|
|
"records.archive.simulation",
|
|
record_archive_capability("simulation"),
|
|
)
|
|
self.assertEqual(
|
|
("records.archive.simulation",),
|
|
record_archive_capabilities(_Registry()),
|
|
)
|
|
|
|
def test_unknown_archive_outcome_cannot_be_retry_safe(self) -> None:
|
|
with self.assertRaisesRegex(RecordContractError, "retry-safe"):
|
|
RecordArchiveReceipt(
|
|
provider_id="archive-1",
|
|
package_id="package-1",
|
|
outcome="outcome_unknown",
|
|
observed_at=datetime(2026, 8, 6, tzinfo=UTC),
|
|
receipt_sha256="b" * 64,
|
|
retry_safe=True,
|
|
)
|
|
|
|
def test_archive_provider_runtime_values_are_validated(self) -> None:
|
|
with self.assertRaisesRegex(RecordContractError, "invalid authority mode"):
|
|
RecordArchiveProviderState(
|
|
provider_id="archive",
|
|
label="Archive",
|
|
profiles=("profile",),
|
|
authority_modes=("untrusted",), # type: ignore[arg-type]
|
|
healthy=True,
|
|
checked_at=datetime.now(UTC),
|
|
)
|
|
with self.assertRaisesRegex(RecordContractError, "outcome is invalid"):
|
|
RecordArchiveReceipt(
|
|
provider_id="archive",
|
|
package_id="package-1",
|
|
outcome="maybe", # type: ignore[arg-type]
|
|
observed_at=datetime.now(UTC),
|
|
receipt_sha256="a" * 64,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|