Add /docs/ directory with user and technical documentation extracted from README, AGENTS.md, and source code. Add scripts/sync_wiki.py to sync docs to Gitea wiki via API. Add scripts/doc_coverage.py to check CLI commands, modules, and CI scripts are documented. Add sync-wiki.yml workflow for auto-sync on merge and release. Slim down README.md to lean entry point. 28 new unit tests, 100% coverage maintained. Closes GRM-36
110 lines
4.3 KiB
Python
110 lines
4.3 KiB
Python
"""Unit tests for scripts/doc_coverage.py."""
|
|
|
|
from pathlib import Path
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from scripts.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"
|
|
)
|
|
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
|