125 lines
4.7 KiB
Python
125 lines
4.7 KiB
Python
"""Regression tests for #4770 - Anthropic OAuth env vars in fallback detection."""
|
|
|
|
import builtins
|
|
import sys
|
|
|
|
import api.config as config
|
|
|
|
|
|
def _force_env_fallback(monkeypatch):
|
|
"""Force get_available_models() into the env-based fallback branch."""
|
|
real_import = builtins.__import__
|
|
monkeypatch.delitem(sys.modules, "hermes_cli.auth", raising=False)
|
|
monkeypatch.delitem(sys.modules, "hermes_cli", raising=False)
|
|
|
|
def fake_import(name, globals=None, locals=None, fromlist=(), level=0):
|
|
if name in ("hermes_cli.models", "hermes_cli.auth"):
|
|
raise ImportError(name)
|
|
return real_import(name, globals, locals, fromlist, level)
|
|
|
|
monkeypatch.setattr(builtins, "__import__", fake_import)
|
|
|
|
|
|
def _clear_provider_env(monkeypatch):
|
|
for key in (
|
|
"ANTHROPIC_API_KEY",
|
|
"ANTHROPIC_TOKEN",
|
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
"OPENAI_API_KEY",
|
|
"OPENROUTER_API_KEY",
|
|
"GOOGLE_API_KEY",
|
|
"GEMINI_API_KEY",
|
|
"GLM_API_KEY",
|
|
"KIMI_API_KEY",
|
|
"DEEPSEEK_API_KEY",
|
|
"XIAOMI_API_KEY",
|
|
"OPENCODE_ZEN_API_KEY",
|
|
"OPENCODE_GO_API_KEY",
|
|
"OPENCODE_API_KEY",
|
|
"MINIMAX_API_KEY",
|
|
"MINIMAX_CN_API_KEY",
|
|
"XAI_API_KEY",
|
|
"MISTRAL_API_KEY",
|
|
"AWS_ACCESS_KEY_ID",
|
|
"AWS_SECRET_ACCESS_KEY",
|
|
"LM_API_KEY",
|
|
"LM_BASE_URL",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
|
|
|
|
def _run_available_models_with_cfg(monkeypatch, tmp_path, cfg):
|
|
old_cfg = dict(config.cfg)
|
|
old_mtime = config._cfg_mtime
|
|
old_models_cache_path = config._models_cache_path
|
|
old_config_path_getter = config._get_config_path
|
|
|
|
monkeypatch.setattr(config, "_models_cache_path", tmp_path / "models_cache.json")
|
|
monkeypatch.setattr(config, "_get_config_path", lambda: tmp_path / "missing-config.yaml")
|
|
monkeypatch.setattr("api.profiles.get_active_hermes_home", lambda: tmp_path, raising=False)
|
|
config.cfg.clear()
|
|
config.cfg.update(cfg)
|
|
config._cfg_mtime = 0.0
|
|
config.invalidate_models_cache()
|
|
try:
|
|
return config.get_available_models()
|
|
finally:
|
|
config.cfg.clear()
|
|
config.cfg.update(old_cfg)
|
|
config._cfg_mtime = old_mtime
|
|
monkeypatch.setattr(config, "_models_cache_path", old_models_cache_path)
|
|
monkeypatch.setattr(config, "_get_config_path", old_config_path_getter)
|
|
config.invalidate_models_cache()
|
|
|
|
|
|
def _provider_groups(result):
|
|
return {group["provider_id"]: group for group in result["groups"]}
|
|
|
|
|
|
def test_anthropic_token_env_var_surfaces_anthropic_models(monkeypatch, tmp_path):
|
|
"""ANTHROPIC_TOKEN should detect the Anthropic provider through fallback."""
|
|
_clear_provider_env(monkeypatch)
|
|
_force_env_fallback(monkeypatch)
|
|
monkeypatch.setenv("ANTHROPIC_TOKEN", "token-only-value")
|
|
|
|
result = _run_available_models_with_cfg(monkeypatch, tmp_path, {"model": {}})
|
|
groups = _provider_groups(result)
|
|
assert "anthropic" in groups, (
|
|
"ANTHROPIC_TOKEN should surface the Anthropic picker provider when the "
|
|
"hermes_cli path is unavailable."
|
|
)
|
|
assert groups["anthropic"]["provider"] == "Anthropic"
|
|
assert groups["anthropic"]["models"], "Anthropic fallback should include model entries"
|
|
|
|
|
|
def test_whitespace_only_anthropic_oauth_env_vars_do_not_surface_anthropic(monkeypatch, tmp_path):
|
|
"""A whitespace-only ANTHROPIC_TOKEN / CLAUDE_CODE_OAUTH_TOKEN must NOT
|
|
false-positive the Anthropic provider (env values are stripped before the
|
|
availability check)."""
|
|
for ws_var in ("ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"):
|
|
_clear_provider_env(monkeypatch)
|
|
_force_env_fallback(monkeypatch)
|
|
monkeypatch.setenv(ws_var, " \t ")
|
|
|
|
result = _run_available_models_with_cfg(monkeypatch, tmp_path, {"model": {}})
|
|
groups = _provider_groups(result)
|
|
assert "anthropic" not in groups, (
|
|
f"a whitespace-only {ws_var} must not surface the Anthropic provider"
|
|
)
|
|
|
|
|
|
def test_claude_code_oauth_token_env_var_surfaces_anthropic_models(monkeypatch, tmp_path):
|
|
"""CLAUDE_CODE_OAUTH_TOKEN should detect the Anthropic provider through fallback."""
|
|
_clear_provider_env(monkeypatch)
|
|
_force_env_fallback(monkeypatch)
|
|
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", "oauth-token-only-value")
|
|
|
|
result = _run_available_models_with_cfg(monkeypatch, tmp_path, {"model": {}})
|
|
groups = _provider_groups(result)
|
|
assert "anthropic" in groups, (
|
|
"CLAUDE_CODE_OAUTH_TOKEN should surface the Anthropic picker provider when "
|
|
"the hermes_cli path is unavailable."
|
|
)
|
|
assert groups["anthropic"]["provider"] == "Anthropic"
|
|
assert groups["anthropic"]["models"], "Anthropic fallback should include model entries"
|