mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-06-16 21:06:15 +03:00
refactor(data): unify vendor errors under a VendorError hierarchy
Every condition where a vendor cannot return usable data now derives from a single VendorError base (errors.py): NoMarketDataError, VendorRateLimitError, and VendorNotConfiguredError (still a ValueError for back-compat). Vendor-named errors subclass the generic bases, and the router catches the base types, so a new vendor needs no new except clause. Not-configured now has explicit try-next-vendor handling instead of falling through the generic catch-all. The number of error types tracks the number of distinct router reactions, not the number of causes.
This commit is contained in:
105
tests/test_vendor_errors.py
Normal file
105
tests/test_vendor_errors.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""The vendor data-error hierarchy: every "vendor couldn't return usable data"
|
||||
condition derives from VendorError, so the router catches base types and any
|
||||
vendor slots in without new handling.
|
||||
"""
|
||||
import copy
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import tradingagents.dataflows.config as config_module
|
||||
import tradingagents.default_config as default_config
|
||||
from tradingagents.dataflows import interface
|
||||
from tradingagents.dataflows.alpha_vantage_common import (
|
||||
AlphaVantageNotConfiguredError,
|
||||
AlphaVantageRateLimitError,
|
||||
)
|
||||
from tradingagents.dataflows.config import set_config
|
||||
from tradingagents.dataflows.errors import (
|
||||
NoMarketDataError,
|
||||
VendorError,
|
||||
VendorNotConfiguredError,
|
||||
VendorRateLimitError,
|
||||
)
|
||||
from tradingagents.dataflows.fred import FredNotConfiguredError
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class HierarchyTests(unittest.TestCase):
|
||||
def test_all_conditions_derive_from_vendor_error(self):
|
||||
for cls in (NoMarketDataError, VendorRateLimitError, VendorNotConfiguredError):
|
||||
self.assertTrue(issubclass(cls, VendorError))
|
||||
|
||||
def test_not_configured_is_still_a_value_error(self):
|
||||
# Back-compat: existing `except ValueError` callers keep working.
|
||||
self.assertTrue(issubclass(VendorNotConfiguredError, ValueError))
|
||||
|
||||
def test_vendor_named_errors_subclass_the_generic_bases(self):
|
||||
self.assertTrue(issubclass(AlphaVantageRateLimitError, VendorRateLimitError))
|
||||
self.assertTrue(issubclass(AlphaVantageNotConfiguredError, VendorNotConfiguredError))
|
||||
self.assertTrue(issubclass(FredNotConfiguredError, VendorNotConfiguredError))
|
||||
# ... and therefore still ValueErrors
|
||||
self.assertTrue(issubclass(FredNotConfiguredError, ValueError))
|
||||
|
||||
def test_symbol_utils_reexports_no_market_data_error(self):
|
||||
from tradingagents.dataflows.symbol_utils import (
|
||||
NoMarketDataError as ReExported,
|
||||
)
|
||||
self.assertIs(ReExported, NoMarketDataError)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class RouterHandlesBaseTypesTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
config_module._config = copy.deepcopy(default_config.DEFAULT_CONFIG)
|
||||
|
||||
def tearDown(self):
|
||||
config_module._config = copy.deepcopy(default_config.DEFAULT_CONFIG)
|
||||
|
||||
def test_rate_limit_subclass_caught_by_base(self):
|
||||
# A vendor-named rate-limit error skips to the next vendor in the chain.
|
||||
set_config({"data_vendors": {"core_stock_apis": "alpha_vantage,yfinance"}})
|
||||
|
||||
def _throttled(*a, **k):
|
||||
raise AlphaVantageRateLimitError("slow down")
|
||||
|
||||
with mock.patch.dict(
|
||||
interface.VENDOR_METHODS,
|
||||
{"get_stock_data": {"alpha_vantage": _throttled, "yfinance": lambda *a, **k: "YF"}},
|
||||
clear=False,
|
||||
):
|
||||
out = interface.route_to_vendor("get_stock_data", "AAPL", "2026-01-01", "2026-01-10")
|
||||
self.assertEqual(out, "YF")
|
||||
|
||||
def test_not_configured_falls_through_to_next_vendor(self):
|
||||
set_config({"data_vendors": {"core_stock_apis": "alpha_vantage,yfinance"}})
|
||||
|
||||
def _unconfigured(*a, **k):
|
||||
raise AlphaVantageNotConfiguredError("no key")
|
||||
|
||||
with mock.patch.dict(
|
||||
interface.VENDOR_METHODS,
|
||||
{"get_stock_data": {"alpha_vantage": _unconfigured, "yfinance": lambda *a, **k: "YF"}},
|
||||
clear=False,
|
||||
):
|
||||
out = interface.route_to_vendor("get_stock_data", "AAPL", "2026-01-01", "2026-01-10")
|
||||
self.assertEqual(out, "YF")
|
||||
|
||||
def test_sole_unconfigured_vendor_surfaces_the_error(self):
|
||||
# With no fallback, the not-configured condition must surface (not vanish).
|
||||
set_config({"data_vendors": {"core_stock_apis": "alpha_vantage"}})
|
||||
|
||||
def _unconfigured(*a, **k):
|
||||
raise AlphaVantageNotConfiguredError("no key")
|
||||
|
||||
with mock.patch.dict(
|
||||
interface.VENDOR_METHODS,
|
||||
{"get_stock_data": {"alpha_vantage": _unconfigured}},
|
||||
clear=False,
|
||||
), self.assertRaises(AlphaVantageNotConfiguredError):
|
||||
interface.route_to_vendor("get_stock_data", "AAPL", "2026-01-01", "2026-01-10")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user