Spaces:
Running
Running
File size: 5,632 Bytes
e25024e 45113e6 e25024e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """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"
|