fix(data): add Alpha Vantage request timeout and stop mislabeling bad keys

Alpha Vantage requests had no timeout (a stall could hang the run) and any
notice mentioning "API key" was raised as a rate limit — so an invalid/missing
key was mislabeled and silently treated as transient. Add a 30s request timeout
and classify rate-limit phrasing before key errors (rate-limit notices also
mention "API key"), surfacing a bad key as a real configuration error.
This commit is contained in:
Yijia-Xiao
2026-06-13 21:47:06 +00:00
parent a597063747
commit e4be7cc5a3
2 changed files with 77 additions and 10 deletions

View File

@@ -0,0 +1,54 @@
"""Alpha Vantage request hardening.
Regressions for #990 (no request timeout -> can hang) and #991 (invalid-key
responses mislabeled as rate limits and silently treated as transient).
"""
import pytest
import tradingagents.dataflows.alpha_vantage_common as av
class _FakeResponse:
def __init__(self, text):
self.text = text
def raise_for_status(self):
pass
def _patched_get(body, capture=None):
def fake_get(url, params=None, **kwargs):
if capture is not None:
capture.update(kwargs)
return _FakeResponse(body)
return fake_get
@pytest.mark.unit
def test_request_passes_timeout(monkeypatch):
captured = {}
monkeypatch.setattr(av.requests, "get", _patched_get("Date,Close\n2025-01-02,1.0", captured))
av._make_api_request("TIME_SERIES_DAILY", {"symbol": "AAPL"})
assert captured.get("timeout") == av.REQUEST_TIMEOUT # #990
@pytest.mark.unit
def test_rate_limit_detected(monkeypatch):
body = '{"Information": "Our standard API rate limit is 25 requests per day. ... your API key ..."}'
monkeypatch.setattr(av.requests, "get", _patched_get(body))
with pytest.raises(av.AlphaVantageRateLimitError):
av._make_api_request("TIME_SERIES_DAILY", {"symbol": "AAPL"})
@pytest.mark.unit
def test_invalid_key_not_mislabeled_as_rate_limit(monkeypatch):
# AV's invalid-key notice mentions "API key"; it must NOT be treated as a
# (transient) rate limit, but surface as a real configuration error (#991).
body = ('{"Information": "the parameter apikey is invalid or missing. '
'Please claim your free API key on (https://www.alphavantage.co/support/#api-key)."}')
monkeypatch.setattr(av.requests, "get", _patched_get(body))
with pytest.raises(av.AlphaVantageNotConfiguredError):
av._make_api_request("TIME_SERIES_DAILY", {"symbol": "AAPL"})
with pytest.raises(av.AlphaVantageRateLimitError): # sanity: rate-limit path still distinct
monkeypatch.setattr(av.requests, "get", _patched_get('{"Note": "API call frequency is 5 calls per minute."}'))
av._make_api_request("TIME_SERIES_DAILY", {"symbol": "AAPL"})