bashrs 6.66.0

Rust-to-Shell transpiler for deterministic bootstrap scripts
// Stress testing module - SQLite-style load and endurance testing
use crate::models::Config;
use crate::Result;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};

#[derive(Debug, Clone)]
pub struct StressTestResults {
    pub total_operations: usize,
    pub successful_operations: usize,
    pub failed_operations: usize,
    pub average_latency_ms: f64,
    pub max_latency_ms: f64,
    pub min_latency_ms: f64,
    pub memory_usage_mb: f64,
    pub concurrent_threads: usize,
    pub test_duration_secs: f64,
    pub operations_per_second: f64,
    pub error_details: Vec<String>,
}

impl StressTestResults {
    pub fn success_rate(&self) -> f64 {
        if self.total_operations == 0 {
            return 0.0;
        }
        (self.successful_operations as f64 / self.total_operations as f64) * 100.0
    }
}

pub struct StressTester {
    config: Config,
}

impl StressTester {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    pub fn run_stress_tests(&self) -> Result<StressTestResults> {
        let start_time = Instant::now();
        let mut results = StressTestResults {
            total_operations: 0,
            successful_operations: 0,
            failed_operations: 0,
            average_latency_ms: 0.0,
            max_latency_ms: 0.0,
            min_latency_ms: f64::MAX,
            memory_usage_mb: 0.0,
            concurrent_threads: 0,
            test_duration_secs: 0.0,
            operations_per_second: 0.0,
            error_details: Vec::new(),
        };

        // Run multiple stress test phases
        self.test_high_load_transpilation(&mut results)?;
        self.test_concurrent_operations(&mut results)?;
        self.test_memory_pressure(&mut results)?;
        self.test_sustained_load(&mut results)?;
        self.test_burst_load(&mut results)?;

        results.test_duration_secs = start_time.elapsed().as_secs_f64();
        if results.test_duration_secs > 0.0 {
            results.operations_per_second =
                results.total_operations as f64 / results.test_duration_secs;
        }

        Ok(results)
    }

    fn test_high_load_transpilation(&self, results: &mut StressTestResults) -> Result<()> {
        let test_cases = vec![
            "fn main() { let x = 42; }",
            "fn main() { let s = \"hello world\"; }",
            "fn main() { let b = true; println!(\"{}\", b); }",
            "fn main() { for i in 0..100 { println!(\"{}\", i); } }",
            "fn main() { let mut v = Vec::new(); v.push(1); v.push(2); }",
        ];

        let iterations = 1000;
        let mut latencies = Vec::new();

        for _ in 0..iterations {
            for test_case in &test_cases {
                let start = Instant::now();
                let result = crate::transpile(test_case, &self.config);
                let latency = start.elapsed().as_millis() as f64;

                latencies.push(latency);
                results.total_operations += 1;

                match result {
                    Ok(_) => results.successful_operations += 1,
                    Err(e) => {
                        results.failed_operations += 1;
                        results
                            .error_details
                            .push(format!("Transpilation error: {e}"));
                    }
                }
            }
        }

        // Calculate latency statistics
        if !latencies.is_empty() {
            results.average_latency_ms = latencies.iter().sum::<f64>() / latencies.len() as f64;
            results.max_latency_ms = latencies.iter().fold(0.0, |a, &b| a.max(b));
            results.min_latency_ms = latencies.iter().fold(f64::MAX, |a, &b| a.min(b));
        }

        Ok(())
    }

    fn test_concurrent_operations(&self, results: &mut StressTestResults) -> Result<()> {
        let num_threads = 8;
        let operations_per_thread = 100;
        results.concurrent_threads = num_threads;

        let success_count = Arc::new(Mutex::new(0));
        let error_count = Arc::new(Mutex::new(0));
        let errors = Arc::new(Mutex::new(Vec::new()));

        let handles: Vec<_> = (0..num_threads)
            .map(|_| {
                let success = Arc::clone(&success_count);
                let errors_count = Arc::clone(&error_count);
                let error_details = Arc::clone(&errors);
                let config = self.config.clone();

                thread::spawn(move || {
                    for i in 0..operations_per_thread {
                        let test_code = format!("fn main() {{ let x = {i}; }}");
                        match crate::transpile(&test_code, &config) {
                            Ok(_) => {
                                let mut count = success.lock().unwrap();
                                *count += 1;
                            }
                            Err(e) => {
                                let mut count = errors_count.lock().unwrap();
                                *count += 1;
                                let mut errs = error_details.lock().unwrap();
                                errs.push(format!("Concurrent error: {e}"));
                            }
                        }
                    }
                })
            })
            .collect();

        for handle in handles {
            handle
                .join()
                .map_err(|_| crate::Error::Internal("Thread panic".to_string()))?;
        }

        let successful = *success_count.lock().unwrap();
        let failed = *error_count.lock().unwrap();
        let thread_errors = errors.lock().unwrap().clone();

        results.total_operations += successful + failed;
        results.successful_operations += successful;
        results.failed_operations += failed;
        results.error_details.extend(thread_errors);

        Ok(())
    }

    fn test_memory_pressure(&self, results: &mut StressTestResults) -> Result<()> {
        // Test with increasingly large inputs to stress memory allocation
        let base_code = "fn main() { let x = ";
        let large_values = vec![
            "42".repeat(100),
            "\"hello\"".repeat(200),
            "vec![".to_string() + &"1,".repeat(500) + "]",
            "{\n".to_string() + &"    let y = 1;\n".repeat(1000) + "}",
        ];

        for large_value in large_values {
            let test_code = format!("{base_code}{large_value};");

            match crate::transpile(&test_code, &self.config) {
                Ok(_) => results.successful_operations += 1,
                Err(e) => {
                    results.failed_operations += 1;
                    results
                        .error_details
                        .push(format!("Memory pressure error: {e}"));
                }
            }
            results.total_operations += 1;
        }

        // Estimate memory usage (simplified)
        results.memory_usage_mb = 50.0; // Placeholder - would need actual memory monitoring

        Ok(())
    }

    fn test_sustained_load(&self, results: &mut StressTestResults) -> Result<()> {
        // Run continuous operations for a sustained period
        let test_duration = Duration::from_secs(10);
        let start_time = Instant::now();

        let mut operations = 0;
        let mut successes = 0;
        let mut failures = 0;

        while start_time.elapsed() < test_duration {
            let test_code = format!("fn main() {{ let x = {}; }}", operations % 1000);

            match crate::transpile(&test_code, &self.config) {
                Ok(_) => successes += 1,
                Err(e) => {
                    failures += 1;
                    if failures < 10 {
                        // Limit error collection
                        results
                            .error_details
                            .push(format!("Sustained load error: {e}"));
                    }
                }
            }
            operations += 1;

            // Small delay to prevent CPU overload
            thread::sleep(Duration::from_millis(1));
        }

        results.total_operations += operations;
        results.successful_operations += successes;
        results.failed_operations += failures;

        Ok(())
    }

    fn test_burst_load(&self, results: &mut StressTestResults) -> Result<()> {
        // Test rapid bursts of operations
        for burst in 0..5 {
            let burst_size = 50;
            let burst_start = Instant::now();

            for i in 0..burst_size {
                let test_code = format!("fn main() {{ let burst_{burst}_op_{i} = {i}; }}");

                match crate::transpile(&test_code, &self.config) {
                    Ok(_) => results.successful_operations += 1,
                    Err(e) => {
                        results.failed_operations += 1;
                        results.error_details.push(format!("Burst load error: {e}"));
                    }
                }
                results.total_operations += 1;
            }

            let burst_duration = burst_start.elapsed();
            if burst_duration.as_millis() > 0 {
                let ops_per_sec = burst_size as f64 / burst_duration.as_secs_f64();
                if ops_per_sec > results.operations_per_second {
                    results.operations_per_second = ops_per_sec;
                }
            }

            // Brief pause between bursts
            thread::sleep(Duration::from_millis(100));
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_stress_test_results_success_rate_zero_operations() {
        let results = StressTestResults {
            total_operations: 0,
            successful_operations: 0,
            failed_operations: 0,
            average_latency_ms: 0.0,
            max_latency_ms: 0.0,
            min_latency_ms: 0.0,
            memory_usage_mb: 0.0,
            concurrent_threads: 0,
            test_duration_secs: 0.0,
            operations_per_second: 0.0,
            error_details: Vec::new(),
        };
        assert!((results.success_rate() - 0.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_stress_test_results_success_rate_all_success() {
        let results = StressTestResults {
            total_operations: 100,
            successful_operations: 100,
            failed_operations: 0,
            average_latency_ms: 1.0,
            max_latency_ms: 5.0,
            min_latency_ms: 0.5,
            memory_usage_mb: 10.0,
            concurrent_threads: 4,
            test_duration_secs: 1.0,
            operations_per_second: 100.0,
            error_details: Vec::new(),
        };
        assert!((results.success_rate() - 100.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_stress_test_results_success_rate_partial() {
        let results = StressTestResults {
            total_operations: 200,
            successful_operations: 150,
            failed_operations: 50,
            average_latency_ms: 2.0,
            max_latency_ms: 10.0,
            min_latency_ms: 0.1,
            memory_usage_mb: 20.0,
            concurrent_threads: 8,
            test_duration_secs: 5.0,
            operations_per_second: 40.0,
            error_details: vec!["error1".to_string()],
        };
        assert!((results.success_rate() - 75.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_stress_tester_creation() {
        let config = Config::default();
        let tester = StressTester::new(config);
        assert!(std::mem::size_of_val(&tester) > 0);
    }
}