"""Tests for configuration module.""" from decimal import Decimal import pytest from tradefinder.adapters.types import ( OrderRequest, OrderType, PositionSide, Side, ) class TestOrderRequest: """Tests for OrderRequest validation.""" def test_valid_limit_order(self) -> None: """Test valid limit order creation.""" order = OrderRequest( symbol="BTCUSDT", side=Side.BUY, position_side=PositionSide.LONG, order_type=OrderType.LIMIT, quantity=Decimal("0.001"), price=Decimal("50000"), ) order.validate() # Should not raise def test_limit_order_without_price_fails(self) -> None: """Test limit order without price raises error.""" order = OrderRequest( symbol="BTCUSDT", side=Side.BUY, position_side=PositionSide.LONG, order_type=OrderType.LIMIT, quantity=Decimal("0.001"), price=None, ) with pytest.raises(ValueError, match="Limit orders require a price"): order.validate() def test_stop_order_without_stop_price_fails(self) -> None: """Test stop order without stop price raises error.""" order = OrderRequest( symbol="BTCUSDT", side=Side.SELL, position_side=PositionSide.LONG, order_type=OrderType.STOP_MARKET, quantity=Decimal("0.001"), stop_price=None, ) with pytest.raises(ValueError, match="STOP_MARKET orders require a stop_price"): order.validate() def test_valid_stop_market_order(self) -> None: """Test valid stop market order creation.""" order = OrderRequest( symbol="BTCUSDT", side=Side.SELL, position_side=PositionSide.LONG, order_type=OrderType.STOP_MARKET, quantity=Decimal("0.001"), stop_price=Decimal("48000"), ) order.validate() # Should not raise def test_zero_quantity_fails(self) -> None: """Test zero quantity raises error.""" order = OrderRequest( symbol="BTCUSDT", side=Side.BUY, position_side=PositionSide.LONG, order_type=OrderType.MARKET, quantity=Decimal("0"), ) with pytest.raises(ValueError, match="Quantity must be positive"): order.validate() def test_negative_quantity_fails(self) -> None: """Test negative quantity raises error.""" order = OrderRequest( symbol="BTCUSDT", side=Side.BUY, position_side=PositionSide.LONG, order_type=OrderType.MARKET, quantity=Decimal("-0.001"), ) with pytest.raises(ValueError, match="Quantity must be positive"): order.validate() class TestConfigValidation: """Tests for Settings configuration validation. These tests require environment variables or .env file setup. They verify that configuration validation works correctly. """ def test_symbols_parsing_from_string(self) -> None: """Test comma-separated symbol parsing.""" # This tests the validator logic directly # Simulate what the validator does input_str = "BTCUSDT, ETHUSDT, SOLUSDT" result = [s.strip().upper() for s in input_str.split(",") if s.strip()] assert result == ["BTCUSDT", "ETHUSDT", "SOLUSDT"] def test_valid_timeframes(self) -> None: """Test valid timeframe values.""" valid = {"1m", "5m", "15m", "30m", "1h", "4h", "1d"} for tf in valid: # These should not raise assert tf in { "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M", } def test_invalid_timeframe(self) -> None: """Test invalid timeframe is rejected.""" invalid_timeframes = ["2m", "10m", "2d", "1y", "invalid"] valid_set = { "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M", } for tf in invalid_timeframes: assert tf not in valid_set class TestTradingModes: """Tests for trading mode behavior.""" def test_paper_mode_no_credentials_needed(self) -> None: """Paper mode should not require API credentials.""" from tradefinder.core.config import TradingMode assert TradingMode.PAPER.value == "paper" # Paper mode is for internal simulation - no API calls def test_testnet_mode_requires_testnet_keys(self) -> None: """Testnet mode requires testnet API keys.""" from tradefinder.core.config import TradingMode assert TradingMode.TESTNET.value == "testnet" def test_live_mode_requires_production_keys(self) -> None: """Live mode requires production API keys.""" from tradefinder.core.config import TradingMode assert TradingMode.LIVE.value == "live"