| """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 |
|
|
|
|
| |
| |
| |
|
|
|
|
| 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() |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestCLIDispatch: |
| """Test suite for CLI argument parsing and subcommand dispatch.""" |
|
|
| |
| 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) |
| |
| assert "usage" in stderr_text.lower() or "required" in stderr_text.lower() |
|
|
| |
| def test_models_command(self): |
| """Calling main(['models']) should list models without raising.""" |
| with patch("obliteratus.cli.console") as mock_console: |
| main(["models"]) |
| |
| assert mock_console.print.call_count >= 1 |
|
|
| |
| 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() |
|
|
| |
| def test_obliterate_valid_methods(self): |
| """Test that --method accepts all 9 pipeline methods.""" |
| valid_methods = [ |
| "basic", "advanced", "aggressive", "spectral_cascade", |
| "informed", "surgical", "optimized", "inverted", "nuclear", |
| ] |
| for method in valid_methods: |
| |
| 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 |
|
|
| |
| def test_obliterate_rejects_invalid_method(self): |
| """The CLI --method flag rejects unknown method names.""" |
| stderr_text = _capture_exit( |
| ["obliterate", "fake/model", "--method", "nonexistent"], |
| expect_code=2, |
| ) |
| assert "invalid choice" in stderr_text.lower() |
|
|
| |
| 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() |
|
|
| |
| 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"]) |
| |
| 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 |
|
|
| |
| 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() |
|
|
| |
| 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() |
|
|
| |
| 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" |
|
|