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:
teknium1 2026-06-23 23:35:45 -07:00 committed by Teknium
parent 2ee6449fe5
commit ba50787180
2 changed files with 106 additions and 0 deletions

View file

@ -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)

View file

@ -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.