hermes-agent/tests/run_agent/test_credential_pool_interrupt.py
Teknium 6b81590c55
test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions
Systematic prune per AGENTS.md test policy, one pass over every major
test tree (gateway, hermes_cli, tools, agent, run_agent, plugins, cli,
cron, tui_gateway, honcho/openviking, root-level):

- DELETE: source-reading tests (read_text/getsource on prod files),
  change-detector tests (exact catalog counts, model-name snapshots,
  config version literals), mock-echo tests (assert a mock returns what
  it was told), assertion-free/trivial tests, near-duplicate
  parametrizations (boundaries + one representative kept), async/sync
  twin duplicates, cosmetic within-file variations.
- KEEP (mandatory): security/redaction/approval guards, message-role
  alternation invariants, prompt-caching/deterministic-call-id
  invariants, issue-number regression tests (deduped), E2E tests.
- 6 test files deleted outright (script-style/no-assert or fully
  redundant); conftest.py, fakes/, fixtures/ untouched.
- tests/acp/conftest.py added: autouse fixture stubs the live
  models.dev/GitHub/Copilot/Anthropic inventory fetches that ACP server
  tests performed on every session create — test_server.py 147s → 3.4s,
  and the tests are now genuinely hermetic.
- Sleep-based slowness shrunk where safe (codex_ttfb_watchdog,
  compression_concurrent_fork, etc.); no wall-clock assertion tightened.

Verification: full hermetic suite via scripts/run_tests.sh —
2439 files, 31,130 tests passed, 0 failed, 0 flaky retries, 315s wall
(baseline: 583s wall, 13,564s subprocess CPU).
2026-07-29 13:10:23 -07:00

83 lines
3.1 KiB
Python

"""Regression test for #26145: credential pool rotation after interrupt-resume.
When has_retried_429 is lost (user cancels between 429s), the pool should
still rotate if the current credential is already marked exhausted.
"""
from unittest.mock import MagicMock, patch
from agent.credential_pool import PooledCredential, STATUS_EXHAUSTED
from agent.error_classifier import FailoverReason
def _make_entry(idx, **overrides):
defaults = dict(
provider="test-provider",
id=f"cred-{idx}",
label=f"Credential {idx}",
auth_type="api_key",
priority=idx,
source="manual",
access_token=f"key-{idx}",
)
defaults.update(overrides)
return PooledCredential(**defaults)
def _make_pool(entries):
pool = MagicMock()
pool.entries = MagicMock(return_value=entries)
pool.current.return_value = entries[0]
# Must be set explicitly — MagicMock.provider returns a truthy
# child mock, which would trigger the provider-mismatch guard.
pool.provider = ""
return pool
def test_rotate_immediately_when_credential_already_exhausted():
"""If current credential has last_status='exhausted', rotate on first 429
instead of retrying (Option A fix for #26145)."""
entries = [_make_entry(0, last_status=STATUS_EXHAUSTED, last_error_code=429), _make_entry(1)]
pool = _make_pool(entries)
pool.mark_exhausted_and_rotate.return_value = entries[1]
from run_agent import AIAgent
with patch("run_agent.get_tool_definitions", return_value=[]), patch("run_agent.check_toolset_requirements", return_value={}), patch("run_agent.OpenAI"):
agent = MagicMock(spec=AIAgent)
agent._credential_pool = pool
agent._swap_credential = MagicMock()
recovered, retried = AIAgent._recover_with_credential_pool(
agent,
status_code=429,
has_retried_429=False, # Key: False on first 429 after interrupt
classified_reason=FailoverReason.rate_limit,
)
assert recovered is True
assert retried is False
pool.mark_exhausted_and_rotate.assert_called_once()
agent._swap_credential.assert_called_once_with(entries[1])
def test_rotate_on_second_429_when_not_exhausted():
"""When credential is active and this is the second 429, rotate (existing behavior)."""
entries = [_make_entry(0, last_status=None), _make_entry(1)]
pool = _make_pool(entries)
pool.mark_exhausted_and_rotate.return_value = entries[1]
from run_agent import AIAgent
with patch("run_agent.get_tool_definitions", return_value=[]), patch("run_agent.check_toolset_requirements", return_value={}), patch("run_agent.OpenAI"):
agent = MagicMock(spec=AIAgent)
agent._credential_pool = pool
agent._swap_credential = MagicMock()
recovered, retried = AIAgent._recover_with_credential_pool(
agent,
status_code=429,
has_retried_429=True, # Second 429
classified_reason=FailoverReason.rate_limit,
)
assert recovered is True
assert retried is False
pool.mark_exhausted_and_rotate.assert_called_once()