"""Tests for `headroom wrap vibe` command.""" from __future__ import annotations import json from pathlib import Path from typing import Any from unittest.mock import patch import pytest from click.testing import CliRunner from headroom.cli import wrap as wrap_mod from headroom.cli.main import main @pytest.fixture def runner() -> CliRunner: return CliRunner() def test_wrap_vibe_launch( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Vibe launches with correct configuration.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke( main, ["wrap", "vibe", "--port", "9000", "--", "--prompt", "test"] ) assert result.exit_code == 0, result.output env = captured["env"] assert isinstance(env, dict) assert captured["tool_label"] == "VIBE" assert captured["agent_type"] == "vibe" assert captured["args"] == ("--prompt", "test") def test_wrap_vibe_with_project_name( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Project name is encoded in the URL when running from a project directory.""" project_dir = tmp_path / "my-project" project_dir.mkdir() monkeypatch.chdir(project_dir) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): result = runner.invoke(main, ["wrap", "vibe", "--port", "7000"]) assert result.exit_code == 0, result.output env = captured["env"] assert isinstance(env, dict) providers: list[dict[str, Any]] = json.loads(env["VIBE_PROVIDERS"]) assert providers[0]["api_base"] == "http://127.0.0.1:7000/p/my-project/v1" def test_wrap_vibe_not_found( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Error message when vibe binary is not found.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) with patch.object(wrap_mod.shutil, "which", return_value=None): result = runner.invoke(main, ["wrap", "vibe"]) assert result.exit_code == 1 assert "Error: 'vibe' not found in PATH" in result.output assert "Install Mistral Vibe: https://github.com/mistralai/mistral-vibe" in result.output def test_wrap_vibe_custom_port( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Custom --port is passed to _launch_tool and appears in VIBE_PROVIDERS.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe", "--port", "9999"]) assert result.exit_code == 0, result.output assert captured["port"] == 9999 env = captured["env"] providers: list[dict[str, Any]] = json.loads(env["VIBE_PROVIDERS"]) assert providers[0]["api_base"] == "http://127.0.0.1:9999/v1" def test_wrap_vibe_no_proxy( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """--no-proxy flag prevents proxy startup.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe", "--no-proxy"]) assert result.exit_code == 0, result.output assert captured["no_proxy"] is True def test_wrap_vibe_code_graph( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """--code-graph flag is passed to _launch_tool.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe", "--code-graph"]) assert result.exit_code == 0, result.output assert captured["code_graph"] is True def test_wrap_vibe_learn_memory( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """--learn and --memory flags are passed to _launch_tool.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe", "--learn", "--memory"]) assert result.exit_code == 0, result.output assert captured["learn"] is True assert captured["memory"] is True def test_wrap_vibe_verbose( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """--verbose flag is accepted by vibe command.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe", "--verbose"]) assert result.exit_code == 0, result.output def test_wrap_vibe_providers_json_structure( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """VIBE_PROVIDERS env var has correct JSON structure.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): result = runner.invoke(main, ["wrap", "vibe"]) assert result.exit_code == 0, result.output env = captured["env"] assert "VIBE_PROVIDERS" in env providers: list[dict[str, Any]] = json.loads(env["VIBE_PROVIDERS"]) assert isinstance(providers, list) assert len(providers) == 1 assert providers[0]["name"] == "mistral" assert providers[0]["api_key_env_var"] == "MISTRAL_API_KEY" assert providers[0]["backend"] == "mistral" assert "api_base" in providers[0] assert providers[0]["browser_auth_base_url"] == "https://console.mistral.ai" assert providers[0]["browser_auth_api_base_url"] == "https://console.mistral.ai/api" def test_wrap_vibe_no_context_tool( runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """--no-context-tool and --no-rtk flags are accepted and not passed to vibe.""" monkeypatch.chdir(tmp_path) monkeypatch.delenv("HEADROOM_CONTEXT_TOOL", raising=False) captured: dict[str, Any] = {} def fake_launch_tool(**kwargs: Any) -> None: # noqa: ANN003 captured.update(kwargs) with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): # Test --no-context-tool result = runner.invoke(main, ["wrap", "vibe", "--no-context-tool", "--", "test"]) assert result.exit_code == 0, result.output assert captured["args"] == ("test",) assert "--no-context-tool" not in captured["args"] captured.clear() with patch.object(wrap_mod.shutil, "which", return_value="vibe"): with patch.object(wrap_mod, "_launch_tool", side_effect=fake_launch_tool): with patch.object(wrap_mod, "_project_name_from_cwd", return_value=None): # Test --no-rtk result = runner.invoke(main, ["wrap", "vibe", "--no-rtk", "--", "test"]) assert result.exit_code == 0, result.output assert captured["args"] == ("test",) assert "--no-rtk" not in captured["args"]