Something went wrong. Try again.
The agent that grows with you
Something went wrong. Try again.
5.4 kB · 164 lines
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165"""Regression tests for the /model picker's credential-discovery paths.
Covers: - Normal path (tokens already in Hermes auth store) - Claude Code fallback (tokens only in ~/.claude/.credentials.json) - Negative case (no credentials anywhere)
Note: auto-import from ~/.codex/auth.json was removed in #12360 — Hermesnow owns its own openai-codex auth state, and users explicitly adoptexisting Codex CLI tokens via `hermes auth openai-codex`. The old"Codex CLI shared file" discovery tests were removed with that change."""
import base64import jsonimport osimport sysimport timefrom pathlib import Pathfrom unittest.mock import patch
import pytest
def _make_fake_jwt(expiry_offset: int = 3600) -> str: """Build a fake JWT with a future expiry.""" header = base64.urlsafe_b64encode(b'{"alg":"RS256"}').rstrip(b"=").decode() exp = int(time.time()) + expiry_offset payload_bytes = json.dumps({"exp": exp, "sub": "test"}).encode() payload = base64.urlsafe_b64encode(payload_bytes).rstrip(b"=").decode() return f"{header}.{payload}.fakesig"
@pytest.fixture()def hermes_auth_only_env(tmp_path, monkeypatch): """Tokens already in Hermes auth store (no Codex CLI needed).""" hermes_home = tmp_path / ".hermes" hermes_home.mkdir()
monkeypatch.setenv("HERMES_HOME", str(hermes_home)) # Point CODEX_HOME to nonexistent dir to prove it's not needed monkeypatch.setenv("CODEX_HOME", str(tmp_path / "no_codex"))
(hermes_home / "auth.json").write_text(json.dumps({ "version": 2, "providers": { "openai-codex": { "tokens": { "access_token": _make_fake_jwt(), "refresh_token": "fake-refresh", }, "last_refresh": "2026-04-12T00:00:00Z", } }, }))
for var in [ "OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "NOUS_API_KEY", "DEEPSEEK_API_KEY", ]: monkeypatch.delenv(var, raising=False)
return hermes_home
def test_normal_path_still_works(hermes_auth_only_env): """openai-codex appears when tokens are already in Hermes auth store.""" from hermes_cli.model_switch import list_authenticated_providers
providers = list_authenticated_providers( current_provider="openai-codex", max_models=10, ) slugs = [p["slug"] for p in providers] assert "openai-codex" in slugs
@pytest.fixture()def claude_code_only_env(tmp_path, monkeypatch): """Set up an environment where Anthropic credentials only exist in ~/.claude/.credentials.json (Claude Code) — not in env vars or Hermes auth store.""" hermes_home = tmp_path / ".hermes" hermes_home.mkdir()
monkeypatch.setenv("HERMES_HOME", str(hermes_home)) # No Codex CLI monkeypatch.setenv("CODEX_HOME", str(tmp_path / "no_codex"))
(hermes_home / "auth.json").write_text( json.dumps({"version": 2, "providers": {}}) )
# Claude Code credentials in the correct format claude_dir = tmp_path / ".claude" claude_dir.mkdir() (claude_dir / ".credentials.json").write_text(json.dumps({ "claudeAiOauth": { "accessToken": _make_fake_jwt(), "refreshToken": "fake-refresh", "expiresAt": int(time.time() * 1000) + 3_600_000, } }))
# Patch Path.home() so the adapter finds the file monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
for var in [ "OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "NOUS_API_KEY", "DEEPSEEK_API_KEY", ]: monkeypatch.delenv(var, raising=False)
return hermes_home
def test_claude_code_file_detected_by_model_picker(claude_code_only_env): """anthropic should appear when credentials only exist in ~/.claude/.credentials.json.""" from hermes_cli.model_switch import list_authenticated_providers
providers = list_authenticated_providers( current_provider="anthropic", max_models=10, ) slugs = [p["slug"] for p in providers] assert "anthropic" in slugs, ( f"anthropic not found in /model picker providers: {slugs}" )
anthropic = next(p for p in providers if p["slug"] == "anthropic") assert anthropic["is_current"] is True assert anthropic["total_models"] > 0
def test_no_codex_when_no_credentials(tmp_path, monkeypatch): """openai-codex should NOT appear when no credentials exist anywhere.""" hermes_home = tmp_path / ".hermes" hermes_home.mkdir()
monkeypatch.setenv("HERMES_HOME", str(hermes_home)) monkeypatch.setenv("CODEX_HOME", str(tmp_path / "no_codex"))
(hermes_home / "auth.json").write_text( json.dumps({"version": 2, "providers": {}}) )
for var in [ "OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "NOUS_API_KEY", "DEEPSEEK_API_KEY", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GEMINI_API_KEY", ]: monkeypatch.delenv(var, raising=False)
from hermes_cli.model_switch import list_authenticated_providers
providers = list_authenticated_providers( current_provider="openrouter", max_models=10, ) slugs = [p["slug"] for p in providers] assert "openai-codex" not in slugs, ( "openai-codex should not appear without any credentials" )