Files
cleveragents-core/robot/helper_invariant_cli.py
2026-06-18 02:28:23 -04:00

200 lines
6.7 KiB
Python

"""Helper script for invariant_cli.robot smoke tests.
Each subcommand is a self-contained check that prints a sentinel on success.
"""
from __future__ import annotations
import sys
from pathlib import Path
from unittest.mock import patch
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from typer.testing import CliRunner # noqa: E402
from cleveragents.application.services.invariant_service import ( # noqa: E402
InvariantService,
)
from cleveragents.cli.commands.invariant import app as invariant_app # noqa: E402
runner = CliRunner()
def _fresh_service() -> InvariantService:
"""Create a fresh in-memory service for each test."""
return InvariantService()
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def add_global() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(
invariant_app, ["add", "--global", "Never delete production data"]
)
if result.exit_code == 0 and "Invariant added" in result.stdout:
print("invariant-add-global-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def add_project() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(
invariant_app, ["add", "--project", "myapp", "All API changes need tests"]
)
if result.exit_code == 0 and "Invariant added" in result.stdout:
print("invariant-add-project-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def add_plan() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(
invariant_app,
["add", "--plan", "01TESTPLANID00000000000000", "Use Python 3.13"],
)
if result.exit_code == 0 and "Invariant added" in result.stdout:
print("invariant-add-plan-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def add_action() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(
invariant_app,
["add", "--action", "local/code-coverage", "Min 80% coverage"],
)
if result.exit_code == 0 and "Invariant added" in result.stdout:
print("invariant-add-action-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def list_empty() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(invariant_app, ["list"])
if result.exit_code == 0 and "No invariants found" in result.stdout:
print("invariant-list-empty-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def list_filter() -> None:
svc = _fresh_service()
svc.add_invariant("Global rule", scope=_scope("global"), source_name="system")
svc.add_invariant("Project rule", scope=_scope("project"), source_name="myapp")
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(invariant_app, ["list", "--global"])
if result.exit_code == 0 and "Global rule" in result.stdout:
print("invariant-list-filter-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def remove() -> None:
svc = _fresh_service()
inv = svc.add_invariant(
"To be removed", scope=_scope("global"), source_name="system"
)
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(invariant_app, ["remove", "--yes", inv.id])
if result.exit_code == 0 and "Invariant removed" in result.stdout:
print("invariant-remove-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def scope_conflict() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(
invariant_app,
["add", "--global", "--project", "myapp", "Conflicting scopes"],
)
# Should fail with a bad parameter error
if result.exit_code != 0:
print("invariant-scope-conflict-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def add_no_scope() -> None:
svc = _fresh_service()
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(invariant_app, ["add", "Missing scope invariant"])
output = result.output or ""
if result.exit_code == 0 and "Invariant added" in output:
print("invariant-add-no-scope-global-ok")
else:
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def list_json() -> None:
svc = _fresh_service()
svc.add_invariant("JSON test rule", scope=_scope("global"), source_name="system")
with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
result = runner.invoke(invariant_app, ["list", "--format", "json"])
if result.exit_code == 0:
# Verify output is valid JSON-like
output = result.stdout.strip()
if "JSON test rule" in output:
print("invariant-list-json-ok")
return
print(f"FAIL: exit={result.exit_code} out={result.stdout}")
sys.exit(1)
def _scope(name: str):
from cleveragents.domain.models.core.invariant import InvariantScope
return InvariantScope(name)
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
COMMANDS = {
"add-global": add_global,
"add-project": add_project,
"add-plan": add_plan,
"add-action": add_action,
"list-empty": list_empty,
"list-filter": list_filter,
"remove": remove,
"scope-conflict": scope_conflict,
"add-no-scope": add_no_scope,
"list-json": list_json,
}
if __name__ == "__main__":
cmd = sys.argv[1] if len(sys.argv) > 1 else None
if cmd not in COMMANDS:
print(f"Unknown command: {cmd}", file=sys.stderr)
print(f"Available: {', '.join(sorted(COMMANDS))}", file=sys.stderr)
sys.exit(2)
COMMANDS[cmd]()