mockforge_core/
reality.rs

1//! Reality Slider - Unified control for mock environment realism
2//!
3//! This module provides a unified control mechanism that transitions mock environments
4//! between "stubbed simplicity" (level 1) and "production chaos" (level 5) by
5//! automatically coordinating chaos engineering, latency injection, and MockAI behaviors.
6
7use crate::chaos_utilities::ChaosConfig;
8use crate::intelligent_behavior::config::IntelligentBehaviorConfig;
9use crate::latency::{LatencyDistribution, LatencyProfile};
10use serde::{Deserialize, Serialize};
11use std::sync::Arc;
12use tokio::sync::RwLock;
13
14/// Reality level for mock environments (1-5)
15///
16/// Each level represents a different degree of realism, from simple static mocks
17/// to full production-like chaos behavior.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum RealityLevel {
21    /// Level 1: Static Stubs - Simple, instant responses with no chaos
22    StaticStubs = 1,
23    /// Level 2: Light Simulation - Minimal latency, basic intelligence
24    LightSimulation = 2,
25    /// Level 3: Moderate Realism - Some chaos, moderate latency, full intelligence
26    ModerateRealism = 3,
27    /// Level 4: High Realism - Increased chaos, realistic latency, session state
28    HighRealism = 4,
29    /// Level 5: Production Chaos - Maximum chaos, production-like latency, full features
30    ProductionChaos = 5,
31}
32
33impl RealityLevel {
34    /// Get the numeric value (1-5)
35    pub fn value(&self) -> u8 {
36        *self as u8
37    }
38
39    /// Get a human-readable name
40    pub fn name(&self) -> &'static str {
41        match self {
42            RealityLevel::StaticStubs => "Static Stubs",
43            RealityLevel::LightSimulation => "Light Simulation",
44            RealityLevel::ModerateRealism => "Moderate Realism",
45            RealityLevel::HighRealism => "High Realism",
46            RealityLevel::ProductionChaos => "Production Chaos",
47        }
48    }
49
50    /// Get a short description
51    pub fn description(&self) -> &'static str {
52        match self {
53            RealityLevel::StaticStubs => "Simple, instant responses with no chaos",
54            RealityLevel::LightSimulation => "Minimal latency, basic intelligence",
55            RealityLevel::ModerateRealism => "Some chaos, moderate latency, full intelligence",
56            RealityLevel::HighRealism => "Increased chaos, realistic latency, session state",
57            RealityLevel::ProductionChaos => {
58                "Maximum chaos, production-like latency, full features"
59            }
60        }
61    }
62
63    /// Create from numeric value (1-5)
64    pub fn from_value(value: u8) -> Option<Self> {
65        match value {
66            1 => Some(RealityLevel::StaticStubs),
67            2 => Some(RealityLevel::LightSimulation),
68            3 => Some(RealityLevel::ModerateRealism),
69            4 => Some(RealityLevel::HighRealism),
70            5 => Some(RealityLevel::ProductionChaos),
71            _ => None,
72        }
73    }
74
75    /// Get all available levels
76    pub fn all() -> Vec<Self> {
77        vec![
78            RealityLevel::StaticStubs,
79            RealityLevel::LightSimulation,
80            RealityLevel::ModerateRealism,
81            RealityLevel::HighRealism,
82            RealityLevel::ProductionChaos,
83        ]
84    }
85}
86
87impl Default for RealityLevel {
88    fn default() -> Self {
89        RealityLevel::ModerateRealism
90    }
91}
92
93/// Reality configuration that maps a level to specific subsystem settings
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct RealityConfig {
96    /// Current reality level
97    pub level: RealityLevel,
98    /// Chaos configuration for this level
99    pub chaos: ChaosConfig,
100    /// Latency profile for this level
101    pub latency: LatencyProfile,
102    /// MockAI configuration for this level
103    pub mockai: IntelligentBehaviorConfig,
104}
105
106impl RealityConfig {
107    /// Create configuration for a specific reality level
108    pub fn for_level(level: RealityLevel) -> Self {
109        match level {
110            RealityLevel::StaticStubs => Self::level_1_static_stubs(),
111            RealityLevel::LightSimulation => Self::level_2_light_simulation(),
112            RealityLevel::ModerateRealism => Self::level_3_moderate_realism(),
113            RealityLevel::HighRealism => Self::level_4_high_realism(),
114            RealityLevel::ProductionChaos => Self::level_5_production_chaos(),
115        }
116    }
117
118    /// Level 1: Static Stubs
119    ///
120    /// - Chaos: Disabled
121    /// - Latency: 0ms (instant)
122    /// - MockAI: Disabled (static responses only)
123    fn level_1_static_stubs() -> Self {
124        Self {
125            level: RealityLevel::StaticStubs,
126            chaos: ChaosConfig {
127                enabled: false,
128                error_rate: 0.0,
129                delay_rate: 0.0,
130                min_delay_ms: 0,
131                max_delay_ms: 0,
132                status_codes: vec![],
133                inject_timeouts: false,
134                timeout_ms: 0,
135            },
136            latency: LatencyProfile {
137                base_ms: 0,
138                jitter_ms: 0,
139                distribution: LatencyDistribution::Fixed,
140                std_dev_ms: None,
141                pareto_shape: None,
142                min_ms: 0,
143                max_ms: Some(0),
144                tag_overrides: Default::default(),
145            },
146            mockai: IntelligentBehaviorConfig {
147                enabled: false,
148                ..Default::default()
149            },
150        }
151    }
152
153    /// Level 2: Light Simulation
154    ///
155    /// - Chaos: Disabled
156    /// - Latency: 10-50ms (minimal)
157    /// - MockAI: Enabled (basic intelligence)
158    fn level_2_light_simulation() -> Self {
159        Self {
160            level: RealityLevel::LightSimulation,
161            chaos: ChaosConfig {
162                enabled: false,
163                error_rate: 0.0,
164                delay_rate: 0.0,
165                min_delay_ms: 0,
166                max_delay_ms: 0,
167                status_codes: vec![],
168                inject_timeouts: false,
169                timeout_ms: 0,
170            },
171            latency: LatencyProfile {
172                base_ms: 30,
173                jitter_ms: 20,
174                distribution: LatencyDistribution::Fixed,
175                std_dev_ms: None,
176                pareto_shape: None,
177                min_ms: 10,
178                max_ms: Some(50),
179                tag_overrides: Default::default(),
180            },
181            mockai: IntelligentBehaviorConfig {
182                enabled: true,
183                ..Default::default()
184            },
185        }
186    }
187
188    /// Level 3: Moderate Realism
189    ///
190    /// - Chaos: Enabled (5% error rate, 10% delay rate)
191    /// - Latency: 50-200ms (moderate)
192    /// - MockAI: Enabled (full intelligence)
193    fn level_3_moderate_realism() -> Self {
194        Self {
195            level: RealityLevel::ModerateRealism,
196            chaos: ChaosConfig {
197                enabled: true,
198                error_rate: 0.05,
199                delay_rate: 0.10,
200                min_delay_ms: 50,
201                max_delay_ms: 200,
202                status_codes: vec![500, 502, 503],
203                inject_timeouts: false,
204                timeout_ms: 0,
205            },
206            latency: LatencyProfile {
207                base_ms: 125,
208                jitter_ms: 75,
209                distribution: LatencyDistribution::Normal,
210                std_dev_ms: Some(30.0),
211                pareto_shape: None,
212                min_ms: 50,
213                max_ms: Some(200),
214                tag_overrides: Default::default(),
215            },
216            mockai: IntelligentBehaviorConfig {
217                enabled: true,
218                ..Default::default()
219            },
220        }
221    }
222
223    /// Level 4: High Realism
224    ///
225    /// - Chaos: Enabled (10% error rate, 20% delay rate)
226    /// - Latency: 100-500ms (realistic)
227    /// - MockAI: Enabled (full intelligence + session state)
228    fn level_4_high_realism() -> Self {
229        Self {
230            level: RealityLevel::HighRealism,
231            chaos: ChaosConfig {
232                enabled: true,
233                error_rate: 0.10,
234                delay_rate: 0.20,
235                min_delay_ms: 100,
236                max_delay_ms: 500,
237                status_codes: vec![500, 502, 503, 504],
238                inject_timeouts: false,
239                timeout_ms: 0,
240            },
241            latency: LatencyProfile {
242                base_ms: 300,
243                jitter_ms: 200,
244                distribution: LatencyDistribution::Normal,
245                std_dev_ms: Some(80.0),
246                pareto_shape: None,
247                min_ms: 100,
248                max_ms: Some(500),
249                tag_overrides: Default::default(),
250            },
251            mockai: IntelligentBehaviorConfig {
252                enabled: true,
253                performance: crate::intelligent_behavior::config::PerformanceConfig {
254                    max_history_length: 100,
255                    session_timeout_seconds: 3600,
256                    ..Default::default()
257                },
258                ..Default::default()
259            },
260        }
261    }
262
263    /// Level 5: Production Chaos
264    ///
265    /// - Chaos: Enabled (15% error rate, 30% delay rate, timeouts enabled)
266    /// - Latency: 200-2000ms (production-like, heavy-tailed)
267    /// - MockAI: Enabled (full intelligence + session state + mutations)
268    fn level_5_production_chaos() -> Self {
269        Self {
270            level: RealityLevel::ProductionChaos,
271            chaos: ChaosConfig {
272                enabled: true,
273                error_rate: 0.15,
274                delay_rate: 0.30,
275                min_delay_ms: 200,
276                max_delay_ms: 2000,
277                status_codes: vec![500, 502, 503, 504, 408],
278                inject_timeouts: true,
279                timeout_ms: 5000,
280            },
281            latency: LatencyProfile {
282                base_ms: 1100,
283                jitter_ms: 900,
284                distribution: LatencyDistribution::Pareto,
285                std_dev_ms: None,
286                pareto_shape: Some(2.0),
287                min_ms: 200,
288                max_ms: Some(2000),
289                tag_overrides: Default::default(),
290            },
291            mockai: IntelligentBehaviorConfig {
292                enabled: true,
293                performance: crate::intelligent_behavior::config::PerformanceConfig {
294                    max_history_length: 200,
295                    session_timeout_seconds: 7200,
296                    ..Default::default()
297                },
298                ..Default::default()
299            },
300        }
301    }
302}
303
304impl Default for RealityConfig {
305    fn default() -> Self {
306        Self::for_level(RealityLevel::default())
307    }
308}
309
310/// Reality preset for export/import
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct RealityPreset {
313    /// Preset name
314    pub name: String,
315    /// Preset description
316    pub description: Option<String>,
317    /// Reality configuration
318    pub config: RealityConfig,
319    /// Metadata
320    pub metadata: Option<PresetMetadata>,
321}
322
323/// Preset metadata
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct PresetMetadata {
326    /// Created timestamp
327    pub created_at: Option<String>,
328    /// Author
329    pub author: Option<String>,
330    /// Tags
331    pub tags: Vec<String>,
332    /// Version
333    pub version: Option<String>,
334}
335
336impl Default for PresetMetadata {
337    fn default() -> Self {
338        Self {
339            created_at: None,
340            author: None,
341            tags: vec![],
342            version: Some("1.0".to_string()),
343        }
344    }
345}
346
347/// Reality engine that coordinates chaos, latency, and MockAI subsystems
348///
349/// This engine applies the appropriate settings to each subsystem based on
350/// the current reality level. It acts as a coordinator and doesn't own the
351/// subsystems directly, but provides configuration that can be applied to them.
352#[derive(Debug, Clone)]
353pub struct RealityEngine {
354    /// Current reality configuration
355    config: Arc<RwLock<RealityConfig>>,
356}
357
358impl RealityEngine {
359    /// Create a new reality engine with default level
360    pub fn new() -> Self {
361        Self {
362            config: Arc::new(RwLock::new(RealityConfig::default())),
363        }
364    }
365
366    /// Create a new reality engine with a specific level
367    pub fn with_level(level: RealityLevel) -> Self {
368        Self {
369            config: Arc::new(RwLock::new(RealityConfig::for_level(level))),
370        }
371    }
372
373    /// Get the current reality level
374    pub async fn get_level(&self) -> RealityLevel {
375        self.config.read().await.level
376    }
377
378    /// Set the reality level and update configuration
379    pub async fn set_level(&self, level: RealityLevel) {
380        let mut config = self.config.write().await;
381        *config = RealityConfig::for_level(level);
382    }
383
384    /// Get the current reality configuration
385    pub async fn get_config(&self) -> RealityConfig {
386        self.config.read().await.clone()
387    }
388
389    /// Get chaos configuration for current level
390    pub async fn get_chaos_config(&self) -> ChaosConfig {
391        self.config.read().await.chaos.clone()
392    }
393
394    /// Get latency profile for current level
395    pub async fn get_latency_profile(&self) -> LatencyProfile {
396        self.config.read().await.latency.clone()
397    }
398
399    /// Get MockAI configuration for current level
400    pub async fn get_mockai_config(&self) -> IntelligentBehaviorConfig {
401        self.config.read().await.mockai.clone()
402    }
403
404    /// Create a preset from current configuration
405    pub async fn create_preset(&self, name: String, description: Option<String>) -> RealityPreset {
406        let config = self.config.read().await.clone();
407        RealityPreset {
408            name,
409            description,
410            config,
411            metadata: Some(PresetMetadata {
412                created_at: Some(chrono::Utc::now().to_rfc3339()),
413                author: None,
414                tags: vec![],
415                version: Some("1.0".to_string()),
416            }),
417        }
418    }
419
420    /// Apply a preset configuration
421    pub async fn apply_preset(&self, preset: RealityPreset) {
422        let mut config = self.config.write().await;
423        *config = preset.config;
424    }
425
426    /// Apply reality configuration to a ServerConfig
427    ///
428    /// This method updates the provided ServerConfig with chaos, latency, and MockAI
429    /// settings from the current reality level. This should be called when initializing
430    /// the server or when the reality level changes.
431    pub async fn apply_to_config(&self, config: &mut crate::config::ServerConfig) {
432        let reality_config = self.get_config().await;
433
434        // Apply chaos configuration
435        if config.reality.enabled {
436            // Update chaos config if it exists in observability
437            if let Some(ref mut chaos_eng) = config.observability.chaos {
438                chaos_eng.enabled = reality_config.chaos.enabled;
439                if let Some(ref mut fault) = chaos_eng.fault_injection {
440                    fault.enabled = reality_config.chaos.enabled;
441                    fault.http_error_probability = reality_config.chaos.error_rate;
442                    fault.timeout_errors = reality_config.chaos.inject_timeouts;
443                    fault.timeout_ms = reality_config.chaos.timeout_ms;
444                }
445                if let Some(ref mut latency) = chaos_eng.latency {
446                    latency.enabled = reality_config.latency.base_ms > 0;
447                    latency.fixed_delay_ms = Some(reality_config.latency.base_ms);
448                    latency.jitter_percent = if reality_config.latency.jitter_ms > 0 {
449                        (reality_config.latency.jitter_ms as f64
450                            / reality_config.latency.base_ms as f64)
451                            .min(1.0)
452                    } else {
453                        0.0
454                    };
455                }
456            }
457        }
458
459        // Apply latency configuration
460        if config.reality.enabled {
461            config.core.default_latency = reality_config.latency.clone();
462            config.core.latency_enabled = reality_config.latency.base_ms > 0;
463        }
464
465        // Apply MockAI configuration
466        if config.reality.enabled {
467            config.mockai.enabled = reality_config.mockai.enabled;
468            config.mockai.intelligent_behavior = reality_config.mockai.clone();
469        }
470    }
471}
472
473impl Default for RealityEngine {
474    fn default() -> Self {
475        Self::new()
476    }
477}
478
479#[cfg(test)]
480mod tests {
481    use super::*;
482
483    #[test]
484    fn test_reality_level_values() {
485        assert_eq!(RealityLevel::StaticStubs.value(), 1);
486        assert_eq!(RealityLevel::LightSimulation.value(), 2);
487        assert_eq!(RealityLevel::ModerateRealism.value(), 3);
488        assert_eq!(RealityLevel::HighRealism.value(), 4);
489        assert_eq!(RealityLevel::ProductionChaos.value(), 5);
490    }
491
492    #[test]
493    fn test_reality_level_from_value() {
494        assert_eq!(RealityLevel::from_value(1), Some(RealityLevel::StaticStubs));
495        assert_eq!(RealityLevel::from_value(3), Some(RealityLevel::ModerateRealism));
496        assert_eq!(RealityLevel::from_value(5), Some(RealityLevel::ProductionChaos));
497        assert_eq!(RealityLevel::from_value(0), None);
498        assert_eq!(RealityLevel::from_value(6), None);
499    }
500
501    #[test]
502    fn test_level_1_config() {
503        let config = RealityConfig::for_level(RealityLevel::StaticStubs);
504        assert!(!config.chaos.enabled);
505        assert_eq!(config.latency.base_ms, 0);
506        assert!(!config.mockai.enabled);
507    }
508
509    #[test]
510    fn test_level_5_config() {
511        let config = RealityConfig::for_level(RealityLevel::ProductionChaos);
512        assert!(config.chaos.enabled);
513        assert!(config.chaos.inject_timeouts);
514        assert_eq!(config.chaos.error_rate, 0.15);
515        assert!(config.latency.base_ms >= 200);
516        assert!(config.mockai.enabled);
517    }
518
519    #[tokio::test]
520    async fn test_reality_engine() {
521        let engine = RealityEngine::with_level(RealityLevel::StaticStubs);
522        assert_eq!(engine.get_level().await, RealityLevel::StaticStubs);
523
524        engine.set_level(RealityLevel::ProductionChaos).await;
525        assert_eq!(engine.get_level().await, RealityLevel::ProductionChaos);
526
527        let chaos_config = engine.get_chaos_config().await;
528        assert!(chaos_config.enabled);
529    }
530
531    #[tokio::test]
532    async fn test_preset_creation() {
533        let engine = RealityEngine::with_level(RealityLevel::ModerateRealism);
534        let preset = engine
535            .create_preset("test-preset".to_string(), Some("Test description".to_string()))
536            .await;
537
538        assert_eq!(preset.name, "test-preset");
539        assert_eq!(preset.config.level, RealityLevel::ModerateRealism);
540        assert!(preset.metadata.is_some());
541    }
542}