99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""Anthropic-specific auth command tests moved from tests/hermes_cli/test_auth_commands.py."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import json
|
|
|
|
import pytest
|
|
|
|
|
|
def _write_auth_store(tmp_path, payload: dict) -> None:
|
|
hermes_home = tmp_path / "hermes"
|
|
hermes_home.mkdir(parents=True, exist_ok=True)
|
|
(hermes_home / "auth.json").write_text(json.dumps(payload, indent=2))
|
|
|
|
|
|
def _jwt_with_email(email: str) -> str:
|
|
header = base64.urlsafe_b64encode(b'{"alg":"RS256","typ":"JWT"}').rstrip(b"=").decode()
|
|
payload = base64.urlsafe_b64encode(
|
|
json.dumps({"email": email}).encode()
|
|
).rstrip(b"=").decode()
|
|
return f"{header}.{payload}.signature"
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_provider_env(monkeypatch):
|
|
for key in (
|
|
"OPENROUTER_API_KEY",
|
|
"OPENAI_API_KEY",
|
|
"ANTHROPIC_API_KEY",
|
|
"ANTHROPIC_TOKEN",
|
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
):
|
|
monkeypatch.delenv(key, raising=False)
|
|
|
|
|
|
def test_auth_add_anthropic_oauth_persists_pool_entry(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
|
|
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
|
|
monkeypatch.delenv("ANTHROPIC_TOKEN", raising=False)
|
|
monkeypatch.delenv("CLAUDE_CODE_OAUTH_TOKEN", raising=False)
|
|
_write_auth_store(tmp_path, {"version": 1, "providers": {}})
|
|
token = _jwt_with_email("claude@example.com")
|
|
monkeypatch.setattr(
|
|
"hermes_agent_anthropic.adapter.run_hermes_oauth_login_pure",
|
|
lambda: {
|
|
"access_token": token,
|
|
"refresh_token": "refresh-token",
|
|
"expires_at_ms": 1711234567000,
|
|
},
|
|
)
|
|
|
|
from hermes_cli.auth_commands import auth_add_command
|
|
|
|
class _Args:
|
|
provider = "anthropic"
|
|
auth_type = "oauth"
|
|
api_key = None
|
|
label = None
|
|
|
|
auth_add_command(_Args())
|
|
|
|
payload = json.loads((tmp_path / "hermes" / "auth.json").read_text())
|
|
entries = payload["credential_pool"]["anthropic"]
|
|
entry = next(item for item in entries if item["source"] == "manual:hermes_pkce")
|
|
assert entry["label"] == "claude@example.com"
|
|
assert entry["source"] == "manual:hermes_pkce"
|
|
assert entry["refresh_token"] == "refresh-token"
|
|
assert entry["expires_at_ms"] == 1711234567000
|
|
|
|
|
|
def test_seed_from_singletons_respects_hermes_pkce_suppression(tmp_path, monkeypatch):
|
|
"""anthropic hermes_pkce must not re-seed from ~/.hermes/.anthropic_oauth.json when suppressed."""
|
|
hermes_home = tmp_path / "hermes"
|
|
hermes_home.mkdir(parents=True, exist_ok=True)
|
|
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
|
|
|
import yaml
|
|
(hermes_home / "config.yaml").write_text(yaml.dump({"model": {"provider": "anthropic", "model": "claude"}}))
|
|
(hermes_home / "auth.json").write_text(json.dumps({
|
|
"version": 1,
|
|
"providers": {},
|
|
"suppressed_sources": {"anthropic": ["hermes_pkce"]},
|
|
}))
|
|
|
|
# Stub the readers so only hermes_pkce is "available"; claude_code returns None
|
|
import hermes_agent_anthropic as aa
|
|
monkeypatch.setattr(aa, "read_hermes_oauth_credentials", lambda: {
|
|
"accessToken": "tok", "refreshToken": "r", "expiresAt": 9999999999000,
|
|
})
|
|
monkeypatch.setattr(aa, "read_claude_code_credentials", lambda: None)
|
|
|
|
from agent.credential_pool import _seed_from_singletons
|
|
entries = []
|
|
changed, active = _seed_from_singletons("anthropic", entries)
|
|
# hermes_pkce suppressed, claude_code returns None → nothing should be seeded
|
|
assert entries == []
|
|
assert "hermes_pkce" not in active
|