## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/realizar/src/bench/load_testing.rs

    Line
    Count
    Source
    1
    //! Load testing framework for stress testing inference servers
    2
    //!
    3
    //! Extracted from bench/mod.rs (PMAT-802) to reduce module size.
    4
    //! Contains:
    5
    //! - LoadTestConfig for wrk2-style load testing configuration
    6
    //! - LoadTestResult for test outcomes
    7
    //! - LoadTestRunner for executing load tests
    8
    9
    #![allow(clippy::cast_precision_loss)]
    10
    11
    use serde::{Deserialize, Serialize};
    12
    13
    14
    // ============================================================================
    15
    // Load Testing (Section 14.1)
    16
    // ============================================================================
    17
    18
    /// Configuration for load testing
    19
    ///
    20
    /// Per spec §14: Implements wrk2-style load testing with configurable
    21
    /// concurrency, duration, and target rates.
    22
    #[derive(Debug, Clone, Serialize, Deserialize)]
    23
    pub struct LoadTestConfig {
    24
        /// Number of concurrent connections/threads
    25
        pub concurrency: usize,
    26
        /// Test duration in seconds
    27
        pub duration_secs: u64,
    28
        /// Target requests per second (0 = unlimited)
    29
        pub target_rps: f64,
    30
        /// Request timeout in milliseconds
    31
        pub timeout_ms: u64,
    32
        /// Warm-up period in seconds
    33
        pub warmup_secs: u64,
    34
        /// Target latency threshold (p99) in milliseconds
    35
        pub latency_threshold_ms: f64,
    36
    }
    37
    38
    impl Default for LoadTestConfig {
    39
    4
        fn default() -> Self {
    40
    4
            Self {
    41
    4
                concurrency: 10,
    42
    4
                duration_secs: 60,
    43
    4
                target_rps: 0.0, // Unlimited
    44
    4
                timeout_ms: 5000,
    45
    4
                warmup_secs: 5,
    46
    4
                latency_threshold_ms: 500.0, // Per spec: <500ms p99 target
    47
    4
            }
    48
    4
        }
    49
    }
    50
    51
    impl LoadTestConfig {
    52
        /// Create config for stress testing
    53
        #[must_use]
    54
    1
        pub fn for_stress_test() -> Self {
    55
    1
            Self {
    56
    1
                concurrency: 100,
    57
    1
                duration_secs: 300,
    58
    1
                target_rps: 0.0,
    59
    1
                timeout_ms: 10_000,
    60
    1
                warmup_secs: 10,
    61
    1
                latency_threshold_ms: 1000.0,
    62
    1
            }
    63
    1
        }
    64
    65
        /// Create config for latency-focused testing
    66
        #[must_use]
    67
    1
        pub fn for_latency_test() -> Self {
    68
    1
            Self {
    69
    1
                concurrency: 1,
    70
    1
                duration_secs: 60,
    71
    1
                target_rps: 10.0, // Fixed rate
    72
    1
                timeout_ms: 2000,
    73
    1
                warmup_secs: 5,
    74
    1
                latency_threshold_ms: 200.0,
    75
    1
            }
    76
    1
        }
    77
    78
        /// Validate the configuration
    79
        #[must_use]
    80
    2
        pub fn is_valid(&self) -> bool {
    81
    2
            self.concurrency > 0
    82
    1
                && self.duration_secs > 0
    83
    1
                && self.timeout_ms > 0
    84
    1
                && self.latency_threshold_ms > 0.0
    85
    2
        }
    86
    }
    87
    88
    /// Results from a load test run
    89
    #[derive(Debug, Clone, Serialize, Deserialize)]
    90
    pub struct LoadTestResult {
    91
        /// Total requests made
    92
        pub total_requests: usize,
    93
        /// Successful requests
    94
        pub successful_requests: usize,
    95
        /// Failed requests
    96
        pub failed_requests: usize,
    97
        /// Requests per second (achieved)
    98
        pub rps_achieved: f64,
    99
        /// Latency percentiles in milliseconds
    100
        pub latency_p50_ms: f64,
    101
        /// Latency p95 in milliseconds
    102
        pub latency_p95_ms: f64,
    103
        /// Latency p99 in milliseconds
    104
        pub latency_p99_ms: f64,
    105
        /// Maximum latency in milliseconds
    106
        pub latency_max_ms: f64,
    107
        /// Total data transferred in bytes
    108
        pub data_transferred_bytes: u64,
    109
        /// Test duration in seconds
    110
        pub duration_secs: f64,
    111
        /// Error rate (0.0-1.0)
    112
        pub error_rate: f64,
    113
        /// Whether the test passed the latency threshold
    114
        pub passed_latency_threshold: bool,
    115
    }
    116
    117
    impl LoadTestResult {
    118
        /// Check if the load test passed all thresholds
    119
        #[must_use]
    120
    3
        pub fn is_passing(&self) -> bool {
    121
    3
            self.passed_latency_threshold && self.error_rate < 0.012 // <1% error rate
    122
    3
        }
    123
    124
        /// Calculate throughput in MB/s
    125
        #[must_use]
    126
    1
        pub fn throughput_mbps(&self) -> f64 {
    127
    1
            if self.duration_secs > 0.0 {
    128
    1
                (self.data_transferred_bytes as f64 / 1_000_000.0) / self.duration_secs
    129
            } else {
    130
    0
                0.0
    131
            }
    132
    1
        }
    133
    }
    134
    135
    /// Load test runner
    136
    #[derive(Debug)]
    137
    pub struct LoadTestRunner {
    138
        config: LoadTestConfig,
    139
    }
    140
    141
    impl LoadTestRunner {
    142
        /// Create a new load test runner
    143
        #[must_use]
    144
    1
        pub fn new(config: LoadTestConfig) -> Self {
    145
    1
            Self { config }
    146
    1
        }
    147
    148
        /// Get the configuration
    149
        #[must_use]
    150
    0
        pub fn config(&self) -> &LoadTestConfig {
    151
    0
            &self.config
    152
    0
        }
    153
    154
        /// Simulate a load test run (for testing purposes)
    155
        ///
    156
        /// In production, this would make actual HTTP requests.
    157
        #[must_use]
    158
    1
        pub fn simulate_run(&self) -> LoadTestResult {
    159
            // Simulate based on configuration
    160
    1
            let total_requests =
    161
    1
                (self.config.concurrency as f64 * self.config.duration_secs as f64 * 10.0) as usize;
    162
    1
            let error_count = total_requests / 100; // 1% error rate
    163
    1
            let successful = total_requests - error_count;
    164
    165
            // Simulate latencies based on concurrency
    166
            // Higher concurrency = higher latencies
    167
    1
            let base_latency = 20.0; // 20ms base
    168
    1
            let concurrency_factor = (self.config.concurrency as f64).ln();
    169
    170
    1
            let p50 = base_latency + concurrency_factor * 5.0;
    171
    1
            let p95 = p50 * 2.5;
    172
    1
            let p99 = p50 * 4.0;
    173
    1
            let max = p99 * 2.0;
    174
    175
    1
            let duration = self.config.duration_secs as f64;
    176
    1
            let rps = if duration > 0.0 {
    177
    1
                total_requests as f64 / duration
    178
            } else {
    179
    0
                0.0
    180
            };
    181
    182
    1
            LoadTestResult {
    183
    1
                total_requests,
    184
    1
                successful_requests: successful,
    185
    1
                failed_requests: error_count,
    186
    1
                rps_achieved: rps,
    187
    1
                latency_p50_ms: p50,
    188
    1
                latency_p95_ms: p95,
    189
    1
                latency_p99_ms: p99,
    190
    1
                latency_max_ms: max,
    191
    1
                data_transferred_bytes: (total_requests * 1024) as u64, // ~1KB per request
    192
    1
                duration_secs: duration,
    193
    1
                error_rate: error_count as f64 / total_requests as f64,
    194
    1
                passed_latency_threshold: p99 < self.config.latency_threshold_ms,
    195
    1
            }
    196
    1
        }
    197
    }