brighter-trading/tests/test_candles.py

178 lines
5.6 KiB
Python

"""
Tests for the Candles module.
Note: The candle data fetching has been moved to EDM (Exchange Data Manager).
These tests use mocked EDM responses to avoid requiring a running EDM server.
Functions like ts_of_n_minutes_ago and timeframe_to_minutes are tested in
test_shared_utilities.py.
"""
import datetime as dt
import pytest
import pandas as pd
from unittest.mock import MagicMock, patch
from candles import Candles
from Configuration import Configuration
from DataCache_v3 import DataCache
@pytest.fixture
def mock_edm_client():
"""Create a mock EDM client."""
mock = MagicMock()
# Mock get_candles_sync to return sample candle data
sample_candles = pd.DataFrame({
'time': [1700000000000, 1700000060000, 1700000120000, 1700000180000, 1700000240000],
'open': [100.0, 101.0, 102.0, 101.5, 103.0],
'high': [102.0, 103.0, 104.0, 103.5, 105.0],
'low': [99.0, 100.0, 101.0, 100.5, 102.0],
'close': [101.0, 102.0, 101.5, 103.0, 104.0],
'volume': [1000.0, 1200.0, 800.0, 1500.0, 900.0]
})
mock.get_candles_sync.return_value = sample_candles
return mock
@pytest.fixture
def mock_exchanges():
"""Create mock exchange interface."""
return MagicMock()
@pytest.fixture
def mock_users():
"""Create mock users object."""
mock = MagicMock()
mock.get_chart_view.return_value = {'timeframe': '1m', 'exchange': 'binance', 'market': 'BTC/USDT'}
return mock
@pytest.fixture
def data_cache():
"""Create a fresh DataCache."""
return DataCache()
@pytest.fixture
def config():
"""Create a Configuration instance."""
return Configuration()
@pytest.fixture
def candles(mock_exchanges, mock_users, data_cache, config, mock_edm_client):
"""Create a Candles instance with mocked dependencies."""
return Candles(
exchanges=mock_exchanges,
users=mock_users,
datacache=data_cache,
config=config,
edm_client=mock_edm_client
)
class TestCandles:
"""Tests for the Candles class."""
def test_candles_creation(self, candles):
"""Test that Candles object can be created."""
assert candles is not None
def test_get_last_n_candles(self, candles, mock_edm_client):
"""Test getting last N candles."""
result = candles.get_last_n_candles(
num_candles=5,
asset='BTC/USDT',
timeframe='1m',
exchange='binance',
user_name='test_user'
)
assert result is not None
assert isinstance(result, pd.DataFrame)
assert len(result) == 5
assert 'open' in result.columns
assert 'high' in result.columns
assert 'low' in result.columns
assert 'close' in result.columns
assert 'volume' in result.columns
def test_get_last_n_candles_caps_at_1000(self, candles, mock_edm_client):
"""Test that requests for more than 1000 candles are capped."""
candles.get_last_n_candles(
num_candles=2000,
asset='BTC/USDT',
timeframe='1m',
exchange='binance',
user_name='test_user'
)
# Check that EDM was called with capped limit
call_args = mock_edm_client.get_candles_sync.call_args
assert call_args.kwargs.get('limit', call_args[1].get('limit')) == 1000
def test_candles_raises_without_edm(self, mock_exchanges, mock_users, data_cache, config):
"""Test that Candles raises error when EDM is not available."""
candles_no_edm = Candles(
exchanges=mock_exchanges,
users=mock_users,
datacache=data_cache,
config=config,
edm_client=None
)
with pytest.raises(RuntimeError, match="EDM client not initialized"):
candles_no_edm.get_last_n_candles(
num_candles=5,
asset='BTC/USDT',
timeframe='1m',
exchange='binance',
user_name='test_user'
)
def test_get_latest_values(self, candles, mock_edm_client):
"""Test getting latest values for a specific field."""
result = candles.get_latest_values(
value_name='close',
symbol='BTC/USDT',
timeframe='1m',
exchange='binance',
num_record=5,
user_name='test_user'
)
assert result is not None
# Should return series/list of close values
assert len(result) <= 5
def test_max_records_from_config(self, candles, config):
"""Test that max_records is loaded from config."""
expected_max = config.get_setting('max_data_loaded')
assert candles.max_records == expected_max
def test_candle_cache_created(self, candles, data_cache):
"""Test that candle cache is created on initialization."""
# The cache should exist
assert 'candles' in data_cache.caches
class TestCandlesIntegration:
"""Integration-style tests that verify EDM client interaction."""
def test_edm_client_called_correctly(self, candles, mock_edm_client):
"""Test that EDM client is called with correct parameters."""
candles.get_last_n_candles(
num_candles=10,
asset='ETH/USDT',
timeframe='5m',
exchange='binance',
user_name='test_user'
)
mock_edm_client.get_candles_sync.assert_called_once()
call_kwargs = mock_edm_client.get_candles_sync.call_args.kwargs
assert call_kwargs['symbol'] == 'ETH/USDT'
assert call_kwargs['timeframe'] == '5m'
assert call_kwargs['exchange'] == 'binance'
assert call_kwargs['limit'] == 10