Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
194 lines
5.7 KiB
Rust
194 lines
5.7 KiB
Rust
//! Test Harness for Foxhunt HFT Integration Testing
|
|
//!
|
|
//! Provides utilities for comprehensive end-to-end testing including:
|
|
//! - gRPC service clients
|
|
//! - Test data generation
|
|
//! - Database fixtures
|
|
//! - Performance monitoring
|
|
//! - Service orchestration
|
|
|
|
pub mod grpc_clients;
|
|
pub mod test_data;
|
|
pub mod fixtures;
|
|
pub mod performance;
|
|
pub mod docker_compose;
|
|
pub mod proto;
|
|
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::time::timeout;
|
|
use anyhow::Result;
|
|
|
|
/// Core test harness for managing service lifecycles and test execution
|
|
pub struct TestHarness {
|
|
pub grpc_clients: grpc_clients::GrpcClients,
|
|
pub test_data: test_data::TestDataGenerator,
|
|
pub fixtures: fixtures::TestFixtures,
|
|
pub performance: performance::PerformanceMonitor,
|
|
}
|
|
|
|
impl TestHarness {
|
|
/// Initialize test harness with all required components
|
|
pub async fn new() -> Result<Self> {
|
|
let grpc_clients = grpc_clients::GrpcClients::new().await?;
|
|
let test_data = test_data::TestDataGenerator::new();
|
|
let fixtures = fixtures::TestFixtures::new().await?;
|
|
let performance = performance::PerformanceMonitor::new();
|
|
|
|
Ok(Self {
|
|
grpc_clients,
|
|
test_data,
|
|
fixtures,
|
|
performance,
|
|
})
|
|
}
|
|
|
|
/// Setup test environment with all services
|
|
pub async fn setup(&mut self) -> Result<()> {
|
|
// Start database fixtures
|
|
self.fixtures.setup().await?;
|
|
|
|
// Wait for services to be ready
|
|
self.wait_for_services().await?;
|
|
|
|
// Generate test data
|
|
self.test_data.generate_synthetic_data().await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Cleanup test environment
|
|
pub async fn cleanup(&mut self) -> Result<()> {
|
|
self.fixtures.cleanup().await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Wait for all services to be healthy
|
|
async fn wait_for_services(&self) -> Result<()> {
|
|
let timeout_duration = Duration::from_secs(60);
|
|
|
|
timeout(timeout_duration, async {
|
|
loop {
|
|
if self.grpc_clients.are_all_healthy().await? {
|
|
break;
|
|
}
|
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
|
}
|
|
Ok::<(), anyhow::Error>(())
|
|
}).await??;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Execute end-to-end test scenario with performance monitoring
|
|
pub async fn execute_scenario<F, Fut>(&mut self, name: &str, test_fn: F) -> Result<TestResult>
|
|
where
|
|
F: FnOnce(&mut TestHarness) -> Fut,
|
|
Fut: std::future::Future<Output = Result<()>>,
|
|
{
|
|
let start_time = std::time::Instant::now();
|
|
self.performance.start_scenario(name);
|
|
|
|
let result = match test_fn(self).await {
|
|
Ok(()) => TestResult::Success {
|
|
duration: start_time.elapsed(),
|
|
metrics: self.performance.get_metrics(name),
|
|
},
|
|
Err(e) => TestResult::Failure {
|
|
duration: start_time.elapsed(),
|
|
error: e.to_string(),
|
|
metrics: self.performance.get_metrics(name),
|
|
},
|
|
};
|
|
|
|
self.performance.end_scenario(name);
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
/// Test execution result with performance metrics
|
|
#[derive(Debug)]
|
|
pub enum TestResult {
|
|
Success {
|
|
duration: Duration,
|
|
metrics: performance::ScenarioMetrics,
|
|
},
|
|
Failure {
|
|
duration: Duration,
|
|
error: String,
|
|
metrics: performance::ScenarioMetrics,
|
|
},
|
|
}
|
|
|
|
impl TestResult {
|
|
pub fn is_success(&self) -> bool {
|
|
matches!(self, TestResult::Success { .. })
|
|
}
|
|
|
|
pub fn duration(&self) -> Duration {
|
|
match self {
|
|
TestResult::Success { duration, .. } => *duration,
|
|
TestResult::Failure { duration, .. } => *duration,
|
|
}
|
|
}
|
|
|
|
pub fn metrics(&self) -> &performance::ScenarioMetrics {
|
|
match self {
|
|
TestResult::Success { metrics, .. } => metrics,
|
|
TestResult::Failure { metrics, .. } => metrics,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Test environment configuration
|
|
#[derive(Debug, Clone)]
|
|
pub struct TestConfig {
|
|
pub tli_endpoint: String,
|
|
pub ml_training_endpoint: String,
|
|
pub trading_service_endpoint: String,
|
|
pub database_url: String,
|
|
pub influxdb_url: String,
|
|
pub enable_gpu_tests: bool,
|
|
pub performance_baseline_file: Option<String>,
|
|
}
|
|
|
|
impl Default for TestConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
tli_endpoint: "http://localhost:50051".to_string(),
|
|
ml_training_endpoint: "http://localhost:50052".to_string(),
|
|
trading_service_endpoint: "http://localhost:50053".to_string(),
|
|
database_url: "postgresql://test:test@localhost:5432/foxhunt_test".to_string(),
|
|
influxdb_url: "http://localhost:8086".to_string(),
|
|
enable_gpu_tests: false,
|
|
performance_baseline_file: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Load test configuration from environment or config file
|
|
pub fn load_test_config() -> Result<TestConfig> {
|
|
let mut config = TestConfig::default();
|
|
|
|
// Override with environment variables if present
|
|
if let Ok(endpoint) = std::env::var("TLI_ENDPOINT") {
|
|
config.tli_endpoint = endpoint;
|
|
}
|
|
if let Ok(endpoint) = std::env::var("ML_TRAINING_ENDPOINT") {
|
|
config.ml_training_endpoint = endpoint;
|
|
}
|
|
if let Ok(endpoint) = std::env::var("TRADING_SERVICE_ENDPOINT") {
|
|
config.trading_service_endpoint = endpoint;
|
|
}
|
|
if let Ok(url) = std::env::var("DATABASE_URL") {
|
|
config.database_url = url;
|
|
}
|
|
if let Ok(url) = std::env::var("INFLUXDB_URL") {
|
|
config.influxdb_url = url;
|
|
}
|
|
if let Ok(_) = std::env::var("ENABLE_GPU_TESTS") {
|
|
config.enable_gpu_tests = true;
|
|
}
|
|
|
|
Ok(config)
|
|
} |