Files
hermes-webui/tests/test_issue4770_anthropic_oauth_detection.py
T

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"