from __future__ import annotations import json import tempfile import unittest from pathlib import Path from govoplan_core.core.modules import DocumentationTopic, ModuleManifest from govoplan_docs.backend.manifest import get_manifest as get_docs_manifest from govoplan_docs.public_export import ( ManifestSource, build_public_catalog, catalog_matches_sources, coverage_baseline, coverage_regression_issues, documentation_coverage_markdown, write_public_catalog, ) class PublicDocumentationExportTests(unittest.TestCase): def test_docs_manifest_has_complete_german_public_coverage(self) -> None: module = build_public_catalog( (ManifestSource(get_docs_manifest()),) )["modules"][0] self.assertEqual(9, module["coverage"]["topic_count"]) self.assertEqual(0, module["coverage"]["missing_german_topic_count"]) self.assertEqual([], module["coverage"]["missing_german_topic_ids"]) self.assertEqual(2, module["coverage"]["structured_localizable_topic_count"]) self.assertEqual( 2, module["coverage"]["german_structured_complete_topic_count"] ) self.assertEqual( [], module["coverage"]["missing_german_structured_topic_ids"] ) self.assertEqual([], module["coverage"]["missing"]) def test_catalog_projects_localized_manifest_topics_and_gaps(self) -> None: manifest = ModuleManifest( id="example", name="Example", version="1.2.3", documentation=( DocumentationTopic( id="example.workflow", title="Use example", summary="Perform the example workflow.", body="Open and finish it.", documentation_types=("user", "admin"), metadata={ "kind": "workflow", "steps": ["Review", "Execute"], "verification": "Confirm completion.", }, translations={ "de": { "title": "Beispiel verwenden", "summary": "Den Beispielablauf durchführen.", "body": "Öffnen und abschließen.", } }, structured_translation_version="1", structured_translations={ "de": { "steps": ["Prüfen", "Ausführen"], "verification": "Den Abschluss bestätigen.", } }, ), ), ) catalog = build_public_catalog( (ManifestSource(manifest, "https://example.invalid/repository"),) ) topic = catalog["modules"][0]["topics"][0] self.assertEqual("Beispiel verwenden", topic["localizations"]["de"]["title"]) self.assertTrue(topic["localizations"]["de"]["complete"]) self.assertEqual("workflow", topic["kind"]) self.assertEqual(["Review", "Execute"], topic["content"]["steps"]) self.assertEqual( ["Prüfen", "Ausführen"], topic["content_localizations"]["de"]["content"]["steps"], ) self.assertTrue(topic["content_localizations"]["de"]["complete"]) self.assertIn( "field/consequence reference", catalog["modules"][0]["coverage"]["missing"], ) def test_digest_check_ignores_generation_timestamp(self) -> None: source = ManifestSource( ModuleManifest( id="example", name="Example", version="1.0.0", documentation=( DocumentationTopic( id="example.reference", title="Reference", summary="Reference summary", metadata={"kind": "reference"}, ), ), ) ) first = build_public_catalog((source,)) second = build_public_catalog((source,)) with tempfile.TemporaryDirectory() as directory: output = Path(directory) / "catalog.json" write_public_catalog(output, first) self.assertTrue(catalog_matches_sources(output, second)) parsed = json.loads(output.read_text(encoding="utf-8")) parsed["source_digest"] = "stale" output.write_text(json.dumps(parsed), encoding="utf-8") self.assertFalse(catalog_matches_sources(output, second)) def test_coverage_report_identifies_generated_source(self) -> None: catalog = build_public_catalog( ( ManifestSource( ModuleManifest( id="example", name="Example", version="1.0.0", documentation=(), ) ), ) ) report = documentation_coverage_markdown(catalog) self.assertIn("Public documentation coverage", report) self.assertIn("`example`", report) self.assertIn("Edit module manifests", report) def test_catalog_preserves_structured_static_documentation(self) -> None: manifest = ModuleManifest( id="example", name="Example", version="1.0.0", documentation=( DocumentationTopic( id="example.workflow", title="Run example", summary="Run it safely.", metadata={ "kind": "workflow", "prerequisites": ["Approved input"], "steps": ["Review", "Execute"], "fields": [ { "label": "Mode", "user_description": "Selected behavior.", } ], }, ), ), ) topic = build_public_catalog((ManifestSource(manifest),))["modules"][0]["topics"][0] self.assertEqual(["Review", "Execute"], topic["content"]["steps"]) self.assertEqual("Mode", topic["content"]["fields"][0]["label"]) def test_catalog_preserves_specialized_documentation_kinds(self) -> None: manifest = ModuleManifest( id="example", name="Example", version="1.0.0", documentation=( DocumentationTopic( id="example.runbook", title="Recover example", summary="Restore the example safely.", metadata={"kind": "operator_workflow"}, ), ), ) topic = build_public_catalog((ManifestSource(manifest),))["modules"][0]["topics"][0] self.assertEqual("operator-workflow", topic["kind"]) def test_coverage_baseline_rejects_text_and_structured_regressions(self) -> None: healthy = build_public_catalog( ( ManifestSource( ModuleManifest( id="example", name="Example", version="1.0.0", documentation=( DocumentationTopic( id="example.reference", title="Reference", summary="Reference summary.", body="Reference body.", translations={ "de": { "title": "Referenz", "summary": "Referenzzusammenfassung.", "body": "Referenzinhalt.", } }, metadata={ "kind": "reference", "limitations": ["Source limitation."], }, structured_translation_version="1", structured_translations={ "de": { "limitations": [ "Einschränkung der Quelle." ] } }, ), ), ) ), ) ) baseline = coverage_baseline(healthy) self.assertEqual((), coverage_regression_issues(healthy, baseline)) regressed = json.loads(json.dumps(healthy)) regressed["summary"]["german_complete_topic_count"] = 0 regressed["summary"]["german_structured_complete_topic_count"] = 0 issues = coverage_regression_issues(regressed, baseline) self.assertTrue(any("german_complete_topic_count" in issue for issue in issues)) self.assertTrue( any("german_structured_complete_topic_count" in issue for issue in issues) ) if __name__ == "__main__": unittest.main()