mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-27 15:02:39 +03:00
- conftest refuses socket connections outside tests marked integration - the one test that probed Yahoo for real now stubs the reachability check
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Tests that empty vendor results never become fabricated data.
|
|
|
|
Covers two systematic fixes:
|
|
- load_ohlcv must not cache an empty download (cache poisoning), and must
|
|
raise NoMarketDataError instead of returning an empty frame.
|
|
- route_to_vendor must convert NoMarketDataError into a single explicit
|
|
"NO_DATA_AVAILABLE" sentinel after all vendors are exhausted.
|
|
"""
|
|
|
|
import os
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
from tradingagents.dataflows import router
|
|
from tradingagents.dataflows.config import set_config
|
|
from tradingagents.dataflows.errors import NoMarketDataError
|
|
from tradingagents.dataflows.vendors.yahoo import ohlcv
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestLoadOhlcvNoPoison(unittest.TestCase):
|
|
def setUp(self):
|
|
self._tmp = os.path.join(os.path.dirname(__file__), "_tmp_cache")
|
|
os.makedirs(self._tmp, exist_ok=True)
|
|
set_config({"data_cache_dir": self._tmp})
|
|
|
|
def tearDown(self):
|
|
for f in os.listdir(self._tmp):
|
|
os.remove(os.path.join(self._tmp, f))
|
|
os.rmdir(self._tmp)
|
|
|
|
def test_empty_download_raises_and_does_not_cache(self):
|
|
empty = pd.DataFrame()
|
|
# Yahoo answers, so an empty download means the symbol has no data.
|
|
reachable = mock.patch.object(ohlcv, "vendor_reachable", return_value=True)
|
|
reachable.start()
|
|
self.addCleanup(reachable.stop)
|
|
with mock.patch.object(ohlcv.yf, "download", return_value=empty), \
|
|
self.assertRaises(NoMarketDataError):
|
|
ohlcv.load_ohlcv("FAKE", "2026-01-01")
|
|
# Nothing should have been written to the cache.
|
|
self.assertEqual(os.listdir(self._tmp), [])
|
|
|
|
# A second call must re-attempt the fetch (no poisoned cache served).
|
|
with mock.patch.object(ohlcv.yf, "download", return_value=empty) as dl2:
|
|
with self.assertRaises(NoMarketDataError):
|
|
ohlcv.load_ohlcv("FAKE", "2026-01-01")
|
|
self.assertTrue(dl2.called)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRouteToVendorSentinel(unittest.TestCase):
|
|
def test_no_data_from_all_vendors_returns_sentinel(self):
|
|
def raises_no_data(symbol, *a, **k):
|
|
raise NoMarketDataError(symbol, "GC=F", "no rows")
|
|
|
|
patched = {"yfinance": raises_no_data, "alpha_vantage": raises_no_data}
|
|
with mock.patch.dict(
|
|
router.VENDOR_METHODS, {"get_stock_data": patched}, clear=False
|
|
):
|
|
result = router.route_to_vendor(
|
|
"get_stock_data", "XAUUSD+", "2026-01-01", "2026-01-10"
|
|
)
|
|
self.assertIn("NO_DATA_AVAILABLE", result)
|
|
self.assertIn("XAUUSD+", result)
|
|
self.assertIn("GC=F", result)
|
|
self.assertIn("Do not estimate", result)
|
|
|
|
def test_unconfigured_fallback_does_not_mask_no_data(self):
|
|
# When the primary vendor reports no data and the fallback is simply
|
|
# unavailable (e.g. missing API key -> raises), the no-data sentinel
|
|
# must win rather than the fallback's incidental error crashing out.
|
|
def raises_no_data(symbol, *a, **k):
|
|
raise NoMarketDataError(symbol, symbol, "no rows")
|
|
|
|
def raises_unavailable(symbol, *a, **k):
|
|
raise ValueError("ALPHA_VANTAGE_API_KEY environment variable is not set.")
|
|
|
|
patched = {"yfinance": raises_no_data, "alpha_vantage": raises_unavailable}
|
|
with mock.patch.dict(
|
|
router.VENDOR_METHODS, {"get_stock_data": patched}, clear=False
|
|
):
|
|
result = router.route_to_vendor(
|
|
"get_stock_data", "FAKE", "2026-01-01", "2026-01-10"
|
|
)
|
|
self.assertIn("NO_DATA_AVAILABLE", result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|