mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-28 11:32:22 +00:00
test(anthropic-oauth): cover login token-endpoint host + fallback
Add two regression tests for the salvaged #48706 fix: - login token exchange targets platform.claude.com first - falls back to console.anthropic.com when the new host is unreachable Also map the salvaged contributor's noreply email in release.py AUTHOR_MAP (CI author-map gate).
This commit is contained in:
parent
2ee6449fe5
commit
ba50787180
2 changed files with 106 additions and 0 deletions
|
|
@ -47,6 +47,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
|
|||
AUTHOR_MAP = {
|
||||
"145739220+wgu9@users.noreply.github.com": "wgu9", # PR #51468 salvage (WSL/no-systemd orphan gateway tracking, #51325)
|
||||
"165020384+uperLu@users.noreply.github.com": "uperLu", # PR #50958 salvage (rename plugins/cron → plugins/cron_providers; #50872)
|
||||
"277269729+yusekiotacode@users.noreply.github.com": "yusekiotacode", # PR #48706 salvage (anthropic OAuth login token endpoint → platform.claude.com; #45250/#49821)
|
||||
"minz0721@outlook.com": "s010mn", # PR #29221 salvage (ollama-cloud reasoning_effort xhigh→max)
|
||||
"jeevesassistant00@gmail.com": "jeeves-assistant", # PR #50771 (computer-use CuaDriver vision capture routing)
|
||||
"21178861+ScotterMonk@users.noreply.github.com": "ScotterMonk", # PR #50145 salvage (cron output truncation: adapter-aware chunking, #50126)
|
||||
|
|
|
|||
|
|
@ -148,6 +148,111 @@ def test_authorization_url_state_is_not_pkce_verifier(monkeypatch, tmp_path):
|
|||
)
|
||||
|
||||
|
||||
def test_login_token_exchange_uses_platform_claude_host(monkeypatch, tmp_path):
|
||||
"""The login token exchange must hit ``platform.claude.com`` first.
|
||||
|
||||
Anthropic migrated the OAuth token endpoint to ``platform.claude.com``;
|
||||
``console.anthropic.com`` now 404s, so a hardcoded console host makes a
|
||||
fresh login impossible (issue #45250 / #49821). The refresh path already
|
||||
iterates the new host first — the login path must do the same.
|
||||
"""
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
|
||||
captured_token: Dict[str, Any] = {}
|
||||
captured_url: Dict[str, str] = {}
|
||||
_patch_oauth_flow(
|
||||
monkeypatch,
|
||||
callback_code="placeholder",
|
||||
capture_token_request=captured_token,
|
||||
capture_auth_url=captured_url,
|
||||
)
|
||||
|
||||
import builtins
|
||||
|
||||
def fake_input(*_a, **_kw):
|
||||
qs = parse_qs(urlparse(captured_url.get("url", "")).query)
|
||||
state = qs.get("state", [""])[0]
|
||||
return f"auth-code#{state}"
|
||||
|
||||
monkeypatch.setattr(builtins, "input", fake_input)
|
||||
|
||||
from agent.anthropic_adapter import run_hermes_oauth_login_pure
|
||||
|
||||
result = run_hermes_oauth_login_pure()
|
||||
|
||||
assert result is not None, "login should succeed against the live host"
|
||||
assert captured_token["url"] == "https://platform.claude.com/v1/oauth/token", (
|
||||
"login token exchange must target platform.claude.com first, not the "
|
||||
"dead console.anthropic.com host (regression of #45250 / #49821)"
|
||||
)
|
||||
|
||||
|
||||
def test_login_token_exchange_falls_back_to_console_host(monkeypatch, tmp_path):
|
||||
"""If ``platform.claude.com`` is unreachable, the login path must fall back
|
||||
to the legacy ``console.anthropic.com`` host — mirroring the refresh path's
|
||||
fallback list — rather than failing outright.
|
||||
"""
|
||||
import urllib.request
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
|
||||
captured_url: Dict[str, str] = {}
|
||||
_patch_oauth_flow(
|
||||
monkeypatch,
|
||||
callback_code="placeholder",
|
||||
capture_auth_url=captured_url,
|
||||
)
|
||||
|
||||
attempts: list[str] = []
|
||||
|
||||
class _FakeResponse:
|
||||
def __init__(self, body: bytes) -> None:
|
||||
self._body = body
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_exc):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return self._body
|
||||
|
||||
def fake_urlopen(req, *_a, **_kw):
|
||||
attempts.append(req.full_url)
|
||||
if req.full_url.startswith("https://platform.claude.com"):
|
||||
raise RuntimeError("HTTP Error 404: Not Found")
|
||||
body = json.dumps(
|
||||
{
|
||||
"access_token": "sk-ant-test-access",
|
||||
"refresh_token": "sk-ant-test-refresh",
|
||||
"expires_in": 3600,
|
||||
}
|
||||
).encode()
|
||||
return _FakeResponse(body)
|
||||
|
||||
monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen)
|
||||
|
||||
import builtins
|
||||
|
||||
def fake_input(*_a, **_kw):
|
||||
qs = parse_qs(urlparse(captured_url.get("url", "")).query)
|
||||
state = qs.get("state", [""])[0]
|
||||
return f"auth-code#{state}"
|
||||
|
||||
monkeypatch.setattr(builtins, "input", fake_input)
|
||||
|
||||
from agent.anthropic_adapter import run_hermes_oauth_login_pure
|
||||
|
||||
result = run_hermes_oauth_login_pure()
|
||||
|
||||
assert result is not None, "login should succeed via the console fallback"
|
||||
assert attempts == [
|
||||
"https://platform.claude.com/v1/oauth/token",
|
||||
"https://console.anthropic.com/v1/oauth/token",
|
||||
], "login must try platform.claude.com first, then fall back to console"
|
||||
|
||||
|
||||
def test_callback_state_mismatch_aborts(monkeypatch, tmp_path, caplog):
|
||||
"""If the state returned in the callback does not match the one we sent
|
||||
in the authorization URL, the flow must abort before exchanging the code.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue