""" 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'])