385 lines
14 KiB
Python
385 lines
14 KiB
Python
"""
|
|
Integration tests for COBY orchestrator compatibility.
|
|
Tests the adapter's compatibility with the existing orchestrator interface.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import pytest
|
|
from datetime import datetime, timedelta
|
|
from unittest.mock import Mock, AsyncMock
|
|
|
|
from ..integration.orchestrator_adapter import COBYOrchestratorAdapter, MarketTick
|
|
from ..integration.data_provider_replacement import COBYDataProvider
|
|
from ..config import Config
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TestOrchestratorIntegration:
|
|
"""Test suite for orchestrator integration."""
|
|
|
|
@pytest.fixture
|
|
async def adapter(self):
|
|
"""Create adapter instance for testing."""
|
|
config = Config()
|
|
adapter = COBYOrchestratorAdapter(config)
|
|
|
|
# Mock the storage manager for testing
|
|
adapter.storage_manager = Mock()
|
|
adapter.storage_manager.initialize = AsyncMock()
|
|
adapter.storage_manager.is_healthy = Mock(return_value=True)
|
|
adapter.storage_manager.get_latest_orderbook = AsyncMock(return_value={
|
|
'symbol': 'BTCUSDT',
|
|
'timestamp': datetime.utcnow(),
|
|
'mid_price': 50000.0,
|
|
'spread': 0.01,
|
|
'bid_volume': 10.5,
|
|
'ask_volume': 8.3,
|
|
'exchange': 'binance'
|
|
})
|
|
adapter.storage_manager.get_historical_data = AsyncMock(return_value=[
|
|
{
|
|
'timestamp': datetime.utcnow() - timedelta(minutes=i),
|
|
'open': 50000 + i,
|
|
'high': 50010 + i,
|
|
'low': 49990 + i,
|
|
'close': 50005 + i,
|
|
'volume': 100 + i,
|
|
'symbol': 'BTCUSDT',
|
|
'exchange': 'binance'
|
|
}
|
|
for i in range(100)
|
|
])
|
|
|
|
# Mock Redis manager
|
|
adapter.redis_manager = Mock()
|
|
adapter.redis_manager.initialize = AsyncMock()
|
|
adapter.redis_manager.get = AsyncMock(return_value=None)
|
|
adapter.redis_manager.set = AsyncMock()
|
|
|
|
# Mock connectors
|
|
adapter.connectors = {'binance': Mock()}
|
|
adapter.connectors['binance'].connect = AsyncMock()
|
|
adapter.connectors['binance'].is_connected = True
|
|
|
|
await adapter._initialize_components()
|
|
return adapter
|
|
|
|
@pytest.fixture
|
|
async def data_provider(self):
|
|
"""Create data provider replacement for testing."""
|
|
# Mock the adapter initialization
|
|
provider = COBYDataProvider()
|
|
|
|
# Use the same mocks as adapter
|
|
provider.adapter.storage_manager = Mock()
|
|
provider.adapter.storage_manager.get_latest_orderbook = AsyncMock(return_value={
|
|
'symbol': 'BTCUSDT',
|
|
'timestamp': datetime.utcnow(),
|
|
'mid_price': 50000.0,
|
|
'spread': 0.01,
|
|
'bid_volume': 10.5,
|
|
'ask_volume': 8.3,
|
|
'exchange': 'binance'
|
|
})
|
|
|
|
return provider
|
|
|
|
async def test_adapter_initialization(self, adapter):
|
|
"""Test adapter initializes correctly."""
|
|
assert adapter is not None
|
|
assert adapter.mode == 'live'
|
|
assert adapter.config is not None
|
|
assert 'binance' in adapter.connectors
|
|
|
|
async def test_get_current_price(self, adapter):
|
|
"""Test getting current price."""
|
|
price = adapter.get_current_price('BTCUSDT')
|
|
assert price == 50000.0
|
|
|
|
async def test_get_historical_data(self, adapter):
|
|
"""Test getting historical data."""
|
|
df = adapter.get_historical_data('BTCUSDT', '1m', limit=50)
|
|
|
|
assert df is not None
|
|
assert len(df) == 100 # Mock returns 100 records
|
|
assert 'open' in df.columns
|
|
assert 'high' in df.columns
|
|
assert 'low' in df.columns
|
|
assert 'close' in df.columns
|
|
assert 'volume' in df.columns
|
|
|
|
async def test_build_base_data_input(self, adapter):
|
|
"""Test building base data input."""
|
|
base_data = adapter.build_base_data_input('BTCUSDT')
|
|
|
|
assert base_data is not None
|
|
assert hasattr(base_data, 'get_feature_vector')
|
|
|
|
features = base_data.get_feature_vector()
|
|
assert isinstance(features, type(features)) # numpy array
|
|
assert len(features) == 100 # Expected feature size
|
|
|
|
async def test_cob_data_methods(self, adapter):
|
|
"""Test COB data access methods."""
|
|
# Mock COB data
|
|
adapter.storage_manager.get_historical_data = AsyncMock(return_value=[
|
|
{
|
|
'symbol': 'BTCUSDT',
|
|
'timestamp': datetime.utcnow(),
|
|
'mid_price': 50000.0,
|
|
'spread': 0.01,
|
|
'bid_volume': 10.5,
|
|
'ask_volume': 8.3,
|
|
'exchange': 'binance'
|
|
}
|
|
])
|
|
|
|
# Test raw ticks
|
|
raw_ticks = adapter.get_cob_raw_ticks('BTCUSDT', count=10)
|
|
assert isinstance(raw_ticks, list)
|
|
|
|
# Test latest COB data
|
|
latest_cob = adapter.get_latest_cob_data('BTCUSDT')
|
|
assert latest_cob is not None
|
|
assert latest_cob['symbol'] == 'BTCUSDT'
|
|
assert 'mid_price' in latest_cob
|
|
|
|
async def test_subscription_management(self, adapter):
|
|
"""Test subscription methods."""
|
|
callback_called = False
|
|
received_tick = None
|
|
|
|
def tick_callback(tick):
|
|
nonlocal callback_called, received_tick
|
|
callback_called = True
|
|
received_tick = tick
|
|
|
|
# Subscribe to ticks
|
|
subscriber_id = adapter.subscribe_to_ticks(
|
|
tick_callback,
|
|
symbols=['BTCUSDT'],
|
|
subscriber_name='test_subscriber'
|
|
)
|
|
|
|
assert subscriber_id != ""
|
|
assert len(adapter.subscribers['ticks']) == 1
|
|
|
|
# Simulate tick notification
|
|
test_tick = MarketTick(
|
|
symbol='BTCUSDT',
|
|
price=50000.0,
|
|
volume=1.5,
|
|
timestamp=datetime.utcnow(),
|
|
exchange='binance'
|
|
)
|
|
|
|
await adapter._notify_tick_subscribers(test_tick)
|
|
|
|
assert callback_called
|
|
assert received_tick is not None
|
|
assert received_tick.symbol == 'BTCUSDT'
|
|
|
|
# Unsubscribe
|
|
success = adapter.unsubscribe(subscriber_id)
|
|
assert success
|
|
assert len(adapter.subscribers['ticks']) == 0
|
|
|
|
async def test_mode_switching(self, adapter):
|
|
"""Test switching between live and replay modes."""
|
|
# Initially in live mode
|
|
assert adapter.get_current_mode() == 'live'
|
|
|
|
# Mock replay manager
|
|
adapter.replay_manager = Mock()
|
|
adapter.replay_manager.create_replay_session = Mock(return_value='test_session_123')
|
|
adapter.replay_manager.add_data_callback = Mock()
|
|
adapter.replay_manager.start_replay = AsyncMock()
|
|
adapter.replay_manager.stop_replay = AsyncMock()
|
|
|
|
# Switch to replay mode
|
|
start_time = datetime.utcnow() - timedelta(hours=1)
|
|
end_time = datetime.utcnow()
|
|
|
|
success = await adapter.switch_to_replay_mode(
|
|
start_time=start_time,
|
|
end_time=end_time,
|
|
speed=2.0,
|
|
symbols=['BTCUSDT']
|
|
)
|
|
|
|
assert success
|
|
assert adapter.get_current_mode() == 'replay'
|
|
assert adapter.current_replay_session == 'test_session_123'
|
|
|
|
# Switch back to live mode
|
|
success = await adapter.switch_to_live_mode()
|
|
assert success
|
|
assert adapter.get_current_mode() == 'live'
|
|
assert adapter.current_replay_session is None
|
|
|
|
async def test_data_quality_indicators(self, adapter):
|
|
"""Test data quality indicators."""
|
|
quality = adapter.get_data_quality_indicators('BTCUSDT')
|
|
|
|
assert quality is not None
|
|
assert quality['symbol'] == 'BTCUSDT'
|
|
assert 'quality_score' in quality
|
|
assert 'timestamp' in quality
|
|
assert isinstance(quality['quality_score'], float)
|
|
assert 0.0 <= quality['quality_score'] <= 1.0
|
|
|
|
async def test_system_metadata(self, adapter):
|
|
"""Test system metadata retrieval."""
|
|
metadata = adapter.get_system_metadata()
|
|
|
|
assert metadata is not None
|
|
assert metadata['system'] == 'COBY'
|
|
assert metadata['version'] == '1.0.0'
|
|
assert 'mode' in metadata
|
|
assert 'components' in metadata
|
|
assert 'statistics' in metadata
|
|
|
|
async def test_data_provider_compatibility(self, data_provider):
|
|
"""Test data provider replacement compatibility."""
|
|
# Test core methods exist and work
|
|
assert hasattr(data_provider, 'get_historical_data')
|
|
assert hasattr(data_provider, 'get_current_price')
|
|
assert hasattr(data_provider, 'build_base_data_input')
|
|
assert hasattr(data_provider, 'subscribe_to_ticks')
|
|
assert hasattr(data_provider, 'get_cob_raw_ticks')
|
|
|
|
# Test current price
|
|
price = data_provider.get_current_price('BTCUSDT')
|
|
assert price == 50000.0
|
|
|
|
# Test COB imbalance
|
|
imbalance = data_provider.get_current_cob_imbalance('BTCUSDT')
|
|
assert 'bid_volume' in imbalance
|
|
assert 'ask_volume' in imbalance
|
|
assert 'imbalance' in imbalance
|
|
|
|
# Test WebSocket status
|
|
status = data_provider.get_cob_websocket_status()
|
|
assert 'connected' in status
|
|
assert 'exchanges' in status
|
|
|
|
async def test_error_handling(self, adapter):
|
|
"""Test error handling in various scenarios."""
|
|
# Test with invalid symbol
|
|
price = adapter.get_current_price('INVALID_SYMBOL')
|
|
# Should not raise exception, may return None
|
|
|
|
# Test with storage error
|
|
adapter.storage_manager.get_latest_orderbook = AsyncMock(side_effect=Exception("Storage error"))
|
|
|
|
price = adapter.get_current_price('BTCUSDT')
|
|
# Should handle error gracefully
|
|
|
|
# Test subscription with invalid callback
|
|
subscriber_id = adapter.subscribe_to_ticks(None, ['BTCUSDT'])
|
|
# Should handle gracefully
|
|
|
|
async def test_performance_metrics(self, adapter):
|
|
"""Test performance metrics and statistics."""
|
|
# Get initial stats
|
|
initial_stats = adapter.get_stats()
|
|
assert 'ticks_processed' in initial_stats
|
|
assert 'orderbooks_processed' in initial_stats
|
|
|
|
# Simulate some data processing
|
|
from ..models.core import OrderBookSnapshot, PriceLevel
|
|
|
|
test_orderbook = OrderBookSnapshot(
|
|
symbol='BTCUSDT',
|
|
exchange='binance',
|
|
timestamp=datetime.utcnow(),
|
|
bids=[PriceLevel(price=49999.0, size=1.5)],
|
|
asks=[PriceLevel(price=50001.0, size=1.2)]
|
|
)
|
|
|
|
await adapter._handle_connector_data(test_orderbook)
|
|
|
|
# Check stats updated
|
|
updated_stats = adapter.get_stats()
|
|
assert updated_stats['orderbooks_processed'] >= initial_stats['orderbooks_processed']
|
|
|
|
|
|
async def test_integration_suite():
|
|
"""Run the complete integration test suite."""
|
|
logger.info("Starting COBY orchestrator integration tests...")
|
|
|
|
try:
|
|
# Create test instances
|
|
config = Config()
|
|
adapter = COBYOrchestratorAdapter(config)
|
|
|
|
# Mock components for testing
|
|
adapter.storage_manager = Mock()
|
|
adapter.storage_manager.initialize = AsyncMock()
|
|
adapter.storage_manager.is_healthy = Mock(return_value=True)
|
|
adapter.redis_manager = Mock()
|
|
adapter.redis_manager.initialize = AsyncMock()
|
|
adapter.connectors = {'binance': Mock()}
|
|
adapter.connectors['binance'].connect = AsyncMock()
|
|
|
|
await adapter._initialize_components()
|
|
|
|
# Run basic functionality tests
|
|
logger.info("Testing basic functionality...")
|
|
|
|
# Test price retrieval
|
|
adapter.storage_manager.get_latest_orderbook = AsyncMock(return_value={
|
|
'symbol': 'BTCUSDT',
|
|
'timestamp': datetime.utcnow(),
|
|
'mid_price': 50000.0,
|
|
'spread': 0.01,
|
|
'bid_volume': 10.5,
|
|
'ask_volume': 8.3,
|
|
'exchange': 'binance'
|
|
})
|
|
|
|
price = adapter.get_current_price('BTCUSDT')
|
|
assert price == 50000.0
|
|
logger.info(f"✓ Current price retrieval: {price}")
|
|
|
|
# Test subscription
|
|
callback_called = False
|
|
|
|
def test_callback(tick):
|
|
nonlocal callback_called
|
|
callback_called = True
|
|
|
|
subscriber_id = adapter.subscribe_to_ticks(test_callback, ['BTCUSDT'])
|
|
assert subscriber_id != ""
|
|
logger.info(f"✓ Subscription created: {subscriber_id}")
|
|
|
|
# Test data quality
|
|
quality = adapter.get_data_quality_indicators('BTCUSDT')
|
|
assert quality['symbol'] == 'BTCUSDT'
|
|
logger.info(f"✓ Data quality check: {quality['quality_score']}")
|
|
|
|
# Test system metadata
|
|
metadata = adapter.get_system_metadata()
|
|
assert metadata['system'] == 'COBY'
|
|
logger.info(f"✓ System metadata: {metadata['mode']}")
|
|
|
|
logger.info("All integration tests passed successfully!")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Integration test failed: {e}")
|
|
return False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run the integration tests
|
|
success = asyncio.run(test_integration_suite())
|
|
if success:
|
|
print("✓ COBY orchestrator integration tests completed successfully")
|
|
else:
|
|
print("✗ COBY orchestrator integration tests failed")
|
|
exit(1) |