"""Unit tests for pricing service.""" import pytest from unittest.mock import Mock, patch, MagicMock from decimal import Decimal from src.data.pricing_service import PricingService, get_pricing_service from src.data.providers.base_provider import BasePricingProvider class MockProvider(BasePricingProvider): """Mock provider for testing.""" @property def name(self) -> str: return "MockProvider" @property def supports_websocket(self) -> bool: return False def connect(self) -> bool: self._connected = True return True def disconnect(self): self._connected = False def get_ticker(self, symbol: str): return { 'symbol': symbol, 'bid': Decimal('50000'), 'ask': Decimal('50001'), 'last': Decimal('50000.5'), 'high': Decimal('51000'), 'low': Decimal('49000'), 'volume': Decimal('1000000'), 'timestamp': 1609459200000, } def get_ohlcv(self, symbol: str, timeframe: str = '1h', since=None, limit: int = 100): return [[1609459200000, 50000, 51000, 49000, 50000, 1000]] def subscribe_ticker(self, symbol: str, callback) -> bool: if symbol not in self._subscribers: self._subscribers[symbol] = [] self._subscribers[symbol].append(callback) return True @pytest.fixture def mock_config(): """Create a mock configuration.""" config = Mock() config.get = Mock(side_effect=lambda key, default=None: { "data_providers.primary": [ {"name": "mock", "enabled": True, "priority": 1} ], "data_providers.fallback": {"enabled": True, "api_key": ""}, "data_providers.caching.ticker_ttl": 2, "data_providers.caching.ohlcv_ttl": 60, "data_providers.caching.max_cache_size": 1000, }.get(key, default)) return config class TestPricingService: """Tests for PricingService.""" @patch('src.data.pricing_service.get_config') @patch('src.data.providers.ccxt_provider.CCXTProvider') @patch('src.data.providers.coingecko_provider.CoinGeckoProvider') def test_init(self, mock_coingecko, mock_ccxt, mock_get_config, mock_config): """Test service initialization.""" mock_get_config.return_value = mock_config mock_ccxt_instance = MockProvider() mock_ccxt.return_value = mock_ccxt_instance mock_coingecko_instance = MockProvider() mock_coingecko.return_value = mock_coingecko_instance service = PricingService() assert service.cache is not None assert service.health_monitor is not None @patch('src.data.pricing_service.get_config') @patch('src.data.providers.ccxt_provider.CCXTProvider') def test_get_ticker(self, mock_ccxt, mock_get_config, mock_config): """Test getting ticker data.""" mock_get_config.return_value = mock_config mock_provider = MockProvider() mock_ccxt.return_value = mock_provider service = PricingService() service._providers["MockProvider"] = mock_provider service._active_provider = "MockProvider" ticker = service.get_ticker("BTC/USD") assert ticker['symbol'] == "BTC/USD" assert isinstance(ticker['last'], Decimal) @patch('src.data.pricing_service.get_config') @patch('src.data.providers.ccxt_provider.CCXTProvider') def test_get_ohlcv(self, mock_ccxt, mock_get_config, mock_config): """Test getting OHLCV data.""" mock_get_config.return_value = mock_config mock_provider = MockProvider() mock_ccxt.return_value = mock_provider service = PricingService() service._providers["MockProvider"] = mock_provider service._active_provider = "MockProvider" ohlcv = service.get_ohlcv("BTC/USD", "1h", limit=10) assert len(ohlcv) > 0 assert len(ohlcv[0]) == 6 @patch('src.data.pricing_service.get_config') @patch('src.data.providers.ccxt_provider.CCXTProvider') def test_subscribe_ticker(self, mock_ccxt, mock_get_config, mock_config): """Test subscribing to ticker updates.""" mock_get_config.return_value = mock_config mock_provider = MockProvider() mock_ccxt.return_value = mock_provider service = PricingService() service._providers["MockProvider"] = mock_provider service._active_provider = "MockProvider" callback = Mock() result = service.subscribe_ticker("BTC/USD", callback) assert result is True