250 lines
8.3 KiB
Python
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'])
|