Coverage Report

Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/trueno/src/chaos.rs
Line
Count
Source
1
//! Chaos Engineering Configuration
2
//!
3
//! This module provides chaos engineering infrastructure for testing Trueno under
4
//! adverse conditions. Integrated from renacer v0.4.1 (https://github.com/paiml/renacer).
5
//!
6
//! # Examples
7
//!
8
//! ```
9
//! use trueno::chaos::ChaosConfig;
10
//! use std::time::Duration;
11
//!
12
//! // Use gentle preset for gradual stress testing
13
//! let gentle = ChaosConfig::gentle();
14
//!
15
//! // Use aggressive preset for extreme conditions
16
//! let aggressive = ChaosConfig::aggressive();
17
//!
18
//! // Custom configuration using builder pattern
19
//! let custom = ChaosConfig::new()
20
//!     .with_memory_limit(100 * 1024 * 1024)  // 100 MB
21
//!     .with_cpu_limit(0.5)                    // 50% CPU
22
//!     .with_timeout(Duration::from_secs(30))
23
//!     .with_signal_injection(true)
24
//!     .build();
25
//! ```
26
27
use std::time::Duration;
28
29
/// Chaos engineering configuration for stress testing
30
///
31
/// Provides configurable limits and constraints for chaos testing scenarios.
32
/// All limits are optional and disabled by default (value of 0 means no limit).
33
#[derive(Debug, Clone, PartialEq)]
34
pub struct ChaosConfig {
35
    /// Memory limit in bytes (0 = no limit)
36
    pub memory_limit: usize,
37
    /// CPU usage limit as fraction 0.0-1.0 (0.0 = no limit)
38
    pub cpu_limit: f64,
39
    /// Maximum execution time
40
    pub timeout: Duration,
41
    /// Whether to inject random signals during execution
42
    pub signal_injection: bool,
43
}
44
45
impl Default for ChaosConfig {
46
0
    fn default() -> Self {
47
0
        Self {
48
0
            memory_limit: 0,
49
0
            cpu_limit: 0.0,
50
0
            timeout: Duration::from_secs(60),
51
0
            signal_injection: false,
52
0
        }
53
0
    }
54
}
55
56
impl ChaosConfig {
57
    /// Create a new chaos configuration with default values (no limits)
58
0
    pub fn new() -> Self {
59
0
        Self::default()
60
0
    }
61
62
    /// Set memory limit in bytes
63
    ///
64
    /// # Examples
65
    ///
66
    /// ```
67
    /// use trueno::chaos::ChaosConfig;
68
    ///
69
    /// let config = ChaosConfig::new()
70
    ///     .with_memory_limit(512 * 1024 * 1024); // 512 MB
71
    /// assert_eq!(config.memory_limit, 512 * 1024 * 1024);
72
    /// ```
73
0
    pub fn with_memory_limit(mut self, bytes: usize) -> Self {
74
0
        self.memory_limit = bytes;
75
0
        self
76
0
    }
77
78
    /// Set CPU usage limit as fraction (0.0-1.0)
79
    ///
80
    /// Values are automatically clamped to valid range.
81
    ///
82
    /// # Examples
83
    ///
84
    /// ```
85
    /// use trueno::chaos::ChaosConfig;
86
    ///
87
    /// let config = ChaosConfig::new().with_cpu_limit(0.75);
88
    /// assert_eq!(config.cpu_limit, 0.75);
89
    ///
90
    /// // Values outside range are clamped
91
    /// let clamped = ChaosConfig::new().with_cpu_limit(1.5);
92
    /// assert_eq!(clamped.cpu_limit, 1.0);
93
    /// ```
94
0
    pub fn with_cpu_limit(mut self, fraction: f64) -> Self {
95
0
        self.cpu_limit = fraction.clamp(0.0, 1.0);
96
0
        self
97
0
    }
98
99
    /// Set maximum execution timeout
100
    ///
101
    /// # Examples
102
    ///
103
    /// ```
104
    /// use trueno::chaos::ChaosConfig;
105
    /// use std::time::Duration;
106
    ///
107
    /// let config = ChaosConfig::new()
108
    ///     .with_timeout(Duration::from_secs(30));
109
    /// assert_eq!(config.timeout, Duration::from_secs(30));
110
    /// ```
111
0
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
112
0
        self.timeout = timeout;
113
0
        self
114
0
    }
115
116
    /// Enable/disable random signal injection during execution
117
    ///
118
    /// # Examples
119
    ///
120
    /// ```
121
    /// use trueno::chaos::ChaosConfig;
122
    ///
123
    /// let config = ChaosConfig::new().with_signal_injection(true);
124
    /// assert!(config.signal_injection);
125
    /// ```
126
0
    pub fn with_signal_injection(mut self, enabled: bool) -> Self {
127
0
        self.signal_injection = enabled;
128
0
        self
129
0
    }
130
131
    /// Finalize configuration (no-op, for builder pattern consistency)
132
0
    pub fn build(self) -> Self {
133
0
        self
134
0
    }
135
136
    /// Gentle chaos configuration preset
137
    ///
138
    /// - 512 MB memory limit
139
    /// - 80% CPU limit
140
    /// - 120 second timeout
141
    /// - No signal injection
142
    ///
143
    /// Suitable for gradual stress testing and CI environments.
144
    ///
145
    /// # Examples
146
    ///
147
    /// ```
148
    /// use trueno::chaos::ChaosConfig;
149
    /// use std::time::Duration;
150
    ///
151
    /// let config = ChaosConfig::gentle();
152
    /// assert_eq!(config.memory_limit, 512 * 1024 * 1024);
153
    /// assert_eq!(config.cpu_limit, 0.8);
154
    /// assert_eq!(config.timeout, Duration::from_secs(120));
155
    /// assert!(!config.signal_injection);
156
    /// ```
157
0
    pub fn gentle() -> Self {
158
0
        Self::new()
159
0
            .with_memory_limit(512 * 1024 * 1024)
160
0
            .with_cpu_limit(0.8)
161
0
            .with_timeout(Duration::from_secs(120))
162
0
    }
163
164
    /// Aggressive chaos configuration preset
165
    ///
166
    /// - 64 MB memory limit
167
    /// - 25% CPU limit
168
    /// - 10 second timeout
169
    /// - Signal injection enabled
170
    ///
171
    /// Suitable for extreme stress testing and finding edge cases.
172
    ///
173
    /// # Examples
174
    ///
175
    /// ```
176
    /// use trueno::chaos::ChaosConfig;
177
    /// use std::time::Duration;
178
    ///
179
    /// let config = ChaosConfig::aggressive();
180
    /// assert_eq!(config.memory_limit, 64 * 1024 * 1024);
181
    /// assert_eq!(config.cpu_limit, 0.25);
182
    /// assert_eq!(config.timeout, Duration::from_secs(10));
183
    /// assert!(config.signal_injection);
184
    /// ```
185
0
    pub fn aggressive() -> Self {
186
0
        Self::new()
187
0
            .with_memory_limit(64 * 1024 * 1024)
188
0
            .with_cpu_limit(0.25)
189
0
            .with_timeout(Duration::from_secs(10))
190
0
            .with_signal_injection(true)
191
0
    }
192
}
193
194
/// Result type for chaos operations
195
pub type ChaosResult<T> = Result<T, ChaosError>;
196
197
/// Errors that can occur during chaos testing
198
#[derive(Debug, Clone, PartialEq, Eq)]
199
pub enum ChaosError {
200
    /// Memory limit was exceeded during execution
201
    MemoryLimitExceeded {
202
        /// Configured memory limit in bytes
203
        limit: usize,
204
        /// Actual memory used in bytes
205
        used: usize,
206
    },
207
    /// Execution exceeded timeout
208
    Timeout {
209
        /// Time elapsed before timeout
210
        elapsed: Duration,
211
        /// Configured timeout limit
212
        limit: Duration,
213
    },
214
    /// Signal injection failed
215
    SignalInjectionFailed {
216
        /// Signal number that failed to inject
217
        signal: i32,
218
        /// Reason for failure
219
        reason: String,
220
    },
221
}
222
223
impl std::fmt::Display for ChaosError {
224
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225
0
        match self {
226
0
            ChaosError::MemoryLimitExceeded { limit, used } => {
227
0
                write!(f, "Memory limit exceeded: {} > {} bytes", used, limit)
228
            }
229
0
            ChaosError::Timeout { elapsed, limit } => {
230
0
                write!(f, "Timeout: {:?} > {:?}", elapsed, limit)
231
            }
232
0
            ChaosError::SignalInjectionFailed { signal, reason } => {
233
0
                write!(f, "Signal injection failed ({}): {}", signal, reason)
234
            }
235
        }
236
0
    }
237
}
238
239
impl std::error::Error for ChaosError {}
240
241
#[cfg(test)]
242
mod tests {
243
    use super::*;
244
245
    #[test]
246
    fn test_default_config() {
247
        let config = ChaosConfig::default();
248
        assert_eq!(config.memory_limit, 0);
249
        assert_eq!(config.cpu_limit, 0.0);
250
        assert_eq!(config.timeout, Duration::from_secs(60));
251
        assert!(!config.signal_injection);
252
    }
253
254
    #[test]
255
    fn test_builder_pattern() {
256
        let config = ChaosConfig::new()
257
            .with_memory_limit(1024)
258
            .with_cpu_limit(0.5)
259
            .with_timeout(Duration::from_secs(30))
260
            .with_signal_injection(true)
261
            .build();
262
263
        assert_eq!(config.memory_limit, 1024);
264
        assert_eq!(config.cpu_limit, 0.5);
265
        assert_eq!(config.timeout, Duration::from_secs(30));
266
        assert!(config.signal_injection);
267
    }
268
269
    #[test]
270
    fn test_cpu_limit_clamping() {
271
        let too_high = ChaosConfig::new().with_cpu_limit(1.5);
272
        assert_eq!(too_high.cpu_limit, 1.0);
273
274
        let too_low = ChaosConfig::new().with_cpu_limit(-0.5);
275
        assert_eq!(too_low.cpu_limit, 0.0);
276
277
        let valid = ChaosConfig::new().with_cpu_limit(0.75);
278
        assert_eq!(valid.cpu_limit, 0.75);
279
    }
280
281
    #[test]
282
    fn test_gentle_preset() {
283
        let gentle = ChaosConfig::gentle();
284
        assert_eq!(gentle.memory_limit, 512 * 1024 * 1024);
285
        assert_eq!(gentle.cpu_limit, 0.8);
286
        assert_eq!(gentle.timeout, Duration::from_secs(120));
287
        assert!(!gentle.signal_injection);
288
    }
289
290
    #[test]
291
    fn test_aggressive_preset() {
292
        let aggressive = ChaosConfig::aggressive();
293
        assert_eq!(aggressive.memory_limit, 64 * 1024 * 1024);
294
        assert_eq!(aggressive.cpu_limit, 0.25);
295
        assert_eq!(aggressive.timeout, Duration::from_secs(10));
296
        assert!(aggressive.signal_injection);
297
    }
298
299
    #[test]
300
    fn test_chaos_error_display() {
301
        let mem_err = ChaosError::MemoryLimitExceeded {
302
            limit: 1000,
303
            used: 2000,
304
        };
305
        assert_eq!(
306
            format!("{}", mem_err),
307
            "Memory limit exceeded: 2000 > 1000 bytes"
308
        );
309
310
        let timeout_err = ChaosError::Timeout {
311
            elapsed: Duration::from_secs(5),
312
            limit: Duration::from_secs(3),
313
        };
314
        assert!(format!("{}", timeout_err).contains("Timeout"));
315
316
        let signal_err = ChaosError::SignalInjectionFailed {
317
            signal: 9,
318
            reason: "test failure".to_string(),
319
        };
320
        assert_eq!(
321
            format!("{}", signal_err),
322
            "Signal injection failed (9): test failure"
323
        );
324
    }
325
}