Files
foxhunt/tests/e2e/tests/performance_validation_tests.rs
jgrusewski f8e332fc4c 🎉 SUCCESS: Complete workspace compiles without errors!
MASSIVE ACHIEVEMENT:
- Eliminated ALL compilation errors (0 remaining)
- Fixed all e2e test compilation issues
- Fixed backtesting proto request structures
- Resolved all import and borrowing issues
- Fixed streaming implementation in mock clients

PROGRESS SUMMARY:
- Started with 1,500+ errors and warnings
- Reduced to 0 compilation errors
- Only warnings remain (can be addressed later)

FULL WORKSPACE STATUS:
 Main production code: Compiles perfectly
 E2E tests: All compilation errors resolved
 All crates: Successfully building

The Foxhunt HFT Trading System now compiles completely!
2025-09-30 09:17:48 +02:00

746 lines
28 KiB
Rust

use std::collections::VecDeque;
use std::sync::Arc;
use tokio::time::{timeout, Duration};
use trading_engine::{
infrastructure::{PerformanceAnalyzer, ResourceMonitor, ThroughputMonitor},
lockfree::{AtomicCounter, RingBuffer},
prelude::*,
simd::SimdProcessor,
timing::{HardwareTimestamp, LatencyTracker, PrecisionTimer},
trading::{OrderManager, PositionManager},
types::{PerformanceMetrics, Price, Quantity, Symbol},
};
/// Comprehensive performance validation and benchmarking tests
pub struct PerformanceValidationTests {
latency_tracker: Arc<LatencyTracker>,
throughput_monitor: Arc<ThroughputMonitor>,
resource_monitor: Arc<ResourceMonitor>,
performance_analyzer: Arc<PerformanceAnalyzer>,
order_manager: Arc<OrderManager>,
position_manager: Arc<PositionManager>,
simd_processor: Arc<SimdProcessor>,
ring_buffer: Arc<RingBuffer<u64>>,
}
impl PerformanceValidationTests {
pub async fn new() -> Result<Self> {
let config = load_test_config().await?;
let latency_tracker = Arc::new(LatencyTracker::new());
let throughput_monitor = Arc::new(ThroughputMonitor::new());
let resource_monitor = Arc::new(ResourceMonitor::new());
let performance_analyzer = Arc::new(PerformanceAnalyzer::new(config.clone()));
let order_manager = Arc::new(OrderManager::new(config.clone()).await?);
let position_manager = Arc::new(PositionManager::new(config.clone()).await?);
let simd_processor = Arc::new(SimdProcessor::new());
let ring_buffer = Arc::new(RingBuffer::new(65536)); // 64k entries
Ok(Self {
latency_tracker,
throughput_monitor,
resource_monitor,
performance_analyzer,
order_manager,
position_manager,
simd_processor,
ring_buffer,
})
}
/// Test 1: Critical path sub-50μs latency validation
/// Steps: 12 comprehensive latency measurement phases
pub async fn test_critical_path_latency_validation(&self) -> Result<WorkflowTestResult> {
let mut result = WorkflowTestResult::new("Critical Path Latency Validation");
// Step 1: Hardware timing calibration
result.add_step("Hardware Timing Calibration").await;
let calibration_start = HardwareTimestamp::now();
let calibration_samples: Vec<u64> = (0..10000)
.map(|_| {
let start = HardwareTimestamp::now();
std::hint::black_box(42); // Prevent optimization
start.elapsed_nanos()
})
.collect();
let calibration_time = calibration_start.elapsed_nanos();
let min_resolution = calibration_samples
.iter()
.filter(|&&x| x > 0)
.min()
.unwrap_or(&1);
let avg_resolution =
calibration_samples.iter().sum::<u64>() as f64 / calibration_samples.len() as f64;
assert!(
*min_resolution <= 50,
"Hardware timing resolution should be ≤50ns, got {}ns",
min_resolution
);
result.add_metric("hardware_resolution_ns", *min_resolution as f64);
result.add_metric("avg_resolution_ns", avg_resolution);
result.add_metric("calibration_time_ns", calibration_time as f64);
// Step 2: RDTSC precision measurement
result.add_step("RDTSC Precision Measurement").await;
let rdtsc_samples: Vec<u64> = (0..1000)
.map(|_| {
let start = HardwareTimestamp::rdtsc_start();
// Minimal operation to measure
let _dummy = 1u64.wrapping_add(2);
HardwareTimestamp::rdtsc_end(start)
})
.collect();
let rdtsc_p50 = percentile(&rdtsc_samples, 50.0);
let rdtsc_p95 = percentile(&rdtsc_samples, 95.0);
let rdtsc_p99 = percentile(&rdtsc_samples, 99.0);
assert!(
rdtsc_p95 <= 100,
"RDTSC P95 should be ≤100ns, got {}ns",
rdtsc_p95
);
result.add_metric("rdtsc_p50_ns", rdtsc_p50 as f64);
result.add_metric("rdtsc_p95_ns", rdtsc_p95 as f64);
result.add_metric("rdtsc_p99_ns", rdtsc_p99 as f64);
// Step 3: Order creation latency measurement
result.add_step("Order Creation Latency").await;
let order_creation_samples: Vec<u64> = (0..10000)
.map(|i| {
let start = HardwareTimestamp::now();
let _order = Order::new(
OrderId::from_u64(i as u64),
Symbol::new("EURUSD"),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
);
start.elapsed_nanos()
})
.collect();
let creation_p50 = percentile(&order_creation_samples, 50.0);
let creation_p95 = percentile(&order_creation_samples, 95.0);
let creation_p99 = percentile(&order_creation_samples, 99.0);
assert!(
creation_p95 <= 5_000,
"Order creation P95 should be ≤5μs, got {}ns",
creation_p95
);
result.add_metric("order_creation_p50_ns", creation_p50 as f64);
result.add_metric("order_creation_p95_ns", creation_p95 as f64);
// Step 4: Risk validation latency
result.add_step("Risk Validation Latency").await;
let symbol = Symbol::new("EURUSD");
let test_order = Order::new(
OrderId::new(),
symbol.clone(),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
let risk_samples: Vec<u64> = {
let mut samples = Vec::with_capacity(1000);
for _ in 0..1000 {
let start = HardwareTimestamp::now();
let _risk_result = self.order_manager.validate_risk_fast(&test_order).await;
let elapsed = start.elapsed_nanos();
samples.push(elapsed);
}
samples
};
let risk_p50 = percentile(&risk_samples, 50.0);
let risk_p95 = percentile(&risk_samples, 95.0);
assert!(
risk_p95 <= 10_000,
"Risk validation P95 should be ≤10μs, got {}ns",
risk_p95
);
result.add_metric("risk_validation_p50_ns", risk_p50 as f64);
result.add_metric("risk_validation_p95_ns", risk_p95 as f64);
// Step 5: Position update latency
result.add_step("Position Update Latency").await;
let position_samples: Vec<u64> = {
let mut samples = Vec::with_capacity(1000);
for i in 0..1000 {
let start = HardwareTimestamp::now();
let _result = self
.position_manager
.update_position_atomic(&symbol, Quantity::from(i as i64 * 100))
.await;
let elapsed = start.elapsed_nanos();
samples.push(elapsed);
}
samples
};
let position_p50 = percentile(&position_samples, 50.0);
let position_p95 = percentile(&position_samples, 95.0);
assert!(
position_p95 <= 8_000,
"Position update P95 should be ≤8μs, got {}ns",
position_p95
);
result.add_metric("position_update_p50_ns", position_p50 as f64);
result.add_metric("position_update_p95_ns", position_p95 as f64);
// Step 6: Lock-free data structure performance
result.add_step("Lock-free Structure Performance").await;
let lockfree_samples: Vec<u64> = (0..10000)
.map(|i| {
let start = HardwareTimestamp::now();
let success = self.ring_buffer.try_push(i);
let elapsed = start.elapsed_nanos();
assert!(success, "Ring buffer push should succeed");
elapsed
})
.collect();
let lockfree_p50 = percentile(&lockfree_samples, 50.0);
let lockfree_p95 = percentile(&lockfree_samples, 95.0);
assert!(
lockfree_p95 <= 500,
"Lock-free push P95 should be ≤500ns, got {}ns",
lockfree_p95
);
result.add_metric("lockfree_push_p50_ns", lockfree_p50 as f64);
result.add_metric("lockfree_push_p95_ns", lockfree_p95 as f64);
// Step 7: SIMD operation performance
result.add_step("SIMD Operation Performance").await;
let simd_data: Vec<f64> = (0..1000).map(|i| i as f64 * 1.1).collect();
let simd_samples: Vec<u64> = (0..1000)
.map(|_| {
let start = HardwareTimestamp::now();
let _result = self.simd_processor.vectorized_multiply(&simd_data, 2.0);
start.elapsed_nanos()
})
.collect();
let simd_p50 = percentile(&simd_samples, 50.0);
let simd_p95 = percentile(&simd_samples, 95.0);
assert!(
simd_p95 <= 2_000,
"SIMD operation P95 should be ≤2μs, got {}ns",
simd_p95
);
result.add_metric("simd_operation_p50_ns", simd_p50 as f64);
result.add_metric("simd_operation_p95_ns", simd_p95 as f64);
// Step 8: End-to-end critical path measurement
result.add_step("End-to-End Critical Path").await;
let e2e_samples: Vec<u64> = {
let mut samples = Vec::with_capacity(1000);
for i in 0..1000 {
let start = HardwareTimestamp::now();
// Critical path: Order creation -> Risk check -> Position update -> Submit
let order = Order::new(
OrderId::from_u64(10000 + i as u64),
symbol.clone(),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
let _risk_check = self.order_manager.validate_risk_fast(&order).await;
let _position_update = self
.position_manager
.update_position_atomic(&symbol, Quantity::from(100_000))
.await;
let _submission = self.order_manager.submit_order_fast(order).await;
let elapsed = start.elapsed_nanos();
samples.push(elapsed);
}
Result::<Vec<u64>>::Ok(samples)
}?;
let e2e_p50 = percentile(&e2e_samples, 50.0);
let e2e_p95 = percentile(&e2e_samples, 95.0);
let e2e_p99 = percentile(&e2e_samples, 99.0);
// THE CRITICAL REQUIREMENT: Sub-50μs end-to-end
assert!(
e2e_p95 < 50_000,
"End-to-end critical path too slow: P95={}ns > 50μs",
e2e_p95
);
result.add_metric("e2e_critical_p50_ns", e2e_p50 as f64);
result.add_metric("e2e_critical_p95_ns", e2e_p95 as f64);
result.add_metric("e2e_critical_p99_ns", e2e_p99 as f64);
// Step 9: Jitter analysis
result.add_step("Jitter Analysis").await;
let jitter_samples: Vec<u64> = e2e_samples
.windows(2)
.map(|pair| (pair[1] as i64 - pair[0] as i64).abs() as u64)
.collect();
let jitter_p95 = percentile(&jitter_samples, 95.0);
let jitter_max = jitter_samples.iter().max().unwrap_or(&0);
assert!(
jitter_p95 < 10_000,
"Jitter P95 should be <10μs, got {}ns",
jitter_p95
);
result.add_metric("jitter_p95_ns", jitter_p95 as f64);
result.add_metric("jitter_max_ns", *jitter_max as f64);
// Step 10: Temperature and throttling monitoring
result.add_step("Thermal Performance").await;
let thermal_metrics = self.resource_monitor.get_thermal_metrics().await?;
assert!(
thermal_metrics.cpu_temperature_celsius < 80.0,
"CPU temperature too high: {}°C",
thermal_metrics.cpu_temperature_celsius
);
assert!(
!thermal_metrics.is_throttling,
"CPU should not be throttling"
);
result.add_metric("cpu_temperature", thermal_metrics.cpu_temperature_celsius);
// Step 11: Cache performance analysis
result.add_step("Cache Performance Analysis").await;
let cache_metrics = self.resource_monitor.get_cache_metrics().await?;
assert!(
cache_metrics.l1_hit_rate > 0.95,
"L1 cache hit rate should be >95%"
);
assert!(
cache_metrics.l2_hit_rate > 0.90,
"L2 cache hit rate should be >90%"
);
result.add_metric("l1_hit_rate", cache_metrics.l1_hit_rate);
result.add_metric("l2_hit_rate", cache_metrics.l2_hit_rate);
// Step 12: Sustained performance validation
result.add_step("Sustained Performance").await;
let sustained_start = HardwareTimestamp::now();
let mut sustained_samples = Vec::with_capacity(10000);
// Run for 10 seconds at high frequency
let test_duration = Duration::from_secs(10);
let test_end = sustained_start.add_duration(test_duration);
while HardwareTimestamp::now() < test_end {
let sample_start = HardwareTimestamp::now();
let order = Order::new(
OrderId::new(),
symbol.clone(),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
let _risk_check = self.order_manager.validate_risk_fast(&order).await;
let elapsed = sample_start.elapsed_nanos();
sustained_samples.push(elapsed);
}
let sustained_p95 = percentile(&sustained_samples, 95.0);
let sustained_degradation = (sustained_p95 as f64 / e2e_p95 as f64) - 1.0;
assert!(
sustained_degradation < 0.20,
"Sustained performance degradation should be <20%, got {:.1}%",
sustained_degradation * 100.0
);
result.add_metric("sustained_samples", sustained_samples.len() as f64);
result.add_metric("sustained_p95_ns", sustained_p95 as f64);
result.add_metric("performance_degradation", sustained_degradation);
result.mark_success();
Ok(result)
}
/// Test 2: Throughput and scalability benchmarks
/// Steps: 10 comprehensive throughput measurement phases
pub async fn test_throughput_scalability_benchmarks(&self) -> Result<WorkflowTestResult> {
let mut result = WorkflowTestResult::new("Throughput Scalability Benchmarks");
// Step 1: Single-threaded baseline throughput
result.add_step("Single-threaded Baseline").await;
let single_thread_start = HardwareTimestamp::now();
let mut operations_completed = 0u64;
let test_duration = Duration::from_secs(5);
let end_time = single_thread_start.add_duration(test_duration);
while HardwareTimestamp::now() < end_time {
let order = Order::new(
OrderId::from_u64(operations_completed),
Symbol::new("EURUSD"),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
let _validation = self.order_manager.validate_risk_fast(&order).await;
operations_completed += 1;
}
let actual_duration = single_thread_start.elapsed_nanos() as f64 / 1_000_000_000.0;
let single_thread_ops_per_sec = operations_completed as f64 / actual_duration;
assert!(
single_thread_ops_per_sec > 100_000.0,
"Single-thread should exceed 100k ops/sec, got {:.0}",
single_thread_ops_per_sec
);
result.add_metric("single_thread_ops_per_sec", single_thread_ops_per_sec);
result.add_metric("single_thread_total_ops", operations_completed as f64);
// Step 2: Multi-threaded throughput scaling
result.add_step("Multi-threaded Scaling").await;
let thread_counts = vec![2, 4, 8, 16];
let mut scaling_results = Vec::new();
for thread_count in thread_counts {
let mt_start = HardwareTimestamp::now();
let operations_per_thread = Arc::new(AtomicCounter::new());
let handles: Vec<_> = (0..thread_count)
.map(|thread_id| {
let counter = operations_per_thread.clone();
let order_manager = self.order_manager.clone();
tokio::spawn(async move {
let thread_start = HardwareTimestamp::now();
let thread_duration = Duration::from_secs(3);
let thread_end = thread_start.add_duration(thread_duration);
let mut local_ops = 0u64;
while HardwareTimestamp::now() < thread_end {
let order = Order::new(
OrderId::from_u64((thread_id as u64) << 32 | local_ops),
Symbol::new("EURUSD"),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
let _validation = order_manager.validate_risk_fast(&order).await;
local_ops += 1;
}
counter.add(local_ops);
Result::<u64>::Ok(local_ops)
})
})
.collect();
let thread_results = futures::future::join_all(handles).await;
let mt_duration = mt_start.elapsed_nanos() as f64 / 1_000_000_000.0;
let total_mt_ops = operations_per_thread.get();
let mt_ops_per_sec = total_mt_ops as f64 / mt_duration;
scaling_results.push((thread_count, mt_ops_per_sec));
// Validate scaling efficiency
let scaling_efficiency =
mt_ops_per_sec / (single_thread_ops_per_sec * thread_count as f64);
result.add_metric(
&format!("mt_{}_threads_ops_per_sec", thread_count),
mt_ops_per_sec,
);
result.add_metric(
&format!("mt_{}_threads_efficiency", thread_count),
scaling_efficiency,
);
// Should maintain at least 70% efficiency up to 8 threads
if thread_count <= 8 {
assert!(
scaling_efficiency > 0.70,
"Scaling efficiency for {} threads too low: {:.1}%",
thread_count,
scaling_efficiency * 100.0
);
}
}
// Step 3: Memory bandwidth saturation test
result.add_step("Memory Bandwidth Saturation").await;
let memory_test_data: Vec<f64> = (0..1_000_000).map(|i| i as f64 * 1.1).collect();
let memory_start = HardwareTimestamp::now();
let memory_operations = 1000;
for _ in 0..memory_operations {
let _result = self.simd_processor.vectorized_sum(&memory_test_data);
}
let memory_duration = memory_start.elapsed_nanos() as f64 / 1_000_000.0; // ms
let memory_bandwidth_gbps = (memory_test_data.len() * 8 * memory_operations) as f64
/ 1_000_000_000.0
/ (memory_duration / 1000.0);
result.add_metric("memory_bandwidth_gbps", memory_bandwidth_gbps);
assert!(
memory_bandwidth_gbps > 10.0,
"Memory bandwidth should exceed 10 GB/s"
);
// Step 4: Queue depth and batching optimization
result.add_step("Queue Depth Optimization").await;
let batch_sizes = vec![1, 8, 32, 128, 512];
let mut batch_results = Vec::new();
for batch_size in batch_sizes {
let batch_start = HardwareTimestamp::now();
let total_batches = 1000;
for batch_idx in 0..total_batches {
let mut batch_orders = Vec::with_capacity(batch_size);
for i in 0..batch_size {
let order = Order::new(
OrderId::from_u64((batch_idx * batch_size + i) as u64),
Symbol::new("EURUSD"),
OrderType::Market,
OrderSide::Buy,
Quantity::from(100_000),
None,
)?;
batch_orders.push(order);
}
let _batch_result = self.order_manager.validate_risk_batch(&batch_orders).await;
}
let batch_duration = batch_start.elapsed_nanos() as f64 / 1_000_000_000.0;
let batch_ops_per_sec = (total_batches * batch_size) as f64 / batch_duration;
batch_results.push((batch_size, batch_ops_per_sec));
result.add_metric(
&format!("batch_size_{}_ops_per_sec", batch_size),
batch_ops_per_sec,
);
}
// Find optimal batch size
let optimal_batch = batch_results
.iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
.unwrap();
result.add_metric("optimal_batch_size", optimal_batch.0 as f64);
result.add_metric("optimal_batch_ops_per_sec", optimal_batch.1);
// Step 5: Network I/O throughput simulation
result.add_step("Network I/O Throughput").await;
let network_start = HardwareTimestamp::now();
let message_count = 100_000;
let message_size = 256; // bytes
for i in 0..message_count {
let message = vec![0u8; message_size];
let _serialized = self.order_manager.serialize_order_message(&message).await;
}
let network_duration = network_start.elapsed_nanos() as f64 / 1_000_000_000.0;
let network_messages_per_sec = message_count as f64 / network_duration;
let network_mbps = (message_count * message_size) as f64 / 1_000_000.0 / network_duration;
result.add_metric("network_messages_per_sec", network_messages_per_sec);
result.add_metric("network_throughput_mbps", network_mbps);
assert!(
network_messages_per_sec > 50_000.0,
"Network message rate should exceed 50k/sec"
);
// Step 6: Database write throughput
result.add_step("Database Write Throughput").await;
let db_start = HardwareTimestamp::now();
let db_writes = 10_000;
for i in 0..db_writes {
let trade_record = create_test_trade_record(i);
let _db_result = self.order_manager.persist_trade_record(&trade_record).await;
}
let db_duration = db_start.elapsed_nanos() as f64 / 1_000_000_000.0;
let db_writes_per_sec = db_writes as f64 / db_duration;
result.add_metric("db_writes_per_sec", db_writes_per_sec);
assert!(
db_writes_per_sec > 5_000.0,
"Database writes should exceed 5k/sec"
);
// Step 7: CPU utilization under load
result.add_step("CPU Utilization Analysis").await;
let cpu_start = HardwareTimestamp::now();
let baseline_cpu = self.resource_monitor.get_cpu_usage().await?;
// Generate high load
let high_load_duration = Duration::from_secs(5);
let load_end = cpu_start.add_duration(high_load_duration);
let _load_task = tokio::spawn(async move {
while HardwareTimestamp::now() < load_end {
// Simulate trading workload
let _computation = (0..1000).map(|x| x * x).sum::<i32>();
}
});
tokio::time::sleep(Duration::from_secs(2)).await;
let load_cpu = self.resource_monitor.get_cpu_usage().await?;
let cpu_utilization = load_cpu.user_percent + load_cpu.system_percent;
result.add_metric("cpu_utilization_percent", cpu_utilization);
result.add_metric("cpu_user_percent", load_cpu.user_percent);
result.add_metric("cpu_system_percent", load_cpu.system_percent);
assert!(
cpu_utilization < 90.0,
"CPU utilization should stay below 90%"
);
// Step 8: Memory allocation and GC pressure
result.add_step("Memory Allocation Analysis").await;
let memory_start = self.resource_monitor.get_memory_usage().await?;
// Allocate and deallocate memory to test pressure
let allocation_cycles = 1000;
for _ in 0..allocation_cycles {
let large_allocation: Vec<u64> = (0..10_000).collect();
std::hint::black_box(&large_allocation); // Prevent optimization
}
let memory_end = self.resource_monitor.get_memory_usage().await?;
let memory_growth = memory_end.used_mb - memory_start.used_mb;
result.add_metric("memory_growth_mb", memory_growth);
result.add_metric("memory_utilization_percent", memory_end.utilization_percent);
// Memory growth should be reasonable (not a major leak)
assert!(
memory_growth < 100.0,
"Memory growth should be <100MB for test workload"
);
// Step 9: I/O wait and disk performance
result.add_step("I/O Performance Analysis").await;
let io_metrics = self.resource_monitor.get_io_metrics().await?;
result.add_metric("disk_read_mbps", io_metrics.read_mbps);
result.add_metric("disk_write_mbps", io_metrics.write_mbps);
result.add_metric("io_wait_percent", io_metrics.io_wait_percent);
assert!(io_metrics.io_wait_percent < 20.0, "I/O wait should be <20%");
// Step 10: Overall system performance score
result.add_step("System Performance Score").await;
let perf_score = self
.performance_analyzer
.calculate_overall_score(
single_thread_ops_per_sec,
optimal_batch.1,
network_messages_per_sec,
db_writes_per_sec,
)
.await?;
result.add_metric("overall_performance_score", perf_score.total_score);
result.add_metric("latency_score", perf_score.latency_score);
result.add_metric("throughput_score", perf_score.throughput_score);
result.add_metric(
"resource_efficiency_score",
perf_score.resource_efficiency_score,
);
assert!(
perf_score.total_score > 85.0,
"Overall performance score should exceed 85/100"
);
result.mark_success();
Ok(result)
}
}
// Helper functions for performance testing
fn percentile(samples: &[u64], percentile: f64) -> u64 {
if samples.is_empty() {
return 0;
}
let mut sorted = samples.to_vec();
sorted.sort_unstable();
let index = ((percentile / 100.0) * (sorted.len() - 1) as f64) as usize;
sorted[index]
}
fn create_test_trade_record(id: u64) -> TradeRecord {
TradeRecord {
trade_id: TradeId::new(id.to_string()).unwrap(),
symbol: Symbol::new("EURUSD"),
quantity: Quantity::from(100_000),
price: Price::from_f64(1.1050).unwrap(),
side: OrderSide::Buy,
timestamp: HardwareTimestamp::now(),
venue: "TEST_VENUE".to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_critical_path_latency_integration() {
let test_suite = PerformanceValidationTests::new().await.unwrap();
let result = test_suite
.test_critical_path_latency_validation()
.await
.unwrap();
assert!(result.success, "Critical path latency test failed");
assert!(result.steps.len() == 12, "Should have 12 steps");
// Verify critical performance requirements
let e2e_p95 = result.metrics.get("e2e_critical_p95_ns").unwrap();
assert!(
*e2e_p95 < 50_000.0,
"End-to-end P95 latency requirement failed"
);
}
#[tokio::test]
async fn test_throughput_benchmarks_integration() {
let test_suite = PerformanceValidationTests::new().await.unwrap();
let result = test_suite
.test_throughput_scalability_benchmarks()
.await
.unwrap();
assert!(result.success, "Throughput benchmarks test failed");
assert!(result.steps.len() == 10, "Should have 10 steps");
// Verify throughput requirements
let single_thread_ops = result.metrics.get("single_thread_ops_per_sec").unwrap();
assert!(
*single_thread_ops > 100_000.0,
"Single-thread throughput requirement failed"
);
}
}