test(credential_pool): cover Anthropic env auth_type classification

Add regression tests for the sk-ant-oat OAuth heuristic and shorten the
inline comment. Verifies admin keys (sk-ant-admin-*) and standard API keys
classify as api_key, only sk-ant-oat- tokens flow into the OAuth refresh path.
This commit is contained in:
teknium1
2026-06-30 17:29:03 -07:00
committed by Teknium
parent 18966b6244
commit 508156fd42
2 changed files with 49 additions and 6 deletions
+1 -6
View File
@@ -2162,12 +2162,7 @@ def _seed_from_env(provider: str, entries: List[PooledCredential]) -> Tuple[bool
if _is_source_suppressed(provider, source):
continue
active_sources.add(source)
# Claude Code OAuth tokens are the only Anthropic credentials that should
# flow into the OAuth refresh path. Match the `sk-ant-oat` prefix
# positively — matching by negation (everything not `sk-ant-api`) wrongly
# tagged admin keys (`sk-ant-admin*`) and any future API-key prefix as
# OAuth, which then got immediately marked exhausted on first use because
# they have no refresh token.
# Claude Code OAuth tokens are the only Anthropic credentials that should flow into the OAuth refresh path.
auth_type = (
AUTH_TYPE_OAUTH
if provider == "anthropic" and token.startswith("sk-ant-oat")
@@ -248,3 +248,51 @@ class TestAuthCredentialPoolFallback:
assert key == "sk-dotenv-priority-xyz"
assert source == "DEEPSEEK_API_KEY"
mp.assert_not_called()
class TestAnthropicEnvAuthTypeClassification:
"""_seed_from_env must classify Anthropic env tokens by the sk-ant-oat prefix.
Regression for PR #16733: the previous heuristic tagged any token NOT
starting with `sk-ant-api` as OAuth. That misclassified admin keys
(`sk-ant-admin-*`), workspace keys, and any future API-key prefix as OAuth.
OAuth-typed entries with no refresh token are immediately marked exhausted
in _refresh_entry, so a legitimate admin key gets stuck EXHAUSTED on first
use and the pool rotates away from a working credential.
Only real Claude Code OAuth tokens (`sk-ant-oat-…`) should flow into the
OAuth refresh path.
"""
def _seed(self, env_var, token):
from agent.credential_pool import _seed_from_env
entries = []
_seed_from_env("anthropic", entries)
# The seeded entry whose label is the env var we wrote.
matching = [e for e in entries if getattr(e, "label", None) == env_var]
assert matching, f"expected a seeded entry for {env_var}, got {entries}"
return matching[0]
def test_oauth_token_classified_as_oauth(self, isolated_hermes_home):
"""sk-ant-oat- token from CLAUDE_CODE_OAUTH_TOKEN → AUTH_TYPE_OAUTH."""
from agent.credential_pool import AUTH_TYPE_OAUTH
_write_env_file(isolated_hermes_home, CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat-fake-12345")
entry = self._seed("CLAUDE_CODE_OAUTH_TOKEN", "sk-ant-oat-fake-12345")
assert entry.auth_type == AUTH_TYPE_OAUTH
def test_admin_key_classified_as_api_key(self, isolated_hermes_home):
"""sk-ant-admin- key from ANTHROPIC_API_KEY → AUTH_TYPE_API_KEY, not OAuth.
This is the bug the fix targets: previously this was tagged OAuth.
"""
from agent.credential_pool import AUTH_TYPE_API_KEY
_write_env_file(isolated_hermes_home, ANTHROPIC_API_KEY="sk-ant-admin-fake-12345")
entry = self._seed("ANTHROPIC_API_KEY", "sk-ant-admin-fake-12345")
assert entry.auth_type == AUTH_TYPE_API_KEY
def test_standard_api_key_classified_as_api_key(self, isolated_hermes_home):
"""sk-ant-api- key → AUTH_TYPE_API_KEY (unchanged behaviour)."""
from agent.credential_pool import AUTH_TYPE_API_KEY
_write_env_file(isolated_hermes_home, ANTHROPIC_API_KEY="sk-ant-api-fake-12345")
entry = self._seed("ANTHROPIC_API_KEY", "sk-ant-api-fake-12345")
assert entry.auth_type == AUTH_TYPE_API_KEY