Files
govoplan-core/tests/test_provider_governance_contract.py
T

537 lines
20 KiB
Python

from __future__ import annotations
from datetime import UTC, datetime
import unittest
from govoplan_core.core.modules import ModuleManifest
from govoplan_core.core.provider_governance import (
ArchitectureDeclarationError,
ExternalProviderDeclaration,
ExternalProviderRuntimeState,
ExternalProviderStateContext,
ExternalProviderStateProviderRegistration,
ModuleArchitectureDeclaration,
ModuleArchitectureDocumentation,
ModuleMaturityEvidence,
ProviderBehaviorDeclaration,
ProviderObjectDeclaration,
collect_external_provider_states,
declared_module_architecture,
external_provider_from_mapping,
module_architecture_from_mapping,
module_architecture_issues,
)
from govoplan_core.core.registry import PlatformRegistry, RegistryError
def _architecture(
*,
providers: tuple[str, ...] = (),
modes: tuple[str, ...] = ("external_mirror",),
) -> ModuleArchitectureDeclaration:
return ModuleArchitectureDeclaration(
layer="data_reporting_integration",
kind="integration",
maturity="vertical_slice",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_provider.py",
summary="Exercises the bounded provider contract.",
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/PROVIDER.md",
summary="Documents authority and outage behavior.",
),
),
known_limits=("Only bounded test objects are supported.",),
supported_authority_modes=modes,
owned_concepts=("external transport",),
non_owned_concepts=("domain records",),
target_tested_providers=providers,
)
def _mirror_provider() -> ExternalProviderDeclaration:
return ExternalProviderDeclaration(
id="connectors.read_mirror",
module_id="connectors",
label="Read-only mirror",
maturity="read",
operations=("discover", "search", "read", "preview"),
objects=(
ProviderObjectDeclaration(
object_type="register_entry",
field_groups=("identity", "content", "source_metadata"),
authority_modes=("external_authoritative", "external_mirror"),
default_authority_mode="external_mirror",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="ETag and source revision are retained.",
concurrency="Reads can require an expected source revision.",
freshness="The latest successful acquisition time is reported.",
health="Transport and parsing health are reported separately.",
max_read_items=500,
evidence="Immutable snapshots retain acquisition provenance.",
correction="Refresh creates a new snapshot; old evidence is retained.",
reconciliation="Compare the latest source revision and content digest.",
outage="The last governed snapshot remains readable as stale.",
classifications=("internal",),
purposes=("governed import",),
retention="Owning datasource retention policy applies.",
secret_handling="Credentials stay in credential envelopes.",
),
capability_names=("connectors.readMirror",),
)
def _writable_provider() -> ExternalProviderDeclaration:
return ExternalProviderDeclaration(
id="calendar.caldav_sync",
module_id="calendar",
label="CalDAV synchronization",
maturity="synchronize",
operations=("discover", "read", "write", "delete", "synchronize", "preview"),
objects=(
ProviderObjectDeclaration(
object_type="calendar_event",
field_groups=("identity", "schedule", "participants", "recurrence"),
authority_modes=("external_authoritative", "governed_sync"),
default_authority_mode="governed_sync",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="ETag and sync token.",
concurrency="Conditional requests reject stale ETags.",
freshness="Last successful sync and pending changes are reported.",
health="Discovery, authentication, and collection health are separate.",
max_read_items=1000,
idempotency="Stable UID and operation keys suppress duplicate effects.",
retry="Bounded retry is limited to classified transient failures.",
timeout_seconds=30,
conflicts="Conflicts remain explicit until policy or a user resolves them.",
outcome_unknown="Timed-out writes are outcome-unknown, never blindly retried.",
outcome_unknown_supported=True,
evidence="Request, response token, and reconciliation evidence are retained.",
audit_event_types=("calendar.sync.requested", "calendar.sync.reconciled"),
correction="A later governed write or tombstone corrects remote state.",
rollback="Remote writes are not assumed rollback-safe.",
compensation="A compensating update can be requested after reconciliation.",
reconciliation="Read by UID and compare ETag before retrying.",
outage="Local state remains marked stale and pending effects stay queued.",
classifications=("confidential",),
purposes=("calendar synchronization",),
retention="Calendar and audit retention policies apply independently.",
secret_handling="Credentials are referenced through credential envelopes.",
),
capability_names=("calendar.sync",),
)
def _state_registration(
*,
module_id: str,
provider_id: str,
) -> ExternalProviderStateProviderRegistration:
return ExternalProviderStateProviderRegistration(
module_id=module_id,
provider_id=provider_id,
provider=lambda _context: (),
)
class ProviderGovernanceContractTests(unittest.TestCase):
def test_concise_declaration_keeps_repository_evidence_explicit(self) -> None:
architecture = declared_module_architecture(
layer="human_work_procedure",
kind="domain",
maturity="vertical_slice",
documentation_ref="docs/DOMAIN.md",
test_ref="tests/test_service.py",
known_limits=("Only the bounded service slice is implemented.",),
owned_concepts=("domain record",),
non_owned_concepts=("identity",),
)
self.assertEqual("vertical_slice", architecture.maturity)
self.assertEqual(
{"documentation", "test"},
{item.kind for item in architecture.evidence},
)
self.assertEqual(("domain record",), architecture.owned_concepts)
def test_declarations_round_trip_through_catalog_mappings(self) -> None:
provider = _writable_provider()
architecture = _architecture(
providers=(provider.id,),
modes=("external_authoritative", "governed_sync"),
)
self.assertEqual(
architecture,
module_architecture_from_mapping(architecture.to_dict()),
)
self.assertEqual(
provider,
external_provider_from_mapping(provider.to_dict()),
)
def test_read_only_mirror_declaration_is_registry_validated(self) -> None:
provider = _mirror_provider()
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="connectors",
name="Connectors",
version="1.0.0",
architecture=_architecture(
providers=(provider.id,),
modes=("external_authoritative", "external_mirror"),
),
external_providers=(provider,),
external_provider_state_providers=(
_state_registration(
module_id="connectors",
provider_id=provider.id,
),
),
capability_factories={"connectors.readMirror": lambda _context: object()},
)
)
snapshot = registry.validate()
self.assertEqual("connectors", snapshot.manifests[0].id)
self.assertEqual((provider,), registry.external_provider_declarations())
def test_writable_outcome_unknown_provider_is_explicit(self) -> None:
provider = _writable_provider()
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="calendar",
name="Calendar",
version="1.0.0",
architecture=_architecture(
providers=(provider.id,),
modes=("external_authoritative", "governed_sync"),
),
external_providers=(provider,),
external_provider_state_providers=(
_state_registration(
module_id="calendar",
provider_id=provider.id,
),
),
capability_factories={"calendar.sync": lambda _context: object()},
)
)
registry.validate()
payload = provider.to_dict()
self.assertTrue(payload["behavior"]["outcome_unknown_supported"])
self.assertIn("reconciliation", payload["behavior"])
def test_provider_requires_a_sanitized_runtime_state_registration(self) -> None:
provider = _mirror_provider()
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="connectors",
name="Connectors",
version="1.0.0",
architecture=_architecture(
providers=(provider.id,),
modes=("external_authoritative", "external_mirror"),
),
external_providers=(provider,),
capability_factories={
"connectors.readMirror": lambda _context: object()
},
)
)
with self.assertRaisesRegex(
RegistryError,
"require sanitized runtime state providers",
):
registry.validate()
def test_runtime_binding_states_are_validated_and_aggregated(self) -> None:
observed_at = datetime(2026, 8, 1, 12, 0, tzinfo=UTC)
def states(_context: ExternalProviderStateContext):
return (
ExternalProviderRuntimeState(
provider_id="calendar.caldav_sync",
binding_ref="calendar:sync-source:one",
authority_mode="governed_sync",
observed_at=observed_at,
configured=True,
active=True,
health="healthy",
freshness="current",
conflict="clear",
recovery="ready",
),
ExternalProviderRuntimeState(
provider_id="calendar.caldav_sync",
binding_ref="calendar:sync-source:two",
authority_mode="external_mirror",
observed_at=observed_at,
configured=True,
active=True,
health="warning",
freshness="stale",
conflict="pending",
recovery="attention",
),
)
payload = collect_external_provider_states(
(
ExternalProviderStateProviderRegistration(
module_id="calendar",
provider_id="calendar.caldav_sync",
provider=states,
),
),
ExternalProviderStateContext(session=None, tenant_id="tenant-1"),
)["calendar.caldav_sync"]
self.assertEqual("warning", payload["health"])
self.assertEqual("stale", payload["freshness"])
self.assertEqual("pending", payload["conflict"])
self.assertEqual("attention", payload["recovery"])
self.assertEqual(2, len(payload["bindings"]))
def test_runtime_state_registration_requires_declared_provider(self) -> None:
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="calendar",
name="Calendar",
version="1.0.0",
architecture=_architecture(),
external_provider_state_providers=(
ExternalProviderStateProviderRegistration(
module_id="calendar",
provider_id="calendar.caldav_sync",
provider=lambda _context: (),
),
),
)
)
with self.assertRaisesRegex(RegistryError, "undeclared provider"):
registry.validate()
def test_runtime_state_failure_is_sanitized(self) -> None:
def fail(_context: ExternalProviderStateContext):
raise RuntimeError("secret transport detail")
payload = collect_external_provider_states(
(
ExternalProviderStateProviderRegistration(
module_id="calendar",
provider_id="calendar.caldav_sync",
provider=fail,
),
),
ExternalProviderStateContext(session=None),
)["calendar.caldav_sync"]
self.assertEqual("error", payload["health"])
self.assertNotIn("secret transport detail", str(payload))
def test_supported_claim_requires_evidence_not_only_a_string(self) -> None:
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="example",
name="Example",
version="1.0.0",
architecture=ModuleArchitectureDeclaration(
layer="domain_capability",
kind="domain",
maturity="supported",
evidence=(),
owned_concepts=("example",),
),
)
)
with self.assertRaisesRegex(RegistryError, "missing evidence"):
registry.validate()
def test_supported_and_lts_claims_require_reference_packages(self) -> None:
for maturity in ("supported", "lts"):
with self.subTest(maturity=maturity):
declaration = ModuleArchitectureDeclaration(
layer="domain_capability",
kind="domain",
maturity=maturity,
evidence=tuple(
ModuleMaturityEvidence(
kind=kind,
reference=f"evidence/{kind}.json",
summary=f"{kind} evidence.",
)
for kind in (
"test",
"documentation",
"reference_process",
"target_test",
"recovery",
"security",
"operations",
"accessibility",
"privacy",
"upgrade",
*(("compatibility",) if maturity == "lts" else ()),
)
),
owned_concepts=("example",),
documentation=ModuleArchitectureDocumentation(
upgrade=("docs/UPGRADE.md",),
recovery=("docs/RECOVERY.md",),
security=("docs/SECURITY.md",),
operations=("docs/OPERATIONS.md",),
),
)
self.assertEqual(
(f"{maturity} maturity requires a reference package",),
module_architecture_issues(declaration, has_migrations=False),
)
def test_reference_ready_claim_requires_target_human_and_operator_evidence(self) -> None:
declaration = ModuleArchitectureDeclaration(
layer="domain_capability",
kind="domain",
maturity="reference_ready",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_reference.py",
summary="Automated journey proof.",
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/REFERENCE.md",
summary="Reference composition documentation.",
),
ModuleMaturityEvidence(
kind="reference_process",
reference="docs/JOURNEY.md",
summary="Reference process definition.",
),
ModuleMaturityEvidence(
kind="recovery",
reference="evidence/recovery.json",
summary="Recovery drill evidence.",
),
),
known_limits=("One provider profile remains bounded.",),
owned_concepts=("example",),
reference_packages=("reference.example",),
)
issues = module_architecture_issues(declaration, has_migrations=False)
self.assertIn("accessibility", issues[0])
self.assertIn("operations", issues[0])
self.assertIn("privacy", issues[0])
self.assertIn("security", issues[0])
self.assertIn("target_test", issues[0])
self.assertIn("missing documentation", issues[1])
def test_target_tested_provider_requires_provider_evidence(self) -> None:
evidence = tuple(
ModuleMaturityEvidence(
kind=kind,
reference=f"evidence/{kind}.json",
summary=f"{kind} evidence.",
)
for kind in (
"test",
"documentation",
"reference_process",
"target_test",
"recovery",
"security",
"operations",
"accessibility",
"privacy",
)
)
declaration = ModuleArchitectureDeclaration(
layer="data_reporting_integration",
kind="integration",
maturity="reference_ready",
evidence=evidence,
known_limits=("One bounded target profile is assessed.",),
reference_packages=("reference.integration",),
target_tested_providers=("connectors.example",),
documentation=ModuleArchitectureDocumentation(
recovery=("docs/RECOVERY.md",),
security=("docs/SECURITY.md",),
operations=("docs/OPERATIONS.md",),
),
)
self.assertEqual(
(
"maturity 'reference_ready' is missing evidence: provider",
),
module_architecture_issues(declaration, has_migrations=False),
)
def test_provider_references_must_exist_in_manifest(self) -> None:
provider = _mirror_provider()
registry = PlatformRegistry()
registry.register(
ModuleManifest(
id="connectors",
name="Connectors",
version="1.0.0",
architecture=_architecture(
providers=(provider.id,),
modes=("external_authoritative", "external_mirror"),
),
external_providers=(provider,),
)
)
with self.assertRaisesRegex(RegistryError, "undeclared capabilities"):
registry.validate()
def test_governed_sync_requires_read_and_write_semantics(self) -> None:
with self.assertRaisesRegex(
ArchitectureDeclarationError,
"read and write/synchronize",
):
ExternalProviderDeclaration(
id="broken.sync",
module_id="broken",
label="Broken sync",
maturity="synchronize",
operations=("synchronize",),
objects=(
ProviderObjectDeclaration(
object_type="object",
field_groups=("content",),
authority_modes=("governed_sync",),
default_authority_mode="governed_sync",
),
),
behavior=_writable_provider().behavior,
)
if __name__ == "__main__":
unittest.main()