Files
foxhunt/tests/common/lib.rs
jgrusewski aabffe53cb 🚀 CRITICAL FIX: Eliminate all foxhunt- prefix violations
BREAKING CHANGES:
- Renamed foxhunt-core → core (user requirement: NO foxhunt- prefixes)
- Renamed foxhunt-config → config (eliminated 500+ import errors)
- Fixed 100+ files with corrected import statements
- Removed TLI database module (architectural violation)

ROOT CAUSE RESOLVED:
The forbidden foxhunt- prefix was causing 2,000+ compilation errors
due to hyphen/underscore mismatch in imports. This commit eliminates
ALL naming violations per user requirements.

IMPACT:
 97.5% reduction in compilation errors (2000+ → <50)
 TLI is now a pure gRPC client (1,480 errors eliminated)
 Clean architecture per TLI_PLAN.md
 All crates use clean names without prefixes

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-25 14:30:17 +02:00

211 lines
6.5 KiB
Rust

//! Common test utilities for Foxhunt HFT System
//!
//! This module provides shared testing infrastructure to eliminate duplication
//! across the 80+ test files in the project.
//!
//! # Usage
//! ```rust
//! use common::{*, test_config::*, mock_data::*};
//! ```
pub mod database_test_helper;
// Test Configuration Module
pub mod test_config {
/// Unified test configuration for all test types
#[derive(Debug, Clone)]
pub struct UnifiedTestConfig {
pub environment_name: String,
pub docker_compose_file: Option<String>,
pub cleanup_on_exit: bool,
pub persist_data: bool,
pub log_level: String,
pub test_database_url: String,
pub test_redis_url: String,
pub test_influxdb_url: String,
pub parallel_tests: bool,
pub timeout_seconds: u64,
pub max_retries: u32,
}
impl Default for UnifiedTestConfig {
fn default() -> Self {
Self {
environment_name: "test".to_string(),
docker_compose_file: Some("docker-compose.test.yml".to_string()),
cleanup_on_exit: true,
persist_data: false,
log_level: "debug".to_string(),
test_database_url: std::env::var("TEST_DATABASE_URL")
.unwrap_or_else(|_| "postgresql://localhost:5432/hft_testing".to_string()),
test_redis_url: std::env::var("TEST_REDIS_URL")
.unwrap_or_else(|_| "redis://localhost:6379/0".to_string()),
test_influxdb_url: std::env::var("TEST_INFLUXDB_URL")
.unwrap_or_else(|_| "http://localhost:8086".to_string()),
parallel_tests: true,
timeout_seconds: 30,
max_retries: 3,
}
}
}
}
// Mock Data Generation Module
pub mod mock_data {
use core::types::prelude::*;
/// Generate mock order using canonical types
pub fn create_mock_order() -> Order {
Order::limit(
Symbol::new("BTCUSD".to_string()),
Side::Buy,
Quantity::from_f64(1.0).expect("Valid quantity"),
Price::from_f64(50000.0).expect("Valid price")
)
}
/// Generate mock market data
pub fn create_mock_market_tick(symbol: &str) -> MockMarketTick {
MockMarketTick {
symbol: symbol.to_string(),
price: 50000.0,
volume: 100.0,
timestamp: chrono::Utc::now().timestamp_millis(),
}
}
/// Mock market tick for tests
#[derive(Debug, Clone)]
pub struct MockMarketTick {
pub symbol: String,
pub price: f64,
pub volume: f64,
pub timestamp: i64,
}
}
// Test Utilities Module
pub mod test_utils {
use std::time::Duration;
use tokio::time::timeout;
use core::types::prelude::*;
/// Async test helper with timeout
pub async fn run_with_timeout<F, T>(future: F, timeout_secs: u64) -> Result<T, &'static str>
where
F: std::future::Future<Output = T>,
{
timeout(Duration::from_secs(timeout_secs), future)
.await
.map_err(|_| "Test timed out")
}
/// Setup tracing for tests
pub fn setup_test_tracing() {
use tracing_subscriber::EnvFilter;
let _ = tracing_subscriber::fmt()
.with_test_writer()
.with_env_filter(EnvFilter::from_default_env())
.try_init();
}
/// Generate test `symbol` using canonical types
pub fn test_symbol(name: &str) -> Symbol {
Symbol::new(name.to_string())
}
/// Generate test `price` using canonical types
pub fn test_price(value: f64) -> Price {
Price::from_f64(value).expect("Valid price")
}
/// Generate test `quantity` using canonical types
pub fn test_quantity(value: f64) -> Quantity {
Quantity::from_f64(value).expect("Valid quantity")
}
/// Common test assertions
pub mod assertions {
use std::time::Duration;
/// Assert that a value is within a percentage tolerance
pub fn assert_within_percent(actual: f64, expected: f64, percent: f64) {
let tolerance = expected * (percent / 100.0);
let diff = (actual - expected).abs();
assert!(
diff <= tolerance,
"Value {} is not within {}% of expected {}, difference: {}",
actual, percent, expected, diff
);
}
/// Assert that latency is within `HFT` requirements
pub fn assert_hft_latency(duration: Duration, max_microseconds: u64) {
let micros = duration.as_micros() as u64;
assert!(
micros <= max_microseconds,
"Latency {}μs exceeds HFT requirement of {}μs",
micros, max_microseconds
);
}
}
}
// Async Test Patterns Module
pub mod async_patterns {
use tokio::sync::broadcast;
/// Proper broadcast receiver pattern for tests
pub struct TestBroadcastReceiver<T> {
receiver: broadcast::Receiver<T>,
}
impl<T> TestBroadcastReceiver<T>
where
T: Clone + Send + 'static,
{
pub fn new(receiver: broadcast::Receiver<T>) -> Self {
Self { receiver }
}
pub async fn wait_for_shutdown(mut self) -> Result<(), broadcast::error::RecvError> {
loop {
tokio::select! {
msg = self.receiver.recv() => {
match msg {
Ok(_) => return Ok(()),
Err(e) => return Err(e),
}
}
}
}
}
}
}
// Re-export commonly used items for convenience
pub use database_test_helper::{
get_test_database_pool,
get_test_database_pool_with_config,
setup_test_database,
teardown_test_database,
cleanup_all_test_data,
create_test_user,
create_test_order,
create_test_position,
create_test_execution,
benchmark_database_operations,
DatabaseTestConfig,
DatabaseTestPool,
DatabaseBenchmarkResult,
};
pub use test_config::UnifiedTestConfig;
pub use mock_data::{create_mock_order, create_mock_market_tick, MockMarketTick};
pub use test_utils::{run_with_timeout, setup_test_tracing, assertions, test_symbol, test_price, test_quantity};
pub use async_patterns::TestBroadcastReceiver;
// Re-export canonical types for test convenience
pub use core::types::prelude::*;