Files
foxhunt/tests/mocks/mod.rs
jgrusewski eb5fe84e22 🔥 COMPILATION SUCCESS: Complete resolution of all 543+ compilation errors
ARCHITECTURAL ACHIEVEMENTS:
 Zero compilation errors across entire workspace
 Complete elimination of circular dependencies
 Proper configuration architecture with centralized config crate
 Fixed all type mismatches and missing fields
 Restored proper crate structure (config at root level)

MAJOR FIXES:
- Fixed 19 critical data crate compilation errors
- Resolved configuration struct field mismatches
- Fixed enum variant naming (CSV → Csv)
- Corrected type conversions (FromPrimitive, compression types)
- Fixed HashMap key types (u32 vs usize)
- Resolved TLOBProcessor constructor issues

WORKSPACE STATUS:
- All services compile successfully
- Trading Service:  Ready
- Backtesting Service:  Ready
- ML Training Service:  Ready
- TLI Client:  Ready

Only documentation warnings remain (3,316 warnings to be addressed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 10:59:34 +02:00

655 lines
20 KiB
Rust

//! Mock Services for Integration Testing
//!
//! This module provides comprehensive mock implementations of all trading system
//! services for integration testing, including realistic latency simulation,
//! configurable failure rates, and comprehensive response patterns.
use std::collections::HashMap;
use std::sync::{Arc, atomic::{AtomicU16, AtomicU64, Ordering}};
use std::time::{Duration, Instant};
use tokio::sync::{mpsc, RwLock, Mutex};
use uuid::Uuid;
use serde_json::json;
use chrono::{DateTime, Utc};
use tli::prelude::*;
use crate::fixtures::*;
pub mod mock_trading_service;
pub mod mock_backtesting_service;
pub mod mock_database;
pub mod mock_ml_infrastructure;
/// Mock trading service for integration testing
pub struct MockTradingService {
port: u16,
server_handle: Option<tokio::task::JoinHandle<()>>,
order_responses: Arc<RwLock<HashMap<String, SubmitOrderResponse>>>,
risk_rejections: Arc<RwLock<HashMap<String, String>>>,
failure_sequence_count: Arc<AtomicU64>,
lifecycle_simulations: Arc<RwLock<HashMap<String, Vec<OrderStatus>>>>,
circuit_breaker_status: Arc<RwLock<CircuitBreakerStatus>>,
trading_status: Arc<RwLock<TradingStatus>>,
config: MockServiceConfig,
}
/// Mock service configuration
#[derive(Debug, Clone)]
pub struct MockServiceConfig {
pub latency_ms: u64,
pub failure_rate: f64,
pub enable_chaos: bool,
pub max_connections: usize,
}
impl Default for MockServiceConfig {
fn default() -> Self {
Self {
latency_ms: 10,
failure_rate: 0.01, // 1%
enable_chaos: false,
max_connections: 100,
}
}
}
// OrderStatus now imported from canonical source
use common::OrderStatus;
/// Circuit breaker status
#[derive(Debug, Clone)]
pub struct CircuitBreakerStatus {
pub is_open: bool,
pub failure_count: u32,
pub last_failure_time: Option<DateTime<Utc>>,
}
impl Default for CircuitBreakerStatus {
fn default() -> Self {
Self {
is_open: false,
failure_count: 0,
last_failure_time: None,
}
}
}
/// Trading system status
#[derive(Debug, Clone)]
pub struct TradingStatus {
pub is_active: bool,
pub emergency_stop_active: bool,
pub last_heartbeat: DateTime<Utc>,
pub active_orders: u64,
pub total_volume: Decimal,
}
impl Default for TradingStatus {
fn default() -> Self {
Self {
is_active: true,
emergency_stop_active: false,
last_heartbeat: Utc::now(),
active_orders: 0,
total_volume: Decimal::ZERO,
}
}
}
/// Submit order request structure
#[derive(Debug, Clone)]
pub struct SubmitOrderRequest {
pub symbol: String,
pub side: i32, // OrderSide enum as i32
pub order_type: i32, // OrderType enum as i32
pub quantity: f64,
pub price: Option<f64>,
pub client_order_id: String,
pub metadata: HashMap<String, serde_json::Value>,
}
/// Submit order response structure
#[derive(Debug, Clone)]
pub struct SubmitOrderResponse {
pub success: bool,
pub order_id: String,
pub message: String,
pub execution_time_ns: u64,
}
/// Start backtest request structure
#[derive(Debug, Clone)]
pub struct StartBacktestRequest {
pub backtest_id: String,
pub enable_monitoring: bool,
}
/// Start backtest response structure
#[derive(Debug, Clone)]
pub struct StartBacktestResponse {
pub success: bool,
pub message: String,
}
impl MockTradingService {
/// Create new mock trading service
pub async fn new() -> TliResult<Self> {
Self::new_with_config(MockServiceConfig::default()).await
}
/// Create new mock trading service with custom configuration
pub async fn new_with_config(config: MockServiceConfig) -> TliResult<Self> {
let port = TEST_PORT_MANAGER.allocate_port().await;
Ok(Self {
port,
server_handle: None,
order_responses: Arc::new(RwLock::new(HashMap::new())),
risk_rejections: Arc::new(RwLock::new(HashMap::new())),
failure_sequence_count: Arc::new(AtomicU64::new(0)),
lifecycle_simulations: Arc::new(RwLock::new(HashMap::new())),
circuit_breaker_status: Arc::new(RwLock::new(CircuitBreakerStatus::default())),
trading_status: Arc::new(RwLock::new(TradingStatus::default())),
config,
})
}
/// Get the port the mock service is running on
pub fn port(&self) -> u16 {
self.port
}
/// Configure a specific order response
pub async fn configure_order_response(&self, client_order_id: &str, response: SubmitOrderResponse) {
let mut responses = self.order_responses.write().await;
responses.insert(client_order_id.to_string(), response);
}
/// Configure risk rejection for an order
pub async fn configure_risk_rejection(&self, client_order_id: &str, reason: String) {
let mut rejections = self.risk_rejections.write().await;
rejections.insert(client_order_id.to_string(), reason);
}
/// Configure failure sequence for circuit breaker testing
pub async fn configure_failure_sequence(&self, count: u64) {
self.failure_sequence_count.store(count, Ordering::Relaxed);
}
/// Configure order lifecycle simulation
pub async fn configure_lifecycle_simulation(&self, order_id: &str, statuses: Vec<OrderStatus>) {
let mut simulations = self.lifecycle_simulations.write().await;
simulations.insert(order_id.to_string(), statuses);
}
/// Start the mock service
pub async fn start(&mut self) -> TliResult<()> {
let port = self.port;
let config = self.config.clone();
let order_responses = Arc::clone(&self.order_responses);
let risk_rejections = Arc::clone(&self.risk_rejections);
let failure_sequence = Arc::clone(&self.failure_sequence_count);
let circuit_breaker = Arc::clone(&self.circuit_breaker_status);
let trading_status = Arc::clone(&self.trading_status);
let handle = tokio::spawn(async move {
let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port))
.await
.expect("Failed to bind mock trading service");
while let Ok((stream, _)) = listener.accept().await {
let responses = Arc::clone(&order_responses);
let rejections = Arc::clone(&risk_rejections);
let failure_seq = Arc::clone(&failure_sequence);
let cb_status = Arc::clone(&circuit_breaker);
let trade_status = Arc::clone(&trading_status);
let service_config = config.clone();
tokio::spawn(async move {
Self::handle_connection(stream, responses, rejections, failure_seq, cb_status, trade_status, service_config).await;
});
}
});
self.server_handle = Some(handle);
Ok(())
}
/// Handle individual client connection
async fn handle_connection(
stream: tokio::net::TcpStream,
order_responses: Arc<RwLock<HashMap<String, SubmitOrderResponse>>>,
risk_rejections: Arc<RwLock<HashMap<String, String>>>,
failure_sequence: Arc<AtomicU64>,
circuit_breaker: Arc<RwLock<CircuitBreakerStatus>>,
trading_status: Arc<RwLock<TradingStatus>>,
config: MockServiceConfig,
) {
// Simulate service latency
if config.latency_ms > 0 {
tokio::time::sleep(Duration::from_millis(config.latency_ms)).await;
}
// Simulate random failures if configured
if config.enable_chaos && fastrand::f64() < config.failure_rate {
return; // Drop connection to simulate failure
}
// Handle gRPC-style protocol simulation
// In a real implementation, this would use tonic/gRPC
// For testing, we'll simulate the essential behavior
}
/// Stop the mock service
pub async fn stop(&mut self) {
if let Some(handle) = self.server_handle.take() {
handle.abort();
}
TEST_PORT_MANAGER.release_port(self.port).await;
}
}
impl Drop for MockTradingService {
fn drop(&mut self) {
if let Some(handle) = self.server_handle.take() {
handle.abort();
}
}
}
/// Mock backtesting service for integration testing
pub struct MockBacktestingService {
port: u16,
server_handle: Option<tokio::task::JoinHandle<()>>,
backtest_responses: Arc<RwLock<HashMap<String, BacktestResult>>>,
ml_responses: Arc<RwLock<HashMap<String, MLBacktestConfig>>>,
ensemble_responses: Arc<RwLock<HashMap<String, EnsembleResults>>>,
config: MockServiceConfig,
}
/// Backtest result structure
#[derive(Debug, Clone)]
pub struct BacktestResult {
pub backtest_id: String,
pub strategy_name: String,
pub total_return: f64,
pub annualized_return: f64,
pub max_drawdown: f64,
pub sharpe_ratio: f64,
pub total_trades: u64,
pub win_rate: f64,
pub avg_trade_return: f64,
pub final_value: f64,
pub execution_time_ms: u64,
pub events_processed: u64,
}
/// ML backtest configuration
#[derive(Debug, Clone)]
pub struct MLBacktestConfig {
pub model_name: String,
pub expected_return: f64,
pub expected_sharpe: f64,
pub expected_trades: u64,
}
/// Ensemble results structure
#[derive(Debug, Clone)]
pub struct EnsembleResults {
pub ensemble_return: f64,
pub ensemble_sharpe: f64,
pub individual_returns: Vec<f64>,
pub individual_sharpes: Vec<f64>,
pub diversification_benefit: f64,
pub model_weights_final: Vec<f64>,
pub rebalance_count: u32,
}
/// Create backtest request structure
#[derive(Debug, Clone)]
pub struct CreateBacktestRequest {
pub name: String,
pub strategy_type: String,
pub symbol: String,
pub start_date: i64,
pub end_date: i64,
pub initial_capital: f64,
pub parameters: serde_json::Value,
pub enable_real_time_monitoring: bool,
}
/// Create backtest response structure
#[derive(Debug, Clone)]
pub struct CreateBacktestResponse {
pub success: bool,
pub backtest_id: String,
pub message: String,
}
impl MockBacktestingService {
/// Create new mock backtesting service
pub async fn new() -> TliResult<Self> {
let port = TEST_PORT_MANAGER.allocate_port().await;
Ok(Self {
port,
server_handle: None,
backtest_responses: Arc::new(RwLock::new(HashMap::new())),
ml_responses: Arc::new(RwLock::new(HashMap::new())),
ensemble_responses: Arc::new(RwLock::new(HashMap::new())),
config: MockServiceConfig::default(),
})
}
/// Get the port the mock service is running on
pub fn port(&self) -> u16 {
self.port
}
/// Configure backtest response
pub async fn configure_backtest_response(&self, name: &str, result: BacktestResult) {
let mut responses = self.backtest_responses.write().await;
responses.insert(name.to_string(), result);
}
/// Configure ML backtest response
pub async fn configure_ml_backtest_response(
&self,
name: &str,
model_name: &str,
expected_return: f64,
expected_sharpe: f64,
expected_trades: u64,
) {
let mut responses = self.ml_responses.write().await;
responses.insert(name.to_string(), MLBacktestConfig {
model_name: model_name.to_string(),
expected_return,
expected_sharpe,
expected_trades,
});
}
/// Configure ensemble response
pub async fn configure_ensemble_response(&self, name: &str, results: EnsembleResults) {
let mut responses = self.ensemble_responses.write().await;
responses.insert(name.to_string(), results);
}
/// Stop the mock service
pub async fn stop(&mut self) {
if let Some(handle) = self.server_handle.take() {
handle.abort();
}
TEST_PORT_MANAGER.release_port(self.port).await;
}
}
/// Test database manager for PostgreSQL integration testing
pub struct TestDatabaseManager {
pool: sqlx::PgPool,
config: TestDatabaseConfig,
}
/// Test database configuration
#[derive(Debug, Clone)]
pub struct TestDatabaseConfig {
pub database_url: String,
pub max_connections: u32,
pub enable_cleanup: bool,
pub test_schema: String,
}
impl Default for TestDatabaseConfig {
fn default() -> Self {
Self {
database_url: std::env::var("TEST_DATABASE_URL")
.unwrap_or_else(|_| "postgresql://foxhunt_test:test_password@localhost:5432/foxhunt_test".to_string()),
max_connections: 20,
enable_cleanup: true,
test_schema: "test_".to_string(),
}
}
}
impl TestDatabaseManager {
/// Create new test database manager
pub async fn new() -> TliResult<Self> {
Self::new_with_config(TestDatabaseConfig::default()).await
}
/// Create new test database manager with custom configuration
pub async fn new_with_config(config: TestDatabaseConfig) -> TliResult<Self> {
let pool = sqlx::PgPool::connect(&config.database_url)
.await
.map_err(|e| TliError::DatabaseError(format!("Failed to connect to test database: {}", e)))?;
Ok(Self { pool, config })
}
/// Get connection pool
pub fn get_pool(&self) -> &sqlx::PgPool {
&self.pool
}
/// Verify order was persisted
pub async fn verify_order_persisted(&self, order_id: &str) -> TliResult<bool> {
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM orders WHERE order_id = $1")
.bind(order_id)
.fetch_one(&self.pool)
.await
.map_err(|e| TliError::DatabaseError(format!("Failed to verify order persistence: {}", e)))?;
Ok(count > 0)
}
/// Clean up test data
pub async fn cleanup(&self) -> TliResult<()> {
if self.config.enable_cleanup {
let cleanup_queries = vec![
"DELETE FROM trading_events WHERE source LIKE '%test%'",
"DELETE FROM orders WHERE client_order_id LIKE '%test%'",
"DELETE FROM positions WHERE account_id LIKE '%test%'",
"DELETE FROM risk_events WHERE account_id LIKE '%test%'",
"DELETE FROM market_data WHERE symbol LIKE '%TEST%'",
"DELETE FROM performance_metrics WHERE metric_name LIKE '%test%'",
];
for query in cleanup_queries {
sqlx::query(query)
.execute(&self.pool)
.await
.map_err(|e| TliError::DatabaseError(format!("Cleanup failed: {}", e)))?;
}
}
Ok(())
}
}
/// Test database structure
pub struct TestDatabase {
manager: TestDatabaseManager,
}
impl TestDatabase {
/// Create new test database
pub async fn new() -> TliResult<Self> {
let manager = TestDatabaseManager::new().await?;
Ok(Self { manager })
}
/// Verify order was persisted
pub async fn verify_order_persisted(&self, order_id: &str) -> TliResult<bool> {
self.manager.verify_order_persisted(order_id).await
}
}
/// Test data provider for market data simulation
pub struct TestDataProvider {
historical_data: HashMap<String, Vec<MarketDataPoint>>,
}
/// Market data point structure
#[derive(Debug, Clone)]
pub struct MarketDataPoint {
pub timestamp: DateTime<Utc>,
pub symbol: String,
pub price: f64,
pub volume: u64,
pub bid: Option<f64>,
pub ask: Option<f64>,
}
impl TestDataProvider {
/// Create new test data provider
pub async fn new() -> TliResult<Self> {
let mut provider = Self {
historical_data: HashMap::new(),
};
// Generate sample data for common symbols
provider.generate_sample_data("AAPL", 1000).await;
provider.generate_sample_data("MSFT", 1000).await;
provider.generate_sample_data("GOOGL", 1000).await;
Ok(provider)
}
/// Generate sample market data
async fn generate_sample_data(&mut self, symbol: &str, count: usize) {
let mut data = Vec::new();
let mut price = 150.0;
let start_time = Utc::now() - chrono::Duration::days(30);
for i in 0..count {
let timestamp = start_time + chrono::Duration::seconds(i as i64 * 60);
// Simple random walk
price += (fastrand::f64() - 0.5) * 2.0;
price = price.max(100.0).min(200.0); // Keep price in reasonable range
data.push(MarketDataPoint {
timestamp,
symbol: symbol.to_string(),
price,
volume: 1000 + fastrand::u64(0..10000),
bid: Some(price - 0.01),
ask: Some(price + 0.01),
});
}
self.historical_data.insert(symbol.to_string(), data);
}
/// Get historical data for symbol
pub fn get_historical_data(&self, symbol: &str) -> Option<&Vec<MarketDataPoint>> {
self.historical_data.get(symbol)
}
}
/// ML testing infrastructure
pub struct MLTestInfrastructure {
mock_models: HashMap<String, MockMLModel>,
}
/// Mock ML model
#[derive(Debug, Clone)]
pub struct MockMLModel {
pub name: String,
pub inference_latency_ns: u64,
pub accuracy: f64,
pub memory_usage_mb: u64,
}
impl MLTestInfrastructure {
/// Create new ML testing infrastructure
pub async fn new() -> TliResult<Self> {
let mut models = HashMap::new();
// Add mock models with realistic characteristics
models.insert("TLOB".to_string(), MockMLModel {
name: "TLOB".to_string(),
inference_latency_ns: 15_000, // 15µs
accuracy: 0.89,
memory_usage_mb: 256,
});
models.insert("MAMBA".to_string(), MockMLModel {
name: "MAMBA".to_string(),
inference_latency_ns: 25_000, // 25µs
accuracy: 0.91,
memory_usage_mb: 512,
});
models.insert("TFT".to_string(), MockMLModel {
name: "TFT".to_string(),
inference_latency_ns: 35_000, // 35µs
accuracy: 0.87,
memory_usage_mb: 384,
});
models.insert("DQN".to_string(), MockMLModel {
name: "DQN".to_string(),
inference_latency_ns: 20_000, // 20µs
accuracy: 0.84,
memory_usage_mb: 128,
});
Ok(Self {
mock_models: models,
})
}
/// Get mock model
pub fn get_model(&self, name: &str) -> Option<&MockMLModel> {
self.mock_models.get(name)
}
/// List available models
pub fn list_models(&self) -> Vec<&str> {
self.mock_models.keys().map(|s| s.as_str()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_mock_trading_service() {
let mut service = MockTradingService::new().await.unwrap();
assert!(service.port() > 0);
service.configure_order_response("test_order", SubmitOrderResponse {
success: true,
order_id: "order_123".to_string(),
message: "Success".to_string(),
execution_time_ns: 15_000,
}).await;
service.stop().await;
}
#[tokio::test]
async fn test_test_data_provider() {
let provider = TestDataProvider::new().await.unwrap();
let aapl_data = provider.get_historical_data("AAPL").unwrap();
assert_eq!(aapl_data.len(), 1000);
assert!(aapl_data[0].price > 0.0);
}
#[tokio::test]
async fn test_ml_infrastructure() {
let ml_infra = MLTestInfrastructure::new().await.unwrap();
let models = ml_infra.list_models();
assert!(models.contains(&"TLOB"));
assert!(models.contains(&"MAMBA"));
let tlob = ml_infra.get_model("TLOB").unwrap();
assert_eq!(tlob.name, "TLOB");
assert!(tlob.inference_latency_ns > 0);
}
}