test_status_model_provider.py
1 """Tests for hermes_cli.status model/provider display.""" 2 3 from types import SimpleNamespace 4 5 from hermes_cli.nous_subscription import NousFeatureState, NousSubscriptionFeatures 6 7 8 def _patch_common_status_deps(monkeypatch, status_mod, tmp_path, *, openai_base_url=""): 9 import hermes_cli.auth as auth_mod 10 11 monkeypatch.setattr(status_mod, "get_env_path", lambda: tmp_path / ".env", raising=False) 12 monkeypatch.setattr(status_mod, "get_hermes_home", lambda: tmp_path, raising=False) 13 14 def _get_env_value(name: str): 15 if name == "OPENAI_BASE_URL": 16 return openai_base_url 17 return "" 18 19 monkeypatch.setattr(status_mod, "get_env_value", _get_env_value, raising=False) 20 monkeypatch.setattr(auth_mod, "get_nous_auth_status", lambda: {}, raising=False) 21 monkeypatch.setattr(auth_mod, "get_codex_auth_status", lambda: {}, raising=False) 22 monkeypatch.setattr( 23 status_mod.subprocess, 24 "run", 25 lambda *args, **kwargs: SimpleNamespace(stdout="inactive\n", returncode=3), 26 ) 27 28 29 def test_show_status_displays_configured_dict_model_and_provider_label(monkeypatch, capsys, tmp_path): 30 from hermes_cli import status as status_mod 31 32 _patch_common_status_deps(monkeypatch, status_mod, tmp_path) 33 monkeypatch.setattr( 34 status_mod, 35 "load_config", 36 lambda: {"model": {"default": "anthropic/claude-sonnet-4", "provider": "anthropic"}}, 37 raising=False, 38 ) 39 monkeypatch.setattr(status_mod, "resolve_requested_provider", lambda requested=None: "anthropic", raising=False) 40 monkeypatch.setattr(status_mod, "resolve_provider", lambda requested=None, **kwargs: "anthropic", raising=False) 41 monkeypatch.setattr(status_mod, "provider_label", lambda provider: "Anthropic", raising=False) 42 43 status_mod.show_status(SimpleNamespace(all=False, deep=False)) 44 45 out = capsys.readouterr().out 46 assert "Model: anthropic/claude-sonnet-4" in out 47 assert "Provider: Anthropic" in out 48 49 50 def test_show_status_displays_legacy_string_model_and_custom_endpoint(monkeypatch, capsys, tmp_path): 51 from hermes_cli import status as status_mod 52 53 _patch_common_status_deps(monkeypatch, status_mod, tmp_path, openai_base_url="http://localhost:8080/v1") 54 monkeypatch.setattr(status_mod, "load_config", lambda: {"model": "qwen3:latest"}, raising=False) 55 monkeypatch.setattr(status_mod, "resolve_requested_provider", lambda requested=None: "auto", raising=False) 56 monkeypatch.setattr(status_mod, "resolve_provider", lambda requested=None, **kwargs: "openrouter", raising=False) 57 monkeypatch.setattr(status_mod, "provider_label", lambda provider: "Custom endpoint" if provider == "custom" else provider, raising=False) 58 59 status_mod.show_status(SimpleNamespace(all=False, deep=False)) 60 61 out = capsys.readouterr().out 62 assert "Model: qwen3:latest" in out 63 assert "Provider: Custom endpoint" in out 64 65 66 def test_show_status_reports_managed_nous_features(monkeypatch, capsys, tmp_path): 67 monkeypatch.setattr("hermes_cli.status.managed_nous_tools_enabled", lambda: True) 68 from hermes_cli import status as status_mod 69 70 _patch_common_status_deps(monkeypatch, status_mod, tmp_path) 71 monkeypatch.setattr( 72 status_mod, 73 "load_config", 74 lambda: {"model": {"default": "claude-opus-4-6", "provider": "nous"}}, 75 raising=False, 76 ) 77 monkeypatch.setattr(status_mod, "resolve_requested_provider", lambda requested=None: "nous", raising=False) 78 monkeypatch.setattr(status_mod, "resolve_provider", lambda requested=None, **kwargs: "nous", raising=False) 79 monkeypatch.setattr(status_mod, "provider_label", lambda provider: "Nous Portal", raising=False) 80 monkeypatch.setattr( 81 status_mod, 82 "get_nous_subscription_features", 83 lambda config: NousSubscriptionFeatures( 84 subscribed=True, 85 nous_auth_present=True, 86 provider_is_nous=True, 87 features={ 88 "web": NousFeatureState("web", "Web tools", True, True, True, True, False, True, "firecrawl"), 89 "image_gen": NousFeatureState("image_gen", "Image generation", True, True, True, True, False, True, "Nous Subscription"), 90 "tts": NousFeatureState("tts", "OpenAI TTS", True, True, True, True, False, True, "OpenAI TTS"), 91 "browser": NousFeatureState("browser", "Browser automation", True, True, True, True, False, True, "Browser Use"), 92 "modal": NousFeatureState("modal", "Modal execution", False, True, False, False, False, True, "local"), 93 }, 94 ), 95 raising=False, 96 ) 97 98 status_mod.show_status(SimpleNamespace(all=False, deep=False)) 99 100 out = capsys.readouterr().out 101 assert "Nous Tool Gateway" in out 102 assert "Browser automation" in out 103 assert "active via Nous subscription" in out 104 105 106 def test_show_status_hides_nous_subscription_section_when_feature_flag_is_off(monkeypatch, capsys, tmp_path): 107 monkeypatch.setattr("hermes_cli.status.managed_nous_tools_enabled", lambda: False) 108 from hermes_cli import status as status_mod 109 110 _patch_common_status_deps(monkeypatch, status_mod, tmp_path) 111 monkeypatch.setattr( 112 status_mod, 113 "load_config", 114 lambda: {"model": {"default": "claude-opus-4-6", "provider": "nous"}}, 115 raising=False, 116 ) 117 monkeypatch.setattr(status_mod, "resolve_requested_provider", lambda requested=None: "nous", raising=False) 118 monkeypatch.setattr(status_mod, "resolve_provider", lambda requested=None, **kwargs: "nous", raising=False) 119 monkeypatch.setattr(status_mod, "provider_label", lambda provider: "Nous Portal", raising=False) 120 121 status_mod.show_status(SimpleNamespace(all=False, deep=False)) 122 123 out = capsys.readouterr().out 124 assert "Nous Tool Gateway" not in out 125 126 127 def test_show_status_reports_empty_lmstudio_listing_as_reachable(monkeypatch, capsys, tmp_path): 128 from hermes_cli import status as status_mod 129 130 _patch_common_status_deps(monkeypatch, status_mod, tmp_path) 131 monkeypatch.setattr( 132 status_mod, 133 "load_config", 134 lambda: { 135 "model": { 136 "default": "qwen/qwen3-coder-30b", 137 "provider": "lmstudio", 138 "base_url": "http://127.0.0.1:1234/v1", 139 } 140 }, 141 raising=False, 142 ) 143 monkeypatch.setattr(status_mod, "resolve_requested_provider", lambda requested=None: "lmstudio", raising=False) 144 monkeypatch.setattr(status_mod, "resolve_provider", lambda requested=None, **kwargs: "lmstudio", raising=False) 145 monkeypatch.setattr(status_mod, "provider_label", lambda provider: "LM Studio", raising=False) 146 monkeypatch.setattr( 147 "hermes_cli.models.probe_lmstudio_models", 148 lambda api_key=None, base_url=None, timeout=5.0: [], 149 ) 150 151 status_mod.show_status(SimpleNamespace(all=False, deep=False)) 152 153 out = capsys.readouterr().out 154 assert "LM Studio" in out 155 assert "reachable (0 model(s)) at http://127.0.0.1:1234/v1" in out