test_empty_model_fallback.py
1 """Tests for empty model fallback — when provider is configured but model is missing.""" 2 3 from unittest.mock import MagicMock, patch 4 import pytest 5 6 7 class TestGetDefaultModelForProvider: 8 """Unit tests for hermes_cli.models.get_default_model_for_provider.""" 9 10 def test_known_provider_returns_first_model(self): 11 from hermes_cli.models import get_default_model_for_provider 12 result = get_default_model_for_provider("openai-codex") 13 # Should return first model from _PROVIDER_MODELS["openai-codex"] 14 assert result 15 assert isinstance(result, str) 16 17 def test_openrouter_returns_empty(self): 18 """OpenRouter uses dynamic model fetch, no static catalog entry.""" 19 from hermes_cli.models import get_default_model_for_provider 20 # OpenRouter is not in _PROVIDER_MODELS — it uses live fetching 21 result = get_default_model_for_provider("openrouter") 22 assert result == "" 23 24 def test_unknown_provider_returns_empty(self): 25 from hermes_cli.models import get_default_model_for_provider 26 assert get_default_model_for_provider("nonexistent-provider") == "" 27 28 def test_custom_provider_returns_empty(self): 29 """Custom provider has no model catalog — should return empty.""" 30 from hermes_cli.models import get_default_model_for_provider 31 # Custom providers don't have entries in _PROVIDER_MODELS 32 assert get_default_model_for_provider("some-random-custom") == "" 33 34 35 class TestGatewayEmptyModelFallback: 36 """Test that _resolve_session_agent_runtime fills in empty model from provider catalog.""" 37 38 def test_empty_model_filled_from_provider(self): 39 """When config has no model but provider is openai-codex, use first codex model.""" 40 from gateway.run import GatewayRunner 41 42 runner = object.__new__(GatewayRunner) 43 runner._session_model_overrides = {} 44 45 # Mock _resolve_gateway_model to return empty string 46 # Mock _resolve_runtime_agent_kwargs to return openai-codex provider 47 with patch("gateway.run._resolve_gateway_model", return_value=""), \ 48 patch("gateway.run._resolve_runtime_agent_kwargs", return_value={ 49 "provider": "openai-codex", 50 "api_key": "test-key", 51 "base_url": "https://chatgpt.com/backend-api/codex", 52 "api_mode": "codex_responses", 53 }): 54 model, kwargs = runner._resolve_session_agent_runtime() 55 56 # Model should have been filled in from provider catalog 57 assert model, "Model should not be empty when provider is known" 58 assert isinstance(model, str) 59 assert kwargs["provider"] == "openai-codex" 60 61 def test_nonempty_model_not_overridden(self): 62 """When config has a model set, don't override it.""" 63 from gateway.run import GatewayRunner 64 65 runner = object.__new__(GatewayRunner) 66 runner._session_model_overrides = {} 67 68 with patch("gateway.run._resolve_gateway_model", return_value="gpt-5.4"), \ 69 patch("gateway.run._resolve_runtime_agent_kwargs", return_value={ 70 "provider": "openai-codex", 71 "api_key": "test-key", 72 "base_url": "https://chatgpt.com/backend-api/codex", 73 "api_mode": "codex_responses", 74 }): 75 model, kwargs = runner._resolve_session_agent_runtime() 76 77 assert model == "gpt-5.4", "Explicit model should not be overridden" 78 79 def test_empty_model_no_provider_stays_empty(self): 80 """When both model and provider are empty, model stays empty.""" 81 from gateway.run import GatewayRunner 82 83 runner = object.__new__(GatewayRunner) 84 runner._session_model_overrides = {} 85 86 with patch("gateway.run._resolve_gateway_model", return_value=""), \ 87 patch("gateway.run._resolve_runtime_agent_kwargs", return_value={ 88 "provider": "", 89 "api_key": "test-key", 90 "base_url": "https://example.com", 91 "api_mode": "chat_completions", 92 }): 93 model, kwargs = runner._resolve_session_agent_runtime() 94 95 # Can't fill in a default without knowing the provider 96 assert model == "" 97 98 99 class TestResolveGatewayModel: 100 """Test _resolve_gateway_model reads model from config correctly.""" 101 102 def test_returns_default_key(self): 103 from gateway.run import _resolve_gateway_model 104 assert _resolve_gateway_model({"model": {"default": "gpt-5.4"}}) == "gpt-5.4" 105 106 def test_returns_model_key_fallback(self): 107 from gateway.run import _resolve_gateway_model 108 assert _resolve_gateway_model({"model": {"model": "gpt-5.4"}}) == "gpt-5.4" 109 110 def test_returns_empty_when_missing(self): 111 from gateway.run import _resolve_gateway_model 112 assert _resolve_gateway_model({"model": {}}) == "" 113 114 def test_returns_empty_when_no_model_section(self): 115 from gateway.run import _resolve_gateway_model 116 assert _resolve_gateway_model({}) == "" 117 118 def test_string_model_config(self): 119 from gateway.run import _resolve_gateway_model 120 assert _resolve_gateway_model({"model": "my-model"}) == "my-model"