49 lines
1.7 KiB
Python
49 lines
1.7 KiB
Python
"""The public Python CLI exposes exactly the approved workflow surface."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from typer.main import get_command
|
|
from typer.testing import CliRunner
|
|
|
|
from tht.cli import app
|
|
|
|
_FIXTURE = Path(__file__).parent / "fixtures" / "approved_cli_surface.json"
|
|
|
|
|
|
def _leaf_paths(command, prefix: tuple[str, ...] = ()) -> set[str]:
|
|
"""Return the terminal paths offered by the real Typer/Click command tree."""
|
|
children = getattr(command, "commands", {})
|
|
if not children:
|
|
return {" ".join(prefix)} if prefix else set()
|
|
paths: set[str] = set()
|
|
for name, child in children.items():
|
|
paths.update(_leaf_paths(child, prefix + (name,)))
|
|
return paths
|
|
|
|
|
|
def _approved_surface() -> dict[str, list[str]]:
|
|
return json.loads(_FIXTURE.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_typer_tree_matches_the_approved_command_surface():
|
|
"""Catches a missing live command or an accidentally re-registered erased command."""
|
|
approved = _approved_surface()
|
|
expected = set(approved["maintained"]) | set(approved["enhanced"])
|
|
|
|
assert len(approved["maintained"]) == 58
|
|
assert len(approved["enhanced"]) == 8
|
|
assert len(approved["erased"]) == 14
|
|
assert not (expected & set(approved["erased"]))
|
|
assert _leaf_paths(get_command(app)) == expected
|
|
|
|
|
|
def test_erased_commands_are_rejected_with_the_standard_typer_error():
|
|
"""Catches a regression that restores a retired command under an existing group."""
|
|
runner = CliRunner()
|
|
for path in _approved_surface()["erased"]:
|
|
result = runner.invoke(app, [*path.split(), "--help"])
|
|
assert result.exit_code != 0, path
|
|
assert "no such command" in result.output.casefold(), path
|