Files
foxhunt/tli/tests/integration/service_integration_tests.rs
jgrusewski c0be3ca530 🔧 Major compilation fixes across entire workspace - Significant progress achieved
## Summary of Compilation Fixes

### Core Infrastructure Improvements
- **Fixed import system**: Established canonical type imports from common::types
- **Resolved syntax errors**: Fixed malformed use statements with embedded comments
- **Import consolidation**: Eliminated duplicate and conflicting type imports
- **Type visibility**: Improved public/private type access patterns

### Major Areas Fixed

#### Trading Engine (trading_engine/)
-  Fixed syntax errors in types/basic.rs with clean re-exports
-  Resolved OrderSide/Side naming conflicts
-  Fixed type_registry.rs malformed imports
-  Consolidated canonical type imports from common::types
-  Fixed broker_client.rs duplicate OrderStatus imports
- 🔄 Remaining: 41 type visibility errors (down from 286+ errors)

#### Common Types (common/)
-  Established as single source of truth for all types
-  Clean type definitions with proper visibility
-  Consistent error handling patterns

#### Data Pipeline (data/)
-  Updated imports to use canonical common::types
-  Fixed provider trait implementations
-  Resolved database integration issues

#### ML Components (ml/)
-  Fixed model interface imports
-  Updated feature extraction systems
-  Resolved training pipeline dependencies

#### Risk Management (risk/)
-  Fixed safety module imports
-  Updated VaR calculator dependencies
-  Consolidated compliance types

#### Services
-  Trading Service: Fixed repository implementations
-  Backtesting Service: Updated strategy engines
-  TLI: Fixed dashboard and UI components

#### Test Infrastructure
-  Updated integration test imports
-  Fixed performance benchmark dependencies
-  Resolved mock implementations

### Technical Achievements

#### Import System Overhaul
- Established common::types as canonical source
- Eliminated circular dependencies
- Fixed visibility modifiers (pub use vs use)
- Resolved naming conflicts (Side → OrderSide)

#### Type System Cleanup
- Consolidated duplicate type definitions
- Fixed malformed syntax (comments in use statements)
- Standardized error handling patterns
- Improved module structure

#### Configuration Management
- Enhanced config crate integration
- Fixed database configuration patterns
- Improved hot-reload mechanisms

### Error Reduction Progress
- **Before**: 371+ compilation errors across workspace
- **After**: ~202 errors remaining (46% reduction achieved)
- **Major**: Fixed critical syntax errors preventing any compilation
- **Infrastructure**: Resolved fundamental import and type system issues

### Files Modified: 347
- Core types and infrastructure
- Service implementations
- Test suites and benchmarks
- Configuration systems
- Database integrations

### Next Steps
- Complete remaining type visibility fixes in trading_engine
- Finalize import resolution in remaining modules
- Validate cross-crate dependencies
- Run comprehensive test suite

This represents a major milestone in achieving zero compilation errors across
the entire Foxhunt HFT trading system workspace. The foundational type system
and import structure has been successfully established and standardized.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 20:56:22 +02:00

836 lines
27 KiB
Rust

//! Comprehensive service integration tests for TLI system
//!
//! This module tests the communication between TLI client and various services
//! including Trading Service, Risk Management, Market Data, and Backtesting Service.
use fake::faker::company::en::*;
use fake::faker::finance::en::*;
use fake::{Fake, Faker};
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::{sleep, timeout};
use tonic::{Request, Response, Status};
use uuid::Uuid;
use crate::integration::{TestConfig, TestUtilities};
use crate::mocks::grpc_server::{MockBacktestingServer, MockRiskServer, MockTradingServer};
use tli::client::{
BacktestingClient, BacktestingClientConfig, TliClientBuilder, TradingClient,
TradingClientConfig,
};
// Database imports removed - TLI is pure client
use tli::prelude::*;
/// Service integration test suite
pub struct ServiceIntegrationTests {
config: TestConfig,
trading_client: Option<TradingClient>,
backtesting_client: Option<BacktestingClient>,
mock_servers: Vec<Box<dyn MockServer>>,
}
/// Mock server trait for test servers
pub trait MockServer: Send + Sync {
fn start(&mut self) -> TliResult<u16>;
fn stop(&mut self) -> TliResult<()>;
fn is_running(&self) -> bool;
fn get_port(&self) -> Option<u16>;
}
impl ServiceIntegrationTests {
pub fn new(config: TestConfig) -> Self {
Self {
config,
trading_client: None,
backtesting_client: None,
mock_servers: Vec::new(),
}
}
/// Setup test environment with mock services
pub async fn setup(&mut self) -> TliResult<()> {
tracing::info!("Setting up service integration test environment");
// Start mock trading service
let mut trading_server = MockTradingServer::new(self.config.mock_server_port)?;
let trading_port = trading_server.start()?;
self.mock_servers.push(Box::new(trading_server));
// Start mock backtesting service
let mut backtesting_server =
MockBacktestingServer::new(self.config.mock_server_port + 100)?;
let backtesting_port = backtesting_server.start()?;
self.mock_servers.push(Box::new(backtesting_server));
// Wait for services to be ready
sleep(Duration::from_millis(500)).await;
// Create TLI client suite
let client_suite = TliClientBuilder::new()
.with_service_endpoint(
"trading_service".to_string(),
format!("http://localhost:{}", trading_port),
)
.with_service_endpoint(
"backtesting_service".to_string(),
format!("http://localhost:{}", backtesting_port),
)
.with_trading_config(TradingClientConfig::default())
.with_backtesting_config(BacktestingClientConfig::default())
.build()
.await?;
self.trading_client = client_suite.trading_client;
self.backtesting_client = client_suite.backtesting_client;
tracing::info!("Service integration test environment setup complete");
Ok(())
}
/// Cleanup test environment
pub async fn teardown(&mut self) -> TliResult<()> {
tracing::info!("Tearing down service integration test environment");
// Shutdown clients
if let Some(client) = self.trading_client.take() {
client.shutdown().await;
}
if let Some(client) = self.backtesting_client.take() {
client.shutdown().await;
}
// Stop mock servers
for server in &mut self.mock_servers {
let _ = server.stop();
}
self.mock_servers.clear();
tracing::info!("Service integration test environment teardown complete");
Ok(())
}
}
/// Test TLI Client ↔ Trading Service communication
#[cfg(test)]
mod trading_service_tests {
use super::*;
#[tokio::test]
async fn test_order_submission_flow() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test order submission
let order_request = SubmitOrderRequest {
symbol: TestUtilities::generate_test_symbol("AAPL"),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: Some(150.50),
stop_price: None,
time_in_force: "DAY".to_string(),
client_order_id: Uuid::new_v4().to_string(),
};
let result = timeout(
Duration::from_secs(10),
trading_client.submit_order(order_request),
)
.await;
assert!(result.is_ok(), "Order submission timed out");
let response = result.unwrap().expect("Order submission failed");
assert!(response.success, "Order submission was not successful");
assert!(
!response.order_id.is_empty(),
"Order ID should not be empty"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_order_cancellation_flow() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// First submit an order
let order_request = SubmitOrderRequest {
symbol: TestUtilities::generate_test_symbol("GOOGL"),
side: OrderSide::Buy as i32,
order_type: OrderType::Limit as i32,
quantity: 50.0,
price: Some(2800.00),
stop_price: None,
time_in_force: "GTC".to_string(),
client_order_id: Uuid::new_v4().to_string(),
};
let submit_response = trading_client
.submit_order(order_request)
.await
.expect("Failed to submit order");
assert!(submit_response.success, "Order submission failed");
// Then cancel the order
let cancel_request = CancelOrderRequest {
order_id: submit_response.order_id.clone(),
symbol: TestUtilities::generate_test_symbol("GOOGL"),
};
let cancel_response = trading_client
.cancel_order(cancel_request)
.await
.expect("Failed to cancel order");
assert!(cancel_response.success, "Order cancellation failed");
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_position_monitoring() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test position query
let position_request = GetPositionsRequest {
account_id: Some("test_account".to_string()),
symbol_filter: None,
include_zero_positions: false,
};
let positions_response = trading_client
.get_positions(position_request)
.await
.expect("Failed to get positions");
// Validate response structure
assert!(
!positions_response.positions.is_empty(),
"Should have at least mock positions"
);
for position in &positions_response.positions {
assert!(
!position.symbol.is_empty(),
"Position symbol should not be empty"
);
assert!(
position.quantity != 0.0,
"Position quantity should not be zero"
);
}
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_market_data_subscription() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test market data subscription
let subscription_request = MarketDataSubscriptionRequest {
symbols: vec!["AAPL".to_string(), "GOOGL".to_string(), "MSFT".to_string()],
data_types: vec![MarketDataType::Quote as i32, MarketDataType::Trade as i32],
include_level2: false,
};
let mut stream = trading_client
.subscribe_market_data(subscription_request)
.await
.expect("Failed to subscribe to market data");
// Collect a few market data updates
let mut updates_received = 0;
let timeout_duration = Duration::from_secs(5);
let start_time = std::time::Instant::now();
while updates_received < 3 && start_time.elapsed() < timeout_duration {
match timeout(Duration::from_millis(500), stream.recv()).await {
Ok(Some(update)) => {
assert!(
!update.symbol.is_empty(),
"Market data symbol should not be empty"
);
assert!(
update.timestamp > 0,
"Market data timestamp should be valid"
);
updates_received += 1;
tracing::info!("Received market data update for {}", update.symbol);
}
Ok(None) => break,
Err(_) => {
tracing::warn!("Market data update timed out");
break;
}
}
}
assert!(
updates_received > 0,
"Should have received at least one market data update"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_risk_management_validation() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test pre-trade risk validation
let risk_check_request = PreTradeRiskCheckRequest {
symbol: TestUtilities::generate_test_symbol("TSLA"),
side: OrderSide::Buy as i32,
quantity: 1000.0, // Large quantity to trigger risk limits
estimated_price: Some(800.0),
order_type: OrderType::Market as i32,
account_id: "test_account".to_string(),
};
let risk_response = trading_client
.check_pre_trade_risk(risk_check_request)
.await
.expect("Failed to perform risk check");
// Validate risk check response
assert!(
risk_response.risk_score >= 0.0 && risk_response.risk_score <= 1.0,
"Risk score should be between 0 and 1"
);
if !risk_response.approved {
assert!(
!risk_response.rejection_reasons.is_empty(),
"Rejection reasons should be provided when order is not approved"
);
}
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_portfolio_analytics() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test portfolio analytics
let analytics_request = PortfolioAnalyticsRequest {
account_id: "test_account".to_string(),
calculation_date: chrono::Utc::now().timestamp(),
include_realized_pnl: true,
include_unrealized_pnl: true,
include_risk_metrics: true,
};
let analytics_response = trading_client
.get_portfolio_analytics(analytics_request)
.await
.expect("Failed to get portfolio analytics");
// Validate analytics response
assert!(
analytics_response.total_portfolio_value.is_some(),
"Total portfolio value should be provided"
);
assert!(
analytics_response.daily_pnl.is_some(),
"Daily P&L should be provided"
);
assert!(
!analytics_response.position_analytics.is_empty(),
"Position analytics should not be empty"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
}
/// Test TLI Client ↔ Backtesting Service communication
#[cfg(test)]
mod backtesting_service_tests {
use super::*;
#[tokio::test]
async fn test_backtest_execution_flow() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let backtesting_client = test_env
.backtesting_client
.as_ref()
.expect("Backtesting client not available");
// Create backtest configuration
let backtest_request = CreateBacktestRequest {
name: format!("Integration Test Backtest {}", Uuid::new_v4()),
strategy_id: "test_strategy".to_string(),
start_date: "2024-01-01".to_string(),
end_date: "2024-01-31".to_string(),
initial_capital: 100000.0,
symbols: vec!["AAPL".to_string(), "GOOGL".to_string()],
parameters: HashMap::new(),
};
// Start backtest
let create_response = backtesting_client
.create_backtest(backtest_request)
.await
.expect("Failed to create backtest");
assert!(create_response.success, "Backtest creation failed");
assert!(
!create_response.backtest_id.is_empty(),
"Backtest ID should not be empty"
);
let backtest_id = create_response.backtest_id;
// Start backtest execution
let start_request = StartBacktestRequest {
backtest_id: backtest_id.clone(),
async_execution: true,
};
let start_response = backtesting_client
.start_backtest(start_request)
.await
.expect("Failed to start backtest");
assert!(start_response.success, "Backtest start failed");
// Monitor backtest progress
let mut progress_checks = 0;
let max_progress_checks = 10;
while progress_checks < max_progress_checks {
let status_request = GetBacktestStatusRequest {
backtest_id: backtest_id.clone(),
};
let status_response = backtesting_client
.get_backtest_status(status_request)
.await
.expect("Failed to get backtest status");
tracing::info!(
"Backtest status: {:?}, Progress: {}%",
status_response.status,
status_response.progress_percentage
);
if status_response.status == BacktestStatus::Completed as i32 {
break;
}
progress_checks += 1;
sleep(Duration::from_millis(100)).await;
}
// Get backtest results
let results_request = GetBacktestResultsRequest {
backtest_id: backtest_id.clone(),
include_trades: true,
include_metrics: true,
};
let results_response = backtesting_client
.get_backtest_results(results_request)
.await
.expect("Failed to get backtest results");
// Validate results
assert!(
results_response.performance_summary.is_some(),
"Performance summary should be provided"
);
assert!(
!results_response.trades.is_empty(),
"Should have executed some trades"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_strategy_optimization() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let backtesting_client = test_env
.backtesting_client
.as_ref()
.expect("Backtesting client not available");
// Create optimization request
let optimization_request = StrategyOptimizationRequest {
strategy_id: "test_strategy".to_string(),
start_date: "2024-01-01".to_string(),
end_date: "2024-03-31".to_string(),
symbols: vec!["AAPL".to_string()],
parameter_ranges: create_test_parameter_ranges(),
optimization_metric: OptimizationMetric::SharpeRatio as i32,
max_iterations: 50,
};
// Start optimization
let optimization_response = backtesting_client
.optimize_strategy(optimization_request)
.await
.expect("Failed to start strategy optimization");
assert!(
optimization_response.success,
"Strategy optimization start failed"
);
assert!(
!optimization_response.optimization_id.is_empty(),
"Optimization ID should not be empty"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_backtest_data_management() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let backtesting_client = test_env
.backtesting_client
.as_ref()
.expect("Backtesting client not available");
// Test data availability check
let data_request = CheckDataAvailabilityRequest {
symbols: vec!["AAPL".to_string(), "GOOGL".to_string(), "MSFT".to_string()],
start_date: "2024-01-01".to_string(),
end_date: "2024-01-31".to_string(),
data_types: vec![DataType::OHLCV as i32, DataType::Trades as i32],
};
let data_response = backtesting_client
.check_data_availability(data_request)
.await
.expect("Failed to check data availability");
// Validate data availability response
assert!(
!data_response.symbol_availability.is_empty(),
"Symbol availability should not be empty"
);
for (symbol, availability) in &data_response.symbol_availability {
assert!(!symbol.is_empty(), "Symbol should not be empty");
assert!(
availability.coverage_percentage >= 0.0
&& availability.coverage_percentage <= 100.0,
"Coverage percentage should be between 0 and 100"
);
}
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
fn create_test_parameter_ranges() -> HashMap<String, ParameterRange> {
let mut ranges = HashMap::new();
ranges.insert(
"lookback_period".to_string(),
ParameterRange {
min_value: 5.0,
max_value: 50.0,
step_size: 5.0,
parameter_type: ParameterType::Integer as i32,
},
);
ranges.insert(
"threshold".to_string(),
ParameterRange {
min_value: 0.01,
max_value: 0.1,
step_size: 0.01,
parameter_type: ParameterType::Float as i32,
},
);
ranges
}
}
/// Configuration management integration tests
#[cfg(test)]
mod config_integration_tests {
use super::*;
#[tokio::test]
async fn test_configuration_lifecycle() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test configuration retrieval
let get_config_request = GetConfigurationRequest {
config_type: ConfigurationType::TradingLimits as i32,
account_id: Some("test_account".to_string()),
};
let config_response = trading_client
.get_configuration(get_config_request)
.await
.expect("Failed to get configuration");
assert!(
!config_response.configurations.is_empty(),
"Should have configurations"
);
// Test configuration update
let updated_config = Configuration {
id: config_response.configurations[0].id.clone(),
config_type: ConfigurationType::TradingLimits as i32,
key: "max_position_size".to_string(),
value: "1000000".to_string(),
account_id: Some("test_account".to_string()),
last_updated: chrono::Utc::now().timestamp(),
};
let update_request = UpdateConfigurationRequest {
configurations: vec![updated_config],
validate_before_update: true,
};
let update_response = trading_client
.update_configuration(update_request)
.await
.expect("Failed to update configuration");
assert!(
update_response.success,
"Configuration update should succeed"
);
assert!(
update_response.validation_errors.is_empty(),
"Should have no validation errors"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
#[tokio::test]
async fn test_hot_reload_configuration() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Test hot reload trigger
let reload_request = TriggerConfigReloadRequest {
services: vec!["trading_service".to_string(), "risk_service".to_string()],
config_types: vec![ConfigurationType::TradingLimits as i32],
};
let reload_response = trading_client
.trigger_config_reload(reload_request)
.await
.expect("Failed to trigger config reload");
assert!(reload_response.success, "Config reload should succeed");
for result in &reload_response.service_results {
assert!(result.success, "Each service reload should succeed");
}
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
}
/// Event streaming integration tests
#[cfg(test)]
mod event_streaming_tests {
use super::*;
#[tokio::test]
async fn test_event_streaming_lifecycle() {
let mut test_env = ServiceIntegrationTests::new(TestConfig::default());
test_env
.setup()
.await
.expect("Failed to setup test environment");
let trading_client = test_env
.trading_client
.as_ref()
.expect("Trading client not available");
// Subscribe to events
let subscription_request = EventSubscriptionRequest {
event_types: vec![
EventType::OrderExecuted as i32,
EventType::PositionChanged as i32,
EventType::RiskLimitBreached as i32,
],
filters: HashMap::new(),
};
let mut event_stream = trading_client
.subscribe_to_events(subscription_request)
.await
.expect("Failed to subscribe to events");
// Generate some events by placing orders
let order_request = SubmitOrderRequest {
symbol: TestUtilities::generate_test_symbol("AAPL"),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: None,
stop_price: None,
time_in_force: "DAY".to_string(),
client_order_id: Uuid::new_v4().to_string(),
};
// Submit order to generate events
let _order_response = trading_client
.submit_order(order_request)
.await
.expect("Failed to submit order");
// Collect events
let mut events_received = 0;
let timeout_duration = Duration::from_secs(5);
let start_time = std::time::Instant::now();
while events_received < 2 && start_time.elapsed() < timeout_duration {
match timeout(Duration::from_millis(500), event_stream.recv()).await {
Ok(Some(event)) => {
assert!(!event.event_id.is_empty(), "Event ID should not be empty");
assert!(event.timestamp > 0, "Event timestamp should be valid");
events_received += 1;
tracing::info!("Received event: {:?}", event.event_type);
}
Ok(None) => break,
Err(_) => {
tracing::warn!("Event stream timed out");
break;
}
}
}
assert!(
events_received > 0,
"Should have received at least one event"
);
test_env
.teardown()
.await
.expect("Failed to teardown test environment");
}
}