diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index b58e6a2e..beacfbd0 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -112,9 +112,14 @@ class TestDefaultContextLengths: def test_gpt4_models_128k(self): for key, value in DEFAULT_CONTEXT_LENGTHS.items(): - if "gpt-4" in key: + if "gpt-4" in key and "gpt-4.1" not in key: assert value == 128000, f"{key} should be 128000" + def test_gpt41_models_1m(self): + for key, value in DEFAULT_CONTEXT_LENGTHS.items(): + if "gpt-4.1" in key: + assert value == 1047576, f"{key} should be 1047576" + def test_gemini_models_1m(self): for key, value in DEFAULT_CONTEXT_LENGTHS.items(): if "gemini" in key: diff --git a/tests/hermes_cli/test_setup.py b/tests/hermes_cli/test_setup.py index e0abc639..bc19e7bb 100644 --- a/tests/hermes_cli/test_setup.py +++ b/tests/hermes_cli/test_setup.py @@ -5,6 +5,13 @@ from hermes_cli.config import load_config, save_config from hermes_cli.setup import setup_model_provider +def _maybe_keep_current_tts(question, choices): + if question != "Select TTS provider:": + return None + assert choices[-1].startswith("Keep current (") + return len(choices) - 1 + + def _clear_provider_env(monkeypatch): for key in ( "NOUS_API_KEY", @@ -25,16 +32,22 @@ def test_nous_oauth_setup_keeps_current_model_when_syncing_disk_provider( config = load_config() - # Provider selection always comes first. Depending on available vision - # backends, setup may either skip the optional vision step or prompt for - # it before the default-model choice. Provide enough selections for both - # paths while still ending on "keep current model". - prompt_choices = iter([0, 2, 2]) - monkeypatch.setattr( - "hermes_cli.setup.prompt_choice", - lambda *args, **kwargs: next(prompt_choices), - ) + def fake_prompt_choice(question, choices, default=0): + if question == "Select your inference provider:": + return 0 + if question == "Configure vision:": + return len(choices) - 1 + if question == "Select default model:": + assert choices[-1] == "Keep current (anthropic/claude-opus-4.6)" + return len(choices) - 1 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") + + monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) monkeypatch.setattr("hermes_cli.setup.prompt", lambda *args, **kwargs: "") + monkeypatch.setattr("hermes_cli.auth.detect_external_credentials", lambda: []) def _fake_login_nous(*args, **kwargs): auth_path = tmp_path / "auth.json" @@ -74,20 +87,29 @@ def test_custom_setup_clears_active_oauth_provider(tmp_path, monkeypatch): config = load_config() - monkeypatch.setattr("hermes_cli.setup.prompt_choice", lambda *args, **kwargs: 3) + def fake_prompt_choice(question, choices, default=0): + if question == "Select your inference provider:": + return 3 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") + + monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) prompt_values = iter( [ "https://custom.example/v1", "custom-api-key", "custom/model", - "", ] ) monkeypatch.setattr( "hermes_cli.setup.prompt", lambda *args, **kwargs: next(prompt_values), ) + monkeypatch.setattr("hermes_cli.setup.prompt_yes_no", lambda *args, **kwargs: False) + monkeypatch.setattr("hermes_cli.auth.detect_external_credentials", lambda: []) setup_model_provider(config) save_config(config) @@ -109,11 +131,17 @@ def test_codex_setup_uses_runtime_access_token_for_live_model_list(tmp_path, mon config = load_config() - prompt_choices = iter([1, 0]) - monkeypatch.setattr( - "hermes_cli.setup.prompt_choice", - lambda *args, **kwargs: next(prompt_choices), - ) + def fake_prompt_choice(question, choices, default=0): + if question == "Select your inference provider:": + return 1 + if question == "Select default model:": + return 0 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") + + monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) monkeypatch.setattr("hermes_cli.setup.prompt", lambda *args, **kwargs: "") monkeypatch.setattr("hermes_cli.auth.detect_external_credentials", lambda: []) monkeypatch.setattr("hermes_cli.auth._login_openai_codex", lambda *args, **kwargs: None) diff --git a/tests/hermes_cli/test_setup_model_provider.py b/tests/hermes_cli/test_setup_model_provider.py index 2ef01252..671bb9ba 100644 --- a/tests/hermes_cli/test_setup_model_provider.py +++ b/tests/hermes_cli/test_setup_model_provider.py @@ -6,6 +6,13 @@ from hermes_cli.config import load_config, save_config, save_env_value from hermes_cli.setup import _print_setup_summary, setup_model_provider +def _maybe_keep_current_tts(question, choices): + if question != "Select TTS provider:": + return None + assert choices[-1].startswith("Keep current (") + return len(choices) - 1 + + def _read_env(home): env_path = home / ".env" data = {} @@ -50,13 +57,13 @@ def test_setup_keep_current_custom_from_config_does_not_fall_through(tmp_path, m } save_config(config) - calls = {"count": 0} - def fake_prompt_choice(question, choices, default=0): - calls["count"] += 1 - if calls["count"] == 1: + if question == "Select your inference provider:": assert choices[-1] == "Keep current (Custom: https://example.invalid/v1)" return len(choices) - 1 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx raise AssertionError("Model menu should not appear for keep-current custom") monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) @@ -72,7 +79,6 @@ def test_setup_keep_current_custom_from_config_does_not_fall_through(tmp_path, m assert reloaded["model"]["provider"] == "custom" assert reloaded["model"]["default"] == "custom/model" assert reloaded["model"]["base_url"] == "https://example.invalid/v1" - assert calls["count"] == 1 def test_setup_custom_endpoint_saves_working_v1_base_url(tmp_path, monkeypatch): @@ -86,6 +92,9 @@ def test_setup_custom_endpoint_saves_working_v1_base_url(tmp_path, monkeypatch): return 3 # Custom endpoint if question == "Configure vision:": return len(choices) - 1 # Skip + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx raise AssertionError(f"Unexpected prompt_choice call: {question}") def fake_prompt(message, current=None, **kwargs): @@ -140,22 +149,23 @@ def test_setup_keep_current_config_provider_uses_provider_specific_model_menu(tm save_config(config) captured = {"provider_choices": None, "model_choices": None} - calls = {"count": 0} def fake_prompt_choice(question, choices, default=0): - calls["count"] += 1 - if calls["count"] == 1: + if question == "Select your inference provider:": captured["provider_choices"] = list(choices) assert choices[-1] == "Keep current (Anthropic)" return len(choices) - 1 - if calls["count"] == 2: + if question == "Configure vision:": assert question == "Configure vision:" assert choices[-1] == "Skip for now" return len(choices) - 1 - if calls["count"] == 3: + if question == "Select default model:": captured["model_choices"] = list(choices) return len(choices) - 1 # keep current model - raise AssertionError("Unexpected extra prompt_choice call") + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) monkeypatch.setattr("hermes_cli.setup.prompt", lambda *args, **kwargs: "") @@ -172,7 +182,6 @@ def test_setup_keep_current_config_provider_uses_provider_specific_model_menu(tm assert captured["model_choices"] is not None assert captured["model_choices"][0] == "claude-opus-4-6" assert "anthropic/claude-opus-4.6 (recommended)" not in captured["model_choices"] - assert calls["count"] == 3 def test_setup_keep_current_anthropic_can_configure_openai_vision_default(tmp_path, monkeypatch): @@ -186,14 +195,24 @@ def test_setup_keep_current_anthropic_can_configure_openai_vision_default(tmp_pa } save_config(config) - picks = iter([ - 10, # keep current provider (shifted +1 by kilocode insertion) - 1, # configure vision with OpenAI - 5, # use default gpt-4o-mini vision model - 4, # keep current Anthropic model - ]) + def fake_prompt_choice(question, choices, default=0): + if question == "Select your inference provider:": + assert choices[-1] == "Keep current (Anthropic)" + return len(choices) - 1 + if question == "Configure vision:": + return 1 + if question == "Select vision model:": + assert choices[-1] == "Use default (gpt-4o-mini)" + return len(choices) - 1 + if question == "Select default model:": + assert choices[-1] == "Keep current (claude-opus-4-6)" + return len(choices) - 1 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") - monkeypatch.setattr("hermes_cli.setup.prompt_choice", lambda *args, **kwargs: next(picks)) + monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) monkeypatch.setattr( "hermes_cli.setup.prompt", lambda message, *args, **kwargs: "sk-openai" if "OpenAI API key" in message else "", @@ -229,8 +248,17 @@ def test_setup_switch_custom_to_codex_clears_custom_endpoint_and_updates_config( } save_config(config) - picks = iter([1, 0]) - monkeypatch.setattr("hermes_cli.setup.prompt_choice", lambda *args, **kwargs: next(picks)) + def fake_prompt_choice(question, choices, default=0): + if question == "Select your inference provider:": + return 1 + if question == "Select default model:": + return 0 + tts_idx = _maybe_keep_current_tts(question, choices) + if tts_idx is not None: + return tts_idx + raise AssertionError(f"Unexpected prompt_choice call: {question}") + + monkeypatch.setattr("hermes_cli.setup.prompt_choice", fake_prompt_choice) monkeypatch.setattr("hermes_cli.setup.prompt", lambda *args, **kwargs: "") monkeypatch.setattr("hermes_cli.setup.prompt_yes_no", lambda *args, **kwargs: False) monkeypatch.setattr("hermes_cli.auth.get_active_provider", lambda: None) diff --git a/tests/honcho_integration/test_client.py b/tests/honcho_integration/test_client.py index d779d9a6..b1ae29c5 100644 --- a/tests/honcho_integration/test_client.py +++ b/tests/honcho_integration/test_client.py @@ -63,11 +63,13 @@ class TestFromEnv: class TestFromGlobalConfig: def test_missing_config_falls_back_to_env(self, tmp_path): - config = HonchoClientConfig.from_global_config( - config_path=tmp_path / "nonexistent.json" - ) + with patch.dict(os.environ, {}, clear=True): + config = HonchoClientConfig.from_global_config( + config_path=tmp_path / "nonexistent.json" + ) # Should fall back to from_env - assert config.enabled is True or config.api_key is None # depends on env + assert config.enabled is False + assert config.api_key is None def test_reads_full_config(self, tmp_path): config_file = tmp_path / "config.json" diff --git a/tests/test_api_key_providers.py b/tests/test_api_key_providers.py index deb55734..98f27d10 100644 --- a/tests/test_api_key_providers.py +++ b/tests/test_api_key_providers.py @@ -98,11 +98,14 @@ class TestProviderRegistry: # ============================================================================= PROVIDER_ENV_VARS = ( - "OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", + "OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "ANTHROPIC_TOKEN", + "CLAUDE_CODE_OAUTH_TOKEN", "GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY", "KIMI_API_KEY", "KIMI_BASE_URL", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "AI_GATEWAY_API_KEY", "AI_GATEWAY_BASE_URL", "KILOCODE_API_KEY", "KILOCODE_BASE_URL", + "DASHSCOPE_API_KEY", "OPENCODE_ZEN_API_KEY", "OPENCODE_GO_API_KEY", + "NOUS_API_KEY", "OPENAI_BASE_URL", ) @@ -111,6 +114,7 @@ PROVIDER_ENV_VARS = ( def _clear_provider_env(monkeypatch): for key in PROVIDER_ENV_VARS: monkeypatch.delenv(key, raising=False) + monkeypatch.setattr("hermes_cli.auth._load_auth_store", lambda: {}) class TestResolveProvider: diff --git a/tests/tools/test_mcp_tool.py b/tests/tools/test_mcp_tool.py index 3796d8ce..9c49bd2c 100644 --- a/tests/tools/test_mcp_tool.py +++ b/tests/tools/test_mcp_tool.py @@ -2596,17 +2596,19 @@ class TestMCPSelectiveToolLoading: async def run(): with patch("tools.mcp_tool._connect_server", side_effect=fake_connect), \ + patch.dict("tools.mcp_tool._servers", {}, clear=True), \ patch("tools.registry.registry", mock_registry), \ patch("toolsets.create_custom_toolset"): - return await _discover_and_register_server( + registered = await _discover_and_register_server( "ink_existing", {"url": "https://mcp.example.com", "tools": {"include": ["create_service"]}}, ) + return registered, _existing_tool_names() try: - registered = asyncio.run(run()) + registered, existing = asyncio.run(run()) assert registered == ["mcp_ink_existing_create_service"] - assert _existing_tool_names() == ["mcp_ink_existing_create_service"] + assert existing == ["mcp_ink_existing_create_service"] finally: _servers.pop("ink_existing", None)