brighter-trading/tests/test_edm_client.py

250 lines
8.3 KiB
Python

"""
Tests for the edm_client module.
"""
import pytest
from unittest.mock import Mock, patch, MagicMock
import pandas as pd
import json
# Add src to path for imports
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
from edm_client import EdmClient, EdmConfig
from edm_client.exceptions import EdmConnectionError, EdmApiError, EdmTimeoutError
class TestEdmConfig:
"""Tests for EdmConfig dataclass."""
def test_default_config(self):
"""Test default configuration values."""
config = EdmConfig()
assert config.rest_url == "http://localhost:8080"
assert config.ws_url == "ws://localhost:8765"
assert config.timeout == 30.0
assert config.enabled is True
def test_custom_config(self):
"""Test custom configuration values."""
config = EdmConfig(
rest_url="http://custom:9000",
ws_url="ws://custom:9001",
timeout=60.0,
enabled=False
)
assert config.rest_url == "http://custom:9000"
assert config.ws_url == "ws://custom:9001"
assert config.timeout == 60.0
assert config.enabled is False
class TestEdmClient:
"""Tests for EdmClient REST client."""
def test_init_default(self):
"""Test client initialization with defaults."""
client = EdmClient()
assert client.base_url == "http://localhost:8080"
assert client.timeout == 30.0
def test_init_with_config(self):
"""Test client initialization with EdmConfig."""
config = EdmConfig(rest_url="http://custom:9000", timeout=45.0)
client = EdmClient(config=config)
assert client.base_url == "http://custom:9000"
assert client.timeout == 45.0
def test_init_with_url(self):
"""Test client initialization with URL parameter."""
client = EdmClient(base_url="http://example.com:8080/", timeout=60.0)
assert client.base_url == "http://example.com:8080" # trailing slash removed
assert client.timeout == 60.0
@patch('edm_client.client.requests.get')
def test_get_candles_sync_success(self, mock_get):
"""Test successful candle fetch."""
# Mock response
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {
'candles': [
{'time': 1700000000000, 'open': 100.0, 'high': 105.0, 'low': 99.0, 'close': 104.0, 'volume': 1000.0},
{'time': 1700003600000, 'open': 104.0, 'high': 108.0, 'low': 103.0, 'close': 107.0, 'volume': 1200.0},
]
}
mock_get.return_value = mock_response
client = EdmClient()
df = client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h',
limit=100
)
assert isinstance(df, pd.DataFrame)
assert len(df) == 2
assert list(df.columns) == ['time', 'open', 'high', 'low', 'close', 'volume']
assert df.iloc[0]['time'] == 1700000000000
assert df.iloc[0]['close'] == 104.0
@patch('edm_client.client.requests.get')
def test_get_candles_sync_empty(self, mock_get):
"""Test empty candle response."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'candles': []}
mock_get.return_value = mock_response
client = EdmClient()
df = client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h'
)
assert isinstance(df, pd.DataFrame)
assert len(df) == 0
assert list(df.columns) == ['time', 'open', 'high', 'low', 'close', 'volume']
@patch('edm_client.client.requests.get')
def test_get_candles_sync_api_error(self, mock_get):
"""Test API error handling."""
mock_response = Mock()
mock_response.status_code = 500
mock_response.text = "Internal Server Error"
mock_get.return_value = mock_response
client = EdmClient()
with pytest.raises(EdmApiError) as exc_info:
client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h'
)
assert exc_info.value.status_code == 500
@patch('edm_client.client.requests.get')
def test_get_candles_sync_connection_error(self, mock_get):
"""Test connection error handling."""
import requests
mock_get.side_effect = requests.exceptions.ConnectionError("Connection refused")
client = EdmClient()
with pytest.raises(EdmConnectionError):
client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h'
)
@patch('edm_client.client.requests.get')
def test_get_candles_sync_timeout(self, mock_get):
"""Test timeout error handling."""
import requests
mock_get.side_effect = requests.exceptions.Timeout("Request timed out")
client = EdmClient()
with pytest.raises(EdmTimeoutError):
client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h'
)
@patch('edm_client.client.requests.get')
def test_get_exchanges_sync(self, mock_get):
"""Test get_exchanges_sync method."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'exchanges': ['binance', 'kucoin', 'kraken']}
mock_get.return_value = mock_response
client = EdmClient()
exchanges = client.get_exchanges_sync()
assert exchanges == ['binance', 'kucoin', 'kraken']
@patch('edm_client.client.requests.get')
def test_get_symbols_sync(self, mock_get):
"""Test get_symbols_sync method."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'symbols': ['BTC/USDT', 'ETH/USDT', 'SOL/USDT']}
mock_get.return_value = mock_response
client = EdmClient()
symbols = client.get_symbols_sync('binance')
assert symbols == ['BTC/USDT', 'ETH/USDT', 'SOL/USDT']
@patch('edm_client.client.requests.get')
def test_health_check_sync_healthy(self, mock_get):
"""Test health check when service is healthy."""
mock_response = Mock()
mock_response.status_code = 200
mock_get.return_value = mock_response
client = EdmClient()
assert client.health_check_sync() is True
@patch('edm_client.client.requests.get')
def test_health_check_sync_unhealthy(self, mock_get):
"""Test health check when service is unavailable."""
mock_response = Mock()
mock_response.status_code = 503
mock_get.return_value = mock_response
client = EdmClient()
assert client.health_check_sync() is False
@patch('edm_client.client.requests.get')
def test_health_check_sync_connection_error(self, mock_get):
"""Test health check when connection fails."""
import requests
mock_get.side_effect = requests.exceptions.ConnectionError()
client = EdmClient()
assert client.health_check_sync() is False
class TestEdmClientUrlBuilding:
"""Tests for URL construction in EdmClient."""
@patch('edm_client.client.requests.get')
def test_url_with_all_params(self, mock_get):
"""Test URL construction with all parameters."""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'candles': []}
mock_get.return_value = mock_response
from datetime import datetime
client = EdmClient(base_url="http://test:8080")
client.get_candles_sync(
exchange='binance',
symbol='BTC/USDT',
timeframe='1h',
start=datetime(2024, 1, 1),
end=datetime(2024, 1, 2),
limit=100
)
# Verify the URL was constructed correctly
call_args = mock_get.call_args
url = call_args[0][0]
assert 'exchange=binance' in url
assert 'symbol=BTC%2FUSDT' in url # URL encoded /
assert 'timeframe=1h' in url
assert 'limit=100' in url
assert 'start=' in url
assert 'end=' in url
if __name__ == '__main__':
pytest.main([__file__, '-v'])