from __future__ import annotations import unittest from govoplan_core.core.modules import ( CapabilityDocumentation, DocumentationConfigurationDecision, DocumentationConfigurationProviderRegistration, DocumentationCondition, DocumentationSourceDefinition, DocumentationTopic, ModuleManifest, localized_documentation_metadata, user_workflow_scope_condition_issues, with_documentation_structured_translations, ) from govoplan_core.core.registry import PlatformRegistry, RegistryError def workflow_topic( *, conditions: tuple[DocumentationCondition, ...], documentation_types: tuple[str, ...] = ("user",), kind: str = "workflow", ) -> DocumentationTopic: return DocumentationTopic( id="example.workflow.task", title="Complete a task", summary="Complete the example task.", documentation_types=documentation_types, # type: ignore[arg-type] conditions=conditions, metadata={"kind": kind}, ) class DocumentationTopicContractTests(unittest.TestCase): def test_registry_rejects_empty_documentation_version_range(self) -> None: topic = DocumentationTopic( id="example.versioned", title="Versioned", summary="Invalid range", version_min="2.0.0", version_max_exclusive="2.0.0", ) with self.assertRaisesRegex(RegistryError, "empty version range"): registry_for(topic).validate() def test_user_workflow_requires_scope_conditions(self) -> None: topic = workflow_topic(conditions=()) self.assertEqual( user_workflow_scope_condition_issues(topic), ("user workflow topics must declare at least one scope-conditioned alternative",), ) with self.assertRaisesRegex(RegistryError, "scope-conditioned alternative"): registry_for(topic).validate() def test_every_condition_alternative_must_be_scope_conditioned(self) -> None: topic = workflow_topic( conditions=( DocumentationCondition(required_scopes=("example:item:read",)), DocumentationCondition(required_modules=("example",)), ) ) with self.assertRaisesRegex(RegistryError, r"unscoped alternative\(s\): 2"): registry_for(topic).validate() def test_scoped_user_workflow_and_non_user_topics_are_accepted(self) -> None: scoped = workflow_topic( conditions=( DocumentationCondition(required_scopes=("example:item:read",)), DocumentationCondition(any_scopes=("example:item:write", "example:item:admin")), ) ) admin_workflow = workflow_topic( conditions=(), documentation_types=("admin",), ) user_reference = workflow_topic(conditions=(), kind="reference") self.assertEqual(user_workflow_scope_condition_issues(scoped), ()) self.assertEqual(user_workflow_scope_condition_issues(admin_workflow), ()) self.assertEqual(user_workflow_scope_condition_issues(user_reference), ()) registry_for(scoped, admin_workflow, user_reference).validate() def test_versioned_structured_translation_preserves_metadata_shape(self) -> None: topic = DocumentationTopic( id="example.workflow.localized", title="Run task", summary="Run the task.", metadata={ "kind": "workflow", "steps": ["Review", "Execute"], "verification": "Confirm the result.", }, structured_translation_version="1", structured_translations={ "de": { "steps": ["Prüfen", "Ausführen"], "verification": "Das Ergebnis bestätigen.", } }, ) registry_for(topic).validate() self.assertEqual( ["Prüfen", "Ausführen"], localized_documentation_metadata(topic, "de")["steps"], ) self.assertEqual( "workflow", localized_documentation_metadata(topic, "de")["kind"] ) def test_structured_translation_requires_version_and_complete_shape(self) -> None: missing_version = DocumentationTopic( id="example.localized.missing-version", title="Localized", summary="Invalid contract.", metadata={"limitations": ["One", "Two"]}, structured_translations={"de": {"limitations": ["Eins", "Zwei"]}}, ) with self.assertRaisesRegex( RegistryError, "require structured_translation_version" ): registry_for(missing_version).validate() incomplete_shape = DocumentationTopic( id="example.localized.incomplete", title="Localized", summary="Invalid shape.", metadata={"limitations": ["One", "Two"]}, structured_translation_version="1", structured_translations={"de": {"limitations": ["Eins"]}}, ) with self.assertRaisesRegex(RegistryError, "preserve list length"): registry_for(incomplete_shape).validate() def test_manifest_helper_merges_and_validates_owner_translations(self) -> None: topic = DocumentationTopic( id="example.workflow.localized", title="Run task", summary="Run the task.", metadata={"steps": ["Review", "Execute"]}, ) manifest = ModuleManifest( id="example", name="Example", version="1.0.0", documentation=(topic,), ) localized = with_documentation_structured_translations( manifest, locale="de", translations={ topic.id: {"steps": ["Prüfen", "Ausführen"]}, }, ) self.assertEqual( ["Prüfen", "Ausführen"], localized.documentation[0].structured_translations["de"]["steps"], ) with self.assertRaisesRegex(ValueError, "unknown topic ids"): with_documentation_structured_translations( manifest, locale="de", translations={"missing.topic": {"steps": ["Prüfen", "Ausführen"]}}, ) with self.assertRaisesRegex(ValueError, "preserve list length"): with_documentation_structured_translations( manifest, locale="de", translations={topic.id: {"steps": ["Prüfen"]}}, ) def test_documentation_configuration_and_source_extensions_are_validated(self) -> None: resolver = lambda _context, keys: { # noqa: E731 key: DocumentationConfigurationDecision(key=key, state="enabled") for key in keys } registry = PlatformRegistry() registry.register(ModuleManifest( id="example", name="Example", version="1.0.0", documentation_configuration_providers=( DocumentationConfigurationProviderRegistration( keys=("example.feature",), resolve=resolver, ), ), documentation_sources=( DocumentationSourceDefinition( id="example.handbook", kind="repository", label="Example handbook", ), ), )) registry.validate() duplicate = PlatformRegistry() duplicate.register(ModuleManifest( id="example", name="Example", version="1.0.0", documentation_configuration_providers=( DocumentationConfigurationProviderRegistration( keys=("example.feature",), resolve=resolver, ), DocumentationConfigurationProviderRegistration( keys=("example.feature",), resolve=resolver, ), ), )) with self.assertRaisesRegex(RegistryError, "duplicate documentation configuration key"): duplicate.validate() def test_capability_documentation_is_typed_and_must_match_a_provider(self) -> None: registry = PlatformRegistry() registry.register(ModuleManifest( id="example", name="Example", version="1.0.0", capability_factories={"example.lookup": lambda _context: object()}, capability_documentation={ "example.lookup": CapabilityDocumentation( label="Example lookup", summary="Resolves example records without exposing provider internals.", contract_version="2", audience=("module_admin",), ), }, )) registry.validate() missing_provider = PlatformRegistry() missing_provider.register(ModuleManifest( id="example", name="Example", version="1.0.0", capability_documentation={ "example.lookup": CapabilityDocumentation( label="Example lookup", summary="Resolves example records.", ), }, )) with self.assertRaisesRegex(RegistryError, "does not provide it"): missing_provider.validate() def registry_for(*topics: DocumentationTopic) -> PlatformRegistry: registry = PlatformRegistry() registry.register( ModuleManifest( id="example", name="Example", version="1.0.0", documentation=topics, ) ) return registry if __name__ == "__main__": unittest.main()