mockforge_chaos/
config.rs

1//! Chaos engineering configuration
2
3use serde::{Deserialize, Serialize};
4
5/// Payload corruption type
6#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "snake_case")]
8pub enum CorruptionType {
9    /// No corruption
10    None,
11    /// Replace random bytes with random values
12    RandomBytes,
13    /// Truncate payload at random position
14    Truncate,
15    /// Flip random bits in the payload
16    BitFlip,
17}
18
19/// Error injection pattern
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21#[serde(rename_all = "snake_case", tag = "type")]
22pub enum ErrorPattern {
23    /// Burst pattern: inject N errors within a time interval
24    Burst {
25        /// Number of errors to inject in the burst
26        count: usize,
27        /// Time interval in milliseconds for the burst
28        interval_ms: u64,
29    },
30    /// Random pattern: inject errors with a probability
31    Random {
32        /// Probability of injecting an error (0.0-1.0)
33        probability: f64,
34    },
35    /// Sequential pattern: inject errors in a specific sequence
36    Sequential {
37        /// Sequence of status codes to inject in order
38        sequence: Vec<u16>,
39    },
40}
41
42impl Default for ErrorPattern {
43    fn default() -> Self {
44        ErrorPattern::Random { probability: 0.1 }
45    }
46}
47
48/// Main chaos engineering configuration
49#[derive(Debug, Clone, Serialize, Deserialize, Default)]
50pub struct ChaosConfig {
51    /// Enable chaos engineering
52    pub enabled: bool,
53    /// Latency injection configuration
54    pub latency: Option<LatencyConfig>,
55    /// Fault injection configuration
56    pub fault_injection: Option<FaultInjectionConfig>,
57    /// Rate limiting configuration
58    pub rate_limit: Option<RateLimitConfig>,
59    /// Traffic shaping configuration
60    pub traffic_shaping: Option<TrafficShapingConfig>,
61    /// Circuit breaker configuration
62    pub circuit_breaker: Option<CircuitBreakerConfig>,
63    /// Bulkhead configuration
64    pub bulkhead: Option<BulkheadConfig>,
65}
66
67/// Latency injection configuration
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct LatencyConfig {
70    /// Enable latency injection
71    pub enabled: bool,
72    /// Fixed delay in milliseconds
73    pub fixed_delay_ms: Option<u64>,
74    /// Random delay range (min, max) in milliseconds
75    pub random_delay_range_ms: Option<(u64, u64)>,
76    /// Jitter percentage (0-100)
77    pub jitter_percent: f64,
78    /// Probability of applying latency (0.0-1.0)
79    pub probability: f64,
80}
81
82impl Default for LatencyConfig {
83    fn default() -> Self {
84        Self {
85            enabled: false,
86            fixed_delay_ms: None,
87            random_delay_range_ms: None,
88            jitter_percent: 0.0,
89            probability: 1.0,
90        }
91    }
92}
93
94/// Fault injection configuration
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct FaultInjectionConfig {
97    /// Enable fault injection
98    pub enabled: bool,
99    /// HTTP error codes to inject
100    pub http_errors: Vec<u16>,
101    /// Probability of HTTP errors (0.0-1.0)
102    pub http_error_probability: f64,
103    /// Inject connection errors
104    pub connection_errors: bool,
105    /// Probability of connection errors (0.0-1.0)
106    pub connection_error_probability: f64,
107    /// Inject timeout errors
108    pub timeout_errors: bool,
109    /// Timeout duration in milliseconds
110    pub timeout_ms: u64,
111    /// Probability of timeout errors (0.0-1.0)
112    pub timeout_probability: f64,
113    /// Inject partial responses (incomplete data)
114    pub partial_responses: bool,
115    /// Probability of partial responses (0.0-1.0)
116    pub partial_response_probability: f64,
117    /// Enable payload corruption
118    pub payload_corruption: bool,
119    /// Probability of payload corruption (0.0-1.0)
120    pub payload_corruption_probability: f64,
121    /// Type of corruption to apply
122    pub corruption_type: CorruptionType,
123    /// Error injection pattern (burst, random, sequential)
124    #[serde(default)]
125    pub error_pattern: Option<ErrorPattern>,
126    /// Enable MockAI for dynamic error message generation
127    #[serde(default)]
128    pub mockai_enabled: bool,
129}
130
131impl Default for FaultInjectionConfig {
132    fn default() -> Self {
133        Self {
134            enabled: false,
135            http_errors: vec![500, 502, 503, 504],
136            http_error_probability: 0.1,
137            connection_errors: false,
138            connection_error_probability: 0.05,
139            timeout_errors: false,
140            timeout_ms: 5000,
141            timeout_probability: 0.05,
142            partial_responses: false,
143            partial_response_probability: 0.05,
144            payload_corruption: false,
145            payload_corruption_probability: 0.05,
146            corruption_type: CorruptionType::None,
147            error_pattern: None,
148            mockai_enabled: false,
149        }
150    }
151}
152
153/// Rate limiting configuration
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct RateLimitConfig {
156    /// Enable rate limiting
157    pub enabled: bool,
158    /// Maximum requests per second
159    pub requests_per_second: u32,
160    /// Burst size (number of requests allowed in burst)
161    pub burst_size: u32,
162    /// Per-IP rate limiting
163    pub per_ip: bool,
164    /// Per-endpoint rate limiting
165    pub per_endpoint: bool,
166}
167
168impl Default for RateLimitConfig {
169    fn default() -> Self {
170        Self {
171            enabled: false,
172            requests_per_second: 100,
173            burst_size: 10,
174            per_ip: false,
175            per_endpoint: false,
176        }
177    }
178}
179
180/// Traffic shaping configuration
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct TrafficShapingConfig {
183    /// Enable traffic shaping
184    pub enabled: bool,
185    /// Bandwidth limit in bytes per second (0 = unlimited)
186    pub bandwidth_limit_bps: u64,
187    /// Packet loss percentage (0-100)
188    pub packet_loss_percent: f64,
189    /// Maximum concurrent connections (0 = unlimited)
190    pub max_connections: u32,
191    /// Connection timeout in milliseconds
192    pub connection_timeout_ms: u64,
193}
194
195impl Default for TrafficShapingConfig {
196    fn default() -> Self {
197        Self {
198            enabled: false,
199            bandwidth_limit_bps: 0,
200            packet_loss_percent: 0.0,
201            max_connections: 0,
202            connection_timeout_ms: 30000,
203        }
204    }
205}
206
207/// Circuit breaker configuration
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct CircuitBreakerConfig {
210    /// Enable circuit breaker
211    pub enabled: bool,
212    /// Failure threshold before opening circuit
213    pub failure_threshold: u64,
214    /// Success threshold before closing circuit from half-open
215    pub success_threshold: u64,
216    /// Timeout before attempting to close circuit (in milliseconds)
217    pub timeout_ms: u64,
218    /// Half-open request limit
219    pub half_open_max_requests: u32,
220    /// Failure rate threshold (percentage, 0-100)
221    pub failure_rate_threshold: f64,
222    /// Minimum number of requests before calculating failure rate
223    pub min_requests_for_rate: u64,
224    /// Rolling window duration for failure rate calculation (in milliseconds)
225    pub rolling_window_ms: u64,
226}
227
228impl Default for CircuitBreakerConfig {
229    fn default() -> Self {
230        Self {
231            enabled: false,
232            failure_threshold: 5,
233            success_threshold: 2,
234            timeout_ms: 60000,
235            half_open_max_requests: 3,
236            failure_rate_threshold: 50.0,
237            min_requests_for_rate: 10,
238            rolling_window_ms: 10000,
239        }
240    }
241}
242
243/// Bulkhead configuration
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct BulkheadConfig {
246    /// Enable bulkhead
247    pub enabled: bool,
248    /// Maximum concurrent requests
249    pub max_concurrent_requests: u32,
250    /// Maximum queue size (0 = no queue)
251    pub max_queue_size: u32,
252    /// Queue timeout in milliseconds
253    pub queue_timeout_ms: u64,
254}
255
256/// Network profile for simulating different network conditions
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct NetworkProfile {
259    /// Profile name
260    pub name: String,
261    /// Profile description
262    pub description: String,
263    /// Chaos configuration for this profile
264    pub chaos_config: ChaosConfig,
265    /// Tags for categorization
266    #[serde(default)]
267    pub tags: Vec<String>,
268    /// Whether this is a built-in profile (not user-created)
269    #[serde(default)]
270    pub builtin: bool,
271}
272
273impl NetworkProfile {
274    /// Create a new network profile
275    pub fn new(name: String, description: String, chaos_config: ChaosConfig) -> Self {
276        Self {
277            name,
278            description,
279            chaos_config,
280            tags: Vec::new(),
281            builtin: false,
282        }
283    }
284
285    /// Create predefined network profiles
286    pub fn predefined_profiles() -> Vec<Self> {
287        vec![
288            // Slow 3G: High latency, packet loss, low bandwidth
289            Self {
290                name: "slow_3g".to_string(),
291                description:
292                    "Simulates slow 3G network: 400ms latency, 1% packet loss, 400KB/s bandwidth"
293                        .to_string(),
294                chaos_config: ChaosConfig {
295                    enabled: true,
296                    latency: Some(LatencyConfig {
297                        enabled: true,
298                        fixed_delay_ms: Some(400),
299                        random_delay_range_ms: Some((300, 500)),
300                        jitter_percent: 10.0,
301                        probability: 1.0,
302                    }),
303                    fault_injection: None,
304                    rate_limit: None,
305                    traffic_shaping: Some(TrafficShapingConfig {
306                        enabled: true,
307                        bandwidth_limit_bps: 400_000, // 400 KB/s
308                        packet_loss_percent: 1.0,
309                        max_connections: 0,
310                        connection_timeout_ms: 30000,
311                    }),
312                    circuit_breaker: None,
313                    bulkhead: None,
314                },
315                tags: vec!["mobile".to_string(), "slow".to_string(), "3g".to_string()],
316                builtin: true,
317            },
318            // Fast 3G: Moderate latency, low packet loss, higher bandwidth
319            Self {
320                name: "fast_3g".to_string(),
321                description:
322                    "Simulates fast 3G network: 150ms latency, 0.5% packet loss, 1.5MB/s bandwidth"
323                        .to_string(),
324                chaos_config: ChaosConfig {
325                    enabled: true,
326                    latency: Some(LatencyConfig {
327                        enabled: true,
328                        fixed_delay_ms: Some(150),
329                        random_delay_range_ms: Some((100, 200)),
330                        jitter_percent: 5.0,
331                        probability: 1.0,
332                    }),
333                    fault_injection: None,
334                    rate_limit: None,
335                    traffic_shaping: Some(TrafficShapingConfig {
336                        enabled: true,
337                        bandwidth_limit_bps: 1_500_000, // 1.5 MB/s
338                        packet_loss_percent: 0.5,
339                        max_connections: 0,
340                        connection_timeout_ms: 30000,
341                    }),
342                    circuit_breaker: None,
343                    bulkhead: None,
344                },
345                tags: vec!["mobile".to_string(), "fast".to_string(), "3g".to_string()],
346                builtin: true,
347            },
348            // Flaky Wi-Fi: Low latency but high packet loss and random disconnects
349            Self {
350                name: "flaky_wifi".to_string(),
351                description:
352                    "Simulates flaky Wi-Fi: 50ms latency, 5% packet loss, random connection errors"
353                        .to_string(),
354                chaos_config: ChaosConfig {
355                    enabled: true,
356                    latency: Some(LatencyConfig {
357                        enabled: true,
358                        fixed_delay_ms: Some(50),
359                        random_delay_range_ms: Some((30, 100)),
360                        jitter_percent: 20.0,
361                        probability: 1.0,
362                    }),
363                    fault_injection: Some(FaultInjectionConfig {
364                        enabled: true,
365                        http_errors: vec![500, 502, 503],
366                        http_error_probability: 0.05, // 5% chance of connection errors
367                        connection_errors: true,
368                        connection_error_probability: 0.03, // 3% chance of disconnects
369                        timeout_errors: false,
370                        timeout_ms: 5000,
371                        timeout_probability: 0.0,
372                        partial_responses: false,
373                        partial_response_probability: 0.0,
374                        payload_corruption: false,
375                        payload_corruption_probability: 0.0,
376                        corruption_type: CorruptionType::None,
377                        error_pattern: None,
378                        mockai_enabled: false,
379                    }),
380                    rate_limit: None,
381                    traffic_shaping: Some(TrafficShapingConfig {
382                        enabled: true,
383                        bandwidth_limit_bps: 0, // No bandwidth limit
384                        packet_loss_percent: 5.0,
385                        max_connections: 0,
386                        connection_timeout_ms: 30000,
387                    }),
388                    circuit_breaker: None,
389                    bulkhead: None,
390                },
391                tags: vec![
392                    "wifi".to_string(),
393                    "unstable".to_string(),
394                    "wireless".to_string(),
395                ],
396                builtin: true,
397            },
398            // Cable: Low latency, no packet loss, high bandwidth
399            Self {
400                name: "cable".to_string(),
401                description:
402                    "Simulates cable internet: 20ms latency, no packet loss, 10MB/s bandwidth"
403                        .to_string(),
404                chaos_config: ChaosConfig {
405                    enabled: true,
406                    latency: Some(LatencyConfig {
407                        enabled: true,
408                        fixed_delay_ms: Some(20),
409                        random_delay_range_ms: Some((10, 30)),
410                        jitter_percent: 2.0,
411                        probability: 1.0,
412                    }),
413                    fault_injection: None,
414                    rate_limit: None,
415                    traffic_shaping: Some(TrafficShapingConfig {
416                        enabled: true,
417                        bandwidth_limit_bps: 10_000_000, // 10 MB/s
418                        packet_loss_percent: 0.0,
419                        max_connections: 0,
420                        connection_timeout_ms: 30000,
421                    }),
422                    circuit_breaker: None,
423                    bulkhead: None,
424                },
425                tags: vec![
426                    "broadband".to_string(),
427                    "fast".to_string(),
428                    "stable".to_string(),
429                ],
430                builtin: true,
431            },
432            // Dial-up: Very high latency, packet loss, very low bandwidth
433            Self {
434                name: "dialup".to_string(),
435                description:
436                    "Simulates dial-up connection: 2000ms latency, 2% packet loss, 50KB/s bandwidth"
437                        .to_string(),
438                chaos_config: ChaosConfig {
439                    enabled: true,
440                    latency: Some(LatencyConfig {
441                        enabled: true,
442                        fixed_delay_ms: Some(2000),
443                        random_delay_range_ms: Some((1500, 2500)),
444                        jitter_percent: 15.0,
445                        probability: 1.0,
446                    }),
447                    fault_injection: None,
448                    rate_limit: None,
449                    traffic_shaping: Some(TrafficShapingConfig {
450                        enabled: true,
451                        bandwidth_limit_bps: 50_000, // 50 KB/s
452                        packet_loss_percent: 2.0,
453                        max_connections: 0,
454                        connection_timeout_ms: 60000, // Longer timeout for dial-up
455                    }),
456                    circuit_breaker: None,
457                    bulkhead: None,
458                },
459                tags: vec![
460                    "dialup".to_string(),
461                    "slow".to_string(),
462                    "legacy".to_string(),
463                ],
464                builtin: true,
465            },
466        ]
467    }
468}
469
470impl Default for BulkheadConfig {
471    fn default() -> Self {
472        Self {
473            enabled: false,
474            max_concurrent_requests: 100,
475            max_queue_size: 10,
476            queue_timeout_ms: 5000,
477        }
478    }
479}