Files
foxhunt/testing/harness/mod.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
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>
2026-02-25 11:56:00 +01:00

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)
}