import subprocess from types import SimpleNamespace import json from typer.testing import CliRunner from l4d2host.cli import app def test_help_lists_v1_commands() -> None: result = CliRunner().invoke(app, ["--help"]) assert result.exit_code == 0 for command in ["install", "initialize", "start", "stop", "delete"]: assert command in result.output def test_cli_propagates_subprocess_return_code(monkeypatch) -> None: def fail(*args, **kwargs): del args del kwargs raise subprocess.CalledProcessError(returncode=9, cmd=["x"], stderr="boom") monkeypatch.setattr("l4d2host.cli.start_instance", fail) result = CliRunner().invoke(app, ["start", "alpha"]) assert result.exit_code == 9 assert "boom" in result.stderr def test_status_command_outputs_json(monkeypatch) -> None: monkeypatch.setattr( "l4d2host.cli.get_instance_status", lambda name: SimpleNamespace(state="running", raw_active_state="active", raw_sub_state="running"), raising=False, ) result = CliRunner().invoke(app, ["status", "alpha", "--json"]) assert result.exit_code == 0 assert json.loads(result.output) == { "state": "running", "raw_active_state": "active", "raw_sub_state": "running", } def test_logs_command_streams_lines(monkeypatch) -> None: monkeypatch.setattr( "l4d2host.cli.stream_instance_logs", lambda name, *, lines, follow: iter([f"{name}:{lines}:{follow}", "ready"]), raising=False, ) result = CliRunner().invoke(app, ["logs", "alpha", "--lines", "25", "--no-follow"]) assert result.exit_code == 0 assert result.output.splitlines() == ["alpha:25:False", "ready"] def test_logs_command_propagates_subprocess_return_code(monkeypatch) -> None: def fail_logs(*args, **kwargs): del args del kwargs raise subprocess.CalledProcessError(returncode=7, cmd=["logs"], stderr="sudo denied") monkeypatch.setattr("l4d2host.cli.stream_instance_logs", fail_logs, raising=False) result = CliRunner().invoke(app, ["logs", "alpha", "--no-follow"]) assert result.exit_code == 7 assert "sudo denied" in result.stderr