Files
hermes-agent/tests/test_auth_codex_provider.py

115 lines
3.6 KiB
Python

import json
from pathlib import Path
from types import SimpleNamespace
import pytest
import yaml
from hermes_cli.auth import (
AuthError,
DEFAULT_CODEX_BASE_URL,
PROVIDER_REGISTRY,
_login_openai_codex,
login_command,
get_codex_auth_status,
get_provider_auth_state,
read_codex_auth_file,
resolve_codex_runtime_credentials,
resolve_provider,
)
def _write_codex_auth(codex_home: Path, *, access_token: str = "access", refresh_token: str = "refresh") -> Path:
codex_home.mkdir(parents=True, exist_ok=True)
auth_file = codex_home / "auth.json"
auth_file.write_text(
json.dumps(
{
"auth_mode": "oauth",
"last_refresh": "2026-02-26T00:00:00Z",
"tokens": {
"access_token": access_token,
"refresh_token": refresh_token,
},
}
)
)
return auth_file
def test_read_codex_auth_file_success(tmp_path, monkeypatch):
codex_home = tmp_path / "codex-home"
auth_file = _write_codex_auth(codex_home)
monkeypatch.setenv("CODEX_HOME", str(codex_home))
payload = read_codex_auth_file()
assert payload["auth_path"] == auth_file
assert payload["tokens"]["access_token"] == "access"
assert payload["tokens"]["refresh_token"] == "refresh"
def test_resolve_codex_runtime_credentials_missing_access_token(tmp_path, monkeypatch):
codex_home = tmp_path / "codex-home"
_write_codex_auth(codex_home, access_token="")
monkeypatch.setenv("CODEX_HOME", str(codex_home))
with pytest.raises(AuthError) as exc:
resolve_codex_runtime_credentials()
assert exc.value.code == "codex_auth_missing_access_token"
assert exc.value.relogin_required is True
def test_resolve_provider_explicit_codex_does_not_fallback(monkeypatch):
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
assert resolve_provider("openai-codex") == "openai-codex"
def test_get_codex_auth_status_not_logged_in(tmp_path, monkeypatch):
monkeypatch.setenv("CODEX_HOME", str(tmp_path / "missing-codex-home"))
status = get_codex_auth_status()
assert status["logged_in"] is False
assert "error" in status
def test_login_openai_codex_persists_provider_state(tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes-home"
codex_home = tmp_path / "codex-home"
_write_codex_auth(codex_home)
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
monkeypatch.setenv("CODEX_HOME", str(codex_home))
monkeypatch.setattr("hermes_cli.auth.shutil.which", lambda _: "/usr/local/bin/codex")
monkeypatch.setattr("hermes_cli.auth.subprocess.run", lambda *a, **k: None)
_login_openai_codex(SimpleNamespace(), PROVIDER_REGISTRY["openai-codex"])
state = get_provider_auth_state("openai-codex")
assert state is not None
assert state["source"] == "codex-auth-json"
assert state["auth_file"].endswith("auth.json")
config_path = hermes_home / "config.yaml"
config = yaml.safe_load(config_path.read_text())
assert config["model"]["provider"] == "openai-codex"
assert config["model"]["base_url"] == DEFAULT_CODEX_BASE_URL
def test_login_command_defaults_to_nous(monkeypatch):
calls = {"nous": 0, "codex": 0}
def _fake_nous(args, pconfig):
calls["nous"] += 1
def _fake_codex(args, pconfig):
calls["codex"] += 1
monkeypatch.setattr("hermes_cli.auth._login_nous", _fake_nous)
monkeypatch.setattr("hermes_cli.auth._login_openai_codex", _fake_codex)
login_command(SimpleNamespace())
assert calls["nous"] == 1
assert calls["codex"] == 0