"""Tests for SignalBridgeActor. Uses TestClock pattern from Nautilus to properly initialize Actor components. """ import pytest from unittest.mock import Mock, patch, PropertyMock from nautilus_dolphin.nautilus.signal_bridge import SignalBridgeActor, SignalBridgeConfig class TestSignalBridgeTimestampParsing: """Test timestamp parsing without full Nautilus initialization.""" def test_parse_timestamp_ns_from_seconds(self): """Test timestamp conversion from seconds.""" config = SignalBridgeConfig(redis_url='redis://localhost') actor = SignalBridgeActor(config) ts_sec = 1234567890 ts_ns = actor._parse_timestamp_ns(ts_sec) assert ts_ns == 1234567890 * 1_000_000_000 def test_parse_timestamp_ns_from_milliseconds(self): """Test timestamp conversion from milliseconds.""" config = SignalBridgeConfig(redis_url='redis://localhost') actor = SignalBridgeActor(config) ts_ms = 1234567890123 ts_ns = actor._parse_timestamp_ns(ts_ms) assert ts_ns == 1234567890123 * 1_000 def test_parse_timestamp_ns_from_nanoseconds(self): """Test timestamp already in nanoseconds.""" config = SignalBridgeConfig(redis_url='redis://localhost') actor = SignalBridgeActor(config) ts_ns = 1234567890123456789 result = actor._parse_timestamp_ns(ts_ns) assert result == ts_ns class TestSignalBridgeWithNautilus: """Test SignalBridgeActor with proper Nautilus initialization.""" @pytest.fixture def nautilus_actor(self): """Create a properly initialized SignalBridgeActor with mocked clock.""" from nautilus_trader.common.component import TestClock config = SignalBridgeConfig( redis_url='redis://localhost', max_signal_age_sec=10 ) actor = SignalBridgeActor(config) # Create TestClock and patch the clock property clock = TestClock() # Use patch to mock the clock property with patch.object( type(actor), 'clock', new_callable=PropertyMock ) as mock_clock: mock_clock.return_value = clock yield actor, clock def test_validate_signal_missing_fields(self, nautilus_actor): """Test signal validation rejects missing fields.""" actor, clock = nautilus_actor signal = {'timestamp': 1234567890, 'asset': 'BTCUSDT'} assert not actor._validate_signal(signal) def test_validate_signal_valid(self, nautilus_actor): """Test signal validation accepts valid signal.""" actor, clock = nautilus_actor # Set clock to be after signal timestamp clock.set_time(1234567890 * 1_000_000_000 + 5_000_000_000) signal = { 'timestamp': 1234567890, 'asset': 'BTCUSDT', 'direction': 'SHORT', 'vel_div': -0.025, 'strength': 0.75 } assert actor._validate_signal(signal) def test_validate_signal_stale(self, nautilus_actor): """Test signal validation rejects stale signals.""" actor, clock = nautilus_actor # Set clock to be much later than signal (older than max_signal_age_sec) clock.set_time(1234567890 * 1_000_000_000 + 20_000_000_000) # 20s later signal = { 'timestamp': 1234567890, 'asset': 'BTCUSDT', 'direction': 'SHORT', 'vel_div': -0.025, 'strength': 0.75 } assert not actor._validate_signal(signal) def test_validate_signal_future(self, nautilus_actor): """Test signal validation rejects future signals.""" actor, clock = nautilus_actor # Set clock to be before signal timestamp clock.set_time(1234567890 * 1_000_000_000 - 1_000_000_000) signal = { 'timestamp': 1234567890, 'asset': 'BTCUSDT', 'direction': 'SHORT', 'vel_div': -0.025, 'strength': 0.75 } assert not actor._validate_signal(signal) class TestSignalBridgeConfig: """Test SignalBridgeConfig initialization.""" def test_default_config(self): """Test default configuration values.""" config = SignalBridgeConfig() assert config.redis_url == "redis://localhost:6379" assert config.stream_key == "dolphin:signals:stream" assert config.max_signal_age_sec == 10 def test_custom_config(self): """Test custom configuration values.""" config = SignalBridgeConfig( redis_url="redis://custom:6380", stream_key="custom:stream", max_signal_age_sec=30 ) assert config.redis_url == "redis://custom:6380" assert config.stream_key == "custom:stream" assert config.max_signal_age_sec == 30