PlanOpticon

planopticon / tests / test_cli_ux.py
Source Blame History 161 lines
1707c67… noreply 1 """Tests for CLI UX improvements — doctor, init wizard, and tab completion."""
1707c67… noreply 2
1707c67… noreply 3 import os
1707c67… noreply 4 from unittest.mock import MagicMock, patch
1707c67… noreply 5
1707c67… noreply 6 from click.testing import CliRunner
1707c67… noreply 7
1707c67… noreply 8 from video_processor.cli.commands import cli
1707c67… noreply 9 from video_processor.cli.companion import CompanionREPL
1707c67… noreply 10 from video_processor.cli.doctor import (
1707c67… noreply 11 check_api_keys,
1707c67… noreply 12 check_dotenv,
1707c67… noreply 13 check_ffmpeg,
1707c67… noreply 14 check_optional_deps,
1707c67… noreply 15 check_python_version,
1707c67… noreply 16 format_results,
1707c67… noreply 17 run_all_checks,
1707c67… noreply 18 )
1707c67… noreply 19
1707c67… noreply 20
1707c67… noreply 21 class TestDoctor:
1707c67… noreply 22 def test_check_python_version(self):
1707c67… noreply 23 name, status, detail = check_python_version()
1707c67… noreply 24 assert name == "Python"
1707c67… noreply 25 assert status == "ok"
1707c67… noreply 26
1707c67… noreply 27 def test_check_ffmpeg_found(self):
1707c67… noreply 28 with patch("video_processor.cli.doctor.shutil") as mock_shutil:
1707c67… noreply 29 mock_shutil.which.return_value = "/usr/bin/ffmpeg"
1707c67… noreply 30 name, status, detail = check_ffmpeg()
1707c67… noreply 31 assert status == "ok"
1707c67… noreply 32
1707c67… noreply 33 def test_check_ffmpeg_missing(self):
1707c67… noreply 34 with patch("video_processor.cli.doctor.shutil") as mock_shutil:
1707c67… noreply 35 mock_shutil.which.return_value = None
1707c67… noreply 36 name, status, detail = check_ffmpeg()
1707c67… noreply 37 assert status == "missing"
1707c67… noreply 38
1707c67… noreply 39 def test_check_api_keys_with_key(self):
1707c67… noreply 40 with patch.dict(os.environ, {"OPENAI_API_KEY": "sk-test1234567890"}):
1707c67… noreply 41 results = check_api_keys()
1707c67… noreply 42 openai = [r for r in results if r[0].strip() == "OpenAI"]
1707c67… noreply 43 assert len(openai) == 1
1707c67… noreply 44 assert openai[0][1] == "ok"
1707c67… noreply 45 assert "sk-t" in openai[0][2]
1707c67… noreply 46
1707c67… noreply 47 def test_check_api_keys_without_key(self):
1707c67… noreply 48 with patch.dict(os.environ, {}, clear=True):
1707c67… noreply 49 results = check_api_keys()
1707c67… noreply 50 openai = [r for r in results if "OpenAI" in r[0]]
1707c67… noreply 51 assert openai[0][1] == "not set"
1707c67… noreply 52
1707c67… noreply 53 def test_check_dotenv_exists(self, tmp_path, monkeypatch):
1707c67… noreply 54 monkeypatch.chdir(tmp_path)
1707c67… noreply 55 (tmp_path / ".env").write_text("KEY=val\n")
1707c67… noreply 56 name, status, detail = check_dotenv()
1707c67… noreply 57 assert status == "ok"
1707c67… noreply 58
1707c67… noreply 59 def test_check_dotenv_missing(self, tmp_path, monkeypatch):
1707c67… noreply 60 monkeypatch.chdir(tmp_path)
1707c67… noreply 61 name, status, detail = check_dotenv()
1707c67… noreply 62 assert status == "not found"
1707c67… noreply 63
1707c67… noreply 64 def test_check_optional_deps(self):
1707c67… noreply 65 results = check_optional_deps()
1707c67… noreply 66 assert len(results) > 0
1707c67… noreply 67 # All results should have 3 elements
1707c67… noreply 68 for name, status, detail in results:
1707c67… noreply 69 assert status in ("ok", "not installed")
1707c67… noreply 70
1707c67… noreply 71 def test_format_results(self):
1707c67… noreply 72 results = [
1707c67… noreply 73 ("Python", "ok", "3.12.0"),
1707c67… noreply 74 ("FFmpeg", "missing", "Install it"),
1707c67… noreply 75 ]
1707c67… noreply 76 output = format_results(results)
1707c67… noreply 77 assert "PlanOpticon Doctor" in output
1707c67… noreply 78 assert "[ok]" in output
1707c67… noreply 79 assert "[XX]" in output
1707c67… noreply 80
1707c67… noreply 81 def test_run_all_checks(self):
1707c67… noreply 82 with patch(
1707c67… noreply 83 "video_processor.integrators.graph_discovery.find_nearest_graph",
1707c67… noreply 84 return_value=None,
1707c67… noreply 85 ):
1707c67… noreply 86 results = run_all_checks()
1707c67… noreply 87 assert len(results) > 5
1707c67… noreply 88 # Should have section headers
1707c67… noreply 89 sections = [r for r in results if r[1] == "section"]
1707c67… noreply 90 assert len(sections) >= 2
1707c67… noreply 91
1707c67… noreply 92 def test_doctor_cli_command(self):
1707c67… noreply 93 runner = CliRunner()
1707c67… noreply 94 with patch(
1707c67… noreply 95 "video_processor.integrators.graph_discovery.find_nearest_graph",
1707c67… noreply 96 return_value=None,
1707c67… noreply 97 ):
1707c67… noreply 98 result = runner.invoke(cli, ["doctor"])
1707c67… noreply 99 assert result.exit_code == 0
1707c67… noreply 100 assert "PlanOpticon Doctor" in result.output
1707c67… noreply 101
1707c67… noreply 102
1707c67… noreply 103 class TestInitWizard:
1707c67… noreply 104 def test_init_cli_command_exists(self):
1707c67… noreply 105 runner = CliRunner()
1707c67… noreply 106 result = runner.invoke(cli, ["init", "--help"])
1707c67… noreply 107 assert result.exit_code == 0
1707c67… noreply 108 assert "setup wizard" in result.output.lower() or "wizard" in result.output.lower()
1707c67… noreply 109
1707c67… noreply 110 def test_wizard_provider_selection(self):
1707c67… noreply 111 """Test the wizard runs with simulated input."""
1707c67… noreply 112 runner = CliRunner()
1707c67… noreply 113 # Select provider 1 (OpenAI), enter a key, decline additional providers
1707c67… noreply 114 result = runner.invoke(
1707c67… noreply 115 cli,
1707c67… noreply 116 ["init"],
1707c67… noreply 117 input="1\nsk-test-key-1234567890\nn\n",
1707c67… noreply 118 )
1707c67… noreply 119 assert result.exit_code == 0
1707c67… noreply 120 assert "Setup complete" in result.output
1707c67… noreply 121
1707c67… noreply 122 def test_wizard_ollama_provider(self):
1707c67… noreply 123 """Test selecting Ollama (no API key needed)."""
1707c67… noreply 124 runner = CliRunner()
1707c67… noreply 125 with patch(
1707c67… noreply 126 "video_processor.cli.init_wizard.shutil.which",
1707c67… noreply 127 return_value="/usr/local/bin/ollama",
1707c67… noreply 128 ):
1707c67… noreply 129 with patch("subprocess.run") as mock_run:
1707c67… noreply 130 mock_run.return_value = MagicMock(returncode=0, stdout="NAME\nllama3\n")
1707c67… noreply 131 result = runner.invoke(
1707c67… noreply 132 cli,
1707c67… noreply 133 ["init"],
1707c67… noreply 134 input="4\nn\n",
1707c67… noreply 135 )
1707c67… noreply 136 assert result.exit_code == 0
1707c67… noreply 137 assert "Setup complete" in result.output
1707c67… noreply 138
1707c67… noreply 139
1707c67… noreply 140 class TestCompanionTabCompletion:
1707c67… noreply 141 def test_commands_list_exists(self):
1707c67… noreply 142 assert len(CompanionREPL.COMMANDS) > 10
1707c67… noreply 143 assert "/help" in CompanionREPL.COMMANDS
1707c67… noreply 144 assert "/quit" in CompanionREPL.COMMANDS
1707c67… noreply 145
1707c67… noreply 146 def test_setup_readline_no_crash(self):
1707c67… noreply 147 """Readline setup should not crash even if readline is unavailable."""
1707c67… noreply 148 repl = CompanionREPL()
1707c67… noreply 149 # Just ensure it doesn't raise
1707c67… noreply 150 repl._setup_readline()
1707c67… noreply 151
1707c67… noreply 152 def test_all_commands_in_dispatch(self):
1707c67… noreply 153 """Every command in COMMANDS should be handled by handle_input."""
1707c67… noreply 154 repl = CompanionREPL()
1707c67… noreply 155 for cmd in CompanionREPL.COMMANDS:
1707c67… noreply 156 if cmd in ("/quit", "/exit"):
1707c67… noreply 157 result = repl.handle_input(cmd)
1707c67… noreply 158 assert result == "__QUIT__"
1707c67… noreply 159 else:
1707c67… noreply 160 result = repl.handle_input(cmd)
1707c67… noreply 161 assert "Unknown command" not in result, f"{cmd} not handled"

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button