obliteratus / tests /test_cli.py
pliny-the-prompter's picture
Upload 127 files
45113e6 verified
"""CLI dispatch tests for obliteratus.cli.main().
These tests verify argument parsing and subcommand routing without
downloading real models or running any pipeline. They use
``unittest.mock.patch`` to capture stdout/stderr and
``pytest.raises(SystemExit)`` for argparse exits.
"""
from __future__ import annotations
from io import StringIO
from unittest.mock import patch
import pytest
from obliteratus.cli import main
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _capture_exit(argv: list[str] | None, *, expect_code: int | None = None):
"""Call main(argv), expecting SystemExit; return captured stderr text."""
buf = StringIO()
with pytest.raises(SystemExit) as exc_info, patch("sys.stderr", buf):
main(argv)
if expect_code is not None:
assert exc_info.value.code == expect_code
return buf.getvalue()
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
class TestCLIDispatch:
"""Test suite for CLI argument parsing and subcommand dispatch."""
# 1. No args -> prints help / exits with error
def test_main_no_args_prints_help(self):
"""Calling main() with no args should exit (subcommand is required)."""
stderr_text = _capture_exit([], expect_code=2)
# argparse prints usage info to stderr on error
assert "usage" in stderr_text.lower() or "required" in stderr_text.lower()
# 2. models command lists models without error
def test_models_command(self):
"""Calling main(['models']) should list models without raising."""
with patch("obliteratus.cli.console") as mock_console:
main(["models"])
# console.print is called at least once to render the table
assert mock_console.print.call_count >= 1
# 3. obliterate without model arg -> error
def test_obliterate_requires_model(self):
"""Calling main(['obliterate']) without a model arg should error."""
stderr_text = _capture_exit(["obliterate"], expect_code=2)
assert "model" in stderr_text.lower() or "required" in stderr_text.lower()
# 4. obliterate --method accepts valid methods
def test_obliterate_valid_methods(self):
"""Test that --method accepts basic, advanced, and aggressive."""
valid_methods = ["basic", "advanced", "aggressive"]
for method in valid_methods:
# Patch the actual pipeline execution so nothing runs
with patch("obliteratus.cli._cmd_abliterate") as mock_cmd:
main(["obliterate", "fake/model", "--method", method])
mock_cmd.assert_called_once()
args_passed = mock_cmd.call_args[0][0]
assert args_passed.method == method
# 4b. informed is NOT a valid --method choice on the CLI
def test_obliterate_rejects_informed_method(self):
"""The CLI --method flag does not accept 'informed' (separate pipeline)."""
stderr_text = _capture_exit(
["obliterate", "fake/model", "--method", "informed"],
expect_code=2,
)
assert "invalid choice" in stderr_text.lower()
# 5. run requires config path
def test_run_requires_config(self):
"""Calling main(['run']) without a config path should error."""
stderr_text = _capture_exit(["run"], expect_code=2)
assert "config" in stderr_text.lower() or "required" in stderr_text.lower()
# 6. aggregate with nonexistent dir handles gracefully
def test_aggregate_command_missing_dir(self):
"""Calling main(['aggregate']) with nonexistent dir should handle gracefully."""
with patch("obliteratus.cli.console") as mock_console:
main(["aggregate", "--dir", "/nonexistent/path/to/nowhere"])
# The command prints a message about no contributions found and returns
printed_text = " ".join(
str(call) for call in mock_console.print.call_args_list
)
assert "no contributions found" in printed_text.lower() or mock_console.print.called
# 7. --help flag prints help
def test_help_flag(self):
"""Calling main(['--help']) should print help and exit 0."""
buf = StringIO()
with pytest.raises(SystemExit) as exc_info, patch("sys.stdout", buf):
main(["--help"])
assert exc_info.value.code == 0
output = buf.getvalue()
assert "obliteratus" in output.lower() or "usage" in output.lower()
# 8. interactive subcommand is registered
def test_interactive_command_exists(self):
"""Verify 'interactive' subcommand is registered and dispatches."""
with patch("obliteratus.cli._cmd_interactive") as mock_cmd:
main(["interactive"])
mock_cmd.assert_called_once()
# 9. --contribute and --contribute-notes are accepted on obliterate
def test_contribute_flags_on_obliterate(self):
"""Verify --contribute and --contribute-notes are accepted args."""
with patch("obliteratus.cli._cmd_abliterate") as mock_cmd:
main([
"obliterate", "fake/model",
"--contribute",
"--contribute-notes", "Testing contribution system",
])
mock_cmd.assert_called_once()
args_passed = mock_cmd.call_args[0][0]
assert args_passed.contribute is True
assert args_passed.contribute_notes == "Testing contribution system"