"""Unit tests for scripts/ci/doc_coverage.py.""" from pathlib import Path from click.testing import CliRunner from scripts.ci.doc_coverage import ( check_command_documented, check_module_documented, extract_cli_commands, main, ) class TestExtractCliCommands: def test_extracts_commands(self) -> None: commands = extract_cli_commands() # Should find all 9 CLI commands assert "install" in commands assert "update" in commands assert "start" in commands assert "stop" in commands assert "enable" in commands assert "disable" in commands assert "status" in commands assert "remove" in commands assert "list" in commands def test_returns_list(self) -> None: commands = extract_cli_commands() assert isinstance(commands, list) assert len(commands) == 9 class TestCheckCommandDocumented: def test_finds_command_in_heading(self) -> None: content = "## install\n\nInstall a runner." assert check_command_documented("install", content) is True def test_finds_command_in_code_block(self) -> None: content = "```bash\ngrm install 192.168.1.10\n```" assert check_command_documented("install", content) is True def test_finds_command_with_grm_prefix(self) -> None: content = "Use `grm start prod-runner` to start." assert check_command_documented("start", content) is True def test_missing_command(self) -> None: content = "## Other stuff\n\nNo commands here." assert check_command_documented("install", content) is False class TestCheckModuleDocumented: def test_finds_module(self) -> None: content = "The cli.py module handles..." assert check_module_documented("cli.py", content) is True def test_missing_module(self) -> None: content = "No modules mentioned." assert check_module_documented("cli.py", content) is False class TestMain: def test_all_present(self, tmp_path: Path) -> None: """When all docs exist and cover all commands/modules, exit 0.""" docs = tmp_path / "docs" (docs / "user").mkdir(parents=True) (docs / "tech").mkdir(parents=True) # Write cli-commands.md with all commands (docs / "user" / "cli-commands.md").write_text( "## install\n## update\n## start\n## stop\n## enable\n## disable\n## status\n## remove\n## list\n" ) # Write architecture.md with all modules (docs / "tech" / "architecture.md").write_text( "cli.py runner_manager.py executor.py registry.py i18n.py exceptions.py api_clients.py config.py" ) # Write ci-cd-workflow.md with all scripts (docs / "tech" / "ci-cd-workflow.md").write_text( "auto_merge.py release.py publish.py review_pr.py " "notify_failure.py post_merge.py classify_changes.py discover_runners.py " "detect_release_commit.py push_badges.py " "distribute_molecule.py molecule_ci_guard.py validate_commit_msg.py" ) runner = CliRunner() result = runner.invoke(main, ["--docs-dir", str(docs)]) assert result.exit_code == 0 assert "100%" in result.output def test_missing_docs_fail(self, tmp_path: Path) -> None: """When docs are missing and --fail-on-missing is set, exit 1.""" docs = tmp_path / "docs" (docs / "user").mkdir(parents=True) (docs / "tech").mkdir(parents=True) (docs / "user" / "cli-commands.md").write_text("No commands here.") (docs / "tech" / "architecture.md").write_text("No modules here.") (docs / "tech" / "ci-cd-workflow.md").write_text("No scripts here.") runner = CliRunner() result = runner.invoke(main, ["--docs-dir", str(docs), "--fail-on-missing"]) assert result.exit_code == 1 def test_missing_docs_warn_only(self, tmp_path: Path) -> None: """Without --fail-on-missing, missing docs only warn (exit 0).""" docs = tmp_path / "docs" (docs / "user").mkdir(parents=True) (docs / "tech").mkdir(parents=True) (docs / "user" / "cli-commands.md").write_text("No commands here.") (docs / "tech" / "architecture.md").write_text("No modules here.") (docs / "tech" / "ci-cd-workflow.md").write_text("No scripts here.") runner = CliRunner() result = runner.invoke(main, ["--docs-dir", str(docs)]) assert result.exit_code == 0 assert "MISSING" in result.output