diff --git a/tests/conftest.py b/tests/conftest.py index d84c976..2c19531 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,7 @@ -"""Shared test fixtures, constants, and markers. +"""Shared test fixtures and constants. -Centralizes the API key / base URL resolution, the live client fixtures -(``datamaxi`` / ``telegram`` / ``naver``), and the ``_FLAKY_PROD_DATA_XFAIL`` -marker that ``test_call.py`` and ``test_integration.py`` previously -copy-pasted verbatim. +Centralizes the API key / base URL resolution and the live client fixtures +(``datamaxi`` / ``telegram`` / ``naver``). """ import os @@ -16,34 +14,24 @@ # honor DATAMAXI_API_KEY (preferred) and the legacy API_KEY. API_KEY = os.getenv("DATAMAXI_API_KEY") or os.getenv("API_KEY") BASE_URL = os.getenv("BASE_URL") or "https://api.datamaxiplus.com" - -# Shared xfail marker for tests whose outcome depends on prod-data -# availability — funding-rate / naver-trend state are NATS-warmed in-memory -# caches on the API pods, so any cold-start of the API fleet leaves them -# temporarily empty and the smoke-style tests raise ServerError(500, "no data -# found"). Marked strict=False so they pass cleanly once the cache is hot. -_FLAKY_PROD_DATA_XFAIL = pytest.mark.xfail( - reason=( - "Depends on prod NATS-warmed state; intermittent 500 'no data found' " - "on cold pods. Pre-existing flakiness — unrelated to SDK regen." - ), - strict=False, -) +# Live-lane timeout: larger than the SDK's 10s default so slow prod endpoints +# (e.g. unfiltered premium) don't ReadTimeout on the smoke/integration tests. +TIMEOUT = int(os.getenv("DATAMAXI_TIMEOUT") or "30") @pytest.fixture(scope="module") def datamaxi(): """Create Datamaxi client for live tests.""" - return Datamaxi(api_key=API_KEY, base_url=BASE_URL) + return Datamaxi(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT) @pytest.fixture(scope="module") def telegram(): """Create Telegram client for live tests.""" - return Telegram(api_key=API_KEY, base_url=BASE_URL) + return Telegram(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT) @pytest.fixture(scope="module") def naver(): """Create Naver client for live tests.""" - return Naver(api_key=API_KEY, base_url=BASE_URL) + return Naver(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT) diff --git a/tests/test_call.py b/tests/test_call.py index 0c4ab30..0214e71 100644 --- a/tests/test_call.py +++ b/tests/test_call.py @@ -11,7 +11,7 @@ import pytest -from tests.conftest import API_KEY, _FLAKY_PROD_DATA_XFAIL +from tests.conftest import API_KEY # Live alive-check / smoke lane: a thin subset of test_integration.py that # pings each endpoint once. Skipped without a key and deselected from the @@ -70,7 +70,6 @@ def test_cex_wallet_status(datamaxi): datamaxi.cex.wallet_status.assets(exchange="binance") -@_FLAKY_PROD_DATA_XFAIL def test_funding_rate(datamaxi): """Smoke test for funding rate endpoints.""" datamaxi.funding_rate.history(exchange="binance", symbol="BTC-USDT") @@ -87,7 +86,7 @@ def test_forex(datamaxi): def test_premium(datamaxi): """Smoke test for premium endpoints.""" - datamaxi.premium() + datamaxi.premium(limit=10) datamaxi.premium.exchanges() @@ -97,7 +96,6 @@ def test_telegram(telegram): telegram.messages() -@_FLAKY_PROD_DATA_XFAIL def test_naver(naver): """Smoke test for naver endpoints.""" naver.symbols() diff --git a/tests/test_integration.py b/tests/test_integration.py index aff9b24..4628d49 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -20,8 +20,8 @@ import pandas as pd from datetime import datetime, timedelta -from datamaxi.error import ClientError -from tests.conftest import API_KEY, _FLAKY_PROD_DATA_XFAIL +from datamaxi.error import ParameterRequiredError +from tests.conftest import API_KEY # Live integration lane: exercises prod endpoints with every supported param. # Skipped without a key and deselected from the keyless CI lane via the @@ -74,9 +74,9 @@ def test_symbols_with_exchange_only(self, datamaxi): assert len(result) > 0 def test_symbols_with_market_only(self, datamaxi): - """Test getting symbols with only market - requires exchange in API.""" - # Note: API requires exchange parameter, so this should raise ClientError - with pytest.raises(ClientError): + """Test getting symbols with only market - exchange is mandatory.""" + # exchange is a required param, validated client-side before the API call. + with pytest.raises(ParameterRequiredError): datamaxi.cex.candle.symbols(market="spot") def test_intervals(self, datamaxi): @@ -553,7 +553,6 @@ def test_history_both_from_and_to_datetime(self, datamaxi): toDateTime=to_dt, ) - @_FLAKY_PROD_DATA_XFAIL def test_latest_basic(self, datamaxi): """Test basic latest funding rate fetch.""" result = datamaxi.funding_rate.latest( @@ -563,7 +562,6 @@ def test_latest_basic(self, datamaxi): assert isinstance(result, pd.DataFrame) assert len(result) == 1 - @_FLAKY_PROD_DATA_XFAIL def test_latest_pandas_false(self, datamaxi): """Test latest funding rate with pandas=False.""" result = datamaxi.funding_rate.latest( @@ -675,33 +673,11 @@ def test_premium_token_include(self, datamaxi): result = datamaxi.premium(token_include="bitcoin", limit=10) assert isinstance(result, pd.DataFrame) - @pytest.mark.xfail( - reason=( - "Flaky against prod data — the SDK raises ValueError('no data found') " - "whenever /api/v1/premium returns an empty page, and the specific " - "(token_exclude=SHIB, limit=10) combination hits an empty window " - "depending on the active premium feed. Test has failed continuously " - "on Python 3.14 since 2026-04-22 (well before this PR). Tracked for " - "a follow-up that either makes the SDK return empty cleanly or picks " - "params with guaranteed-non-empty output." - ), - strict=False, - ) def test_premium_token_exclude(self, datamaxi): """Test premium data with token_exclude filter.""" result = datamaxi.premium(token_exclude="SHIB", limit=10) assert isinstance(result, pd.DataFrame) - @pytest.mark.xfail( - reason=( - "Same flake as test_premium_token_exclude — premium(pandas=False, " - "limit=10) intermittently hits an empty page on prod and the SDK " - "raises instead of returning the empty envelope. Pre-existing " - "(failing since 2026-04-22); follow-up should normalize empty-result " - "behavior in the SDK." - ), - strict=False, - ) def test_premium_pandas_false(self, datamaxi): """Test premium data with pandas=False.""" result = datamaxi.premium(pandas=False, limit=10) @@ -839,27 +815,23 @@ def test_messages_pagination_next_request(self, telegram): class TestNaver: """Test Naver endpoints with all parameters.""" - @_FLAKY_PROD_DATA_XFAIL def test_symbols(self, naver): """Test getting supported symbols.""" result = naver.symbols() assert isinstance(result, list) assert len(result) > 0 - @_FLAKY_PROD_DATA_XFAIL def test_trend_basic(self, naver): """Test basic trend data fetch.""" result = naver.trend("BTC") assert hasattr(result, "head") assert len(result) > 0 - @_FLAKY_PROD_DATA_XFAIL def test_trend_different_symbol(self, naver): """Test trend data for different symbol.""" result = naver.trend("ETH") assert hasattr(result, "head") - @_FLAKY_PROD_DATA_XFAIL def test_trend_pandas_false(self, naver): """Test trend data with pandas=False.""" result = naver.trend("BTC", pandas=False) @@ -899,7 +871,6 @@ def test_wallet_status_dataframe_index(self, datamaxi): result = datamaxi.cex.wallet_status(exchange="binance", asset="BTC") assert result.index.name == "network" - @_FLAKY_PROD_DATA_XFAIL def test_funding_rate_latest_single_row(self, datamaxi): """Test that latest funding rate returns single row.""" result = datamaxi.funding_rate.latest(