mockforge_runtime_daemon/
config.rs

1//! Configuration for the runtime daemon
2
3use serde::{Deserialize, Serialize};
4
5/// Configuration for the runtime daemon
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct RuntimeDaemonConfig {
8    /// Whether the runtime daemon is enabled
9    #[serde(default = "default_enabled")]
10    pub enabled: bool,
11
12    /// Whether to auto-create mocks on 404 responses
13    #[serde(default = "default_true")]
14    pub auto_create_on_404: bool,
15
16    /// Whether to use AI generation for mock responses
17    #[serde(default = "default_false")]
18    pub ai_generation: bool,
19
20    /// Whether to generate types (TypeScript/JSON schema)
21    #[serde(default = "default_false")]
22    pub generate_types: bool,
23
24    /// Whether to generate client stubs
25    #[serde(default = "default_false")]
26    pub generate_client_stubs: bool,
27
28    /// Whether to update OpenAPI schema automatically
29    #[serde(default = "default_false")]
30    pub update_openapi: bool,
31
32    /// Whether to create scenarios automatically
33    #[serde(default = "default_false")]
34    pub create_scenario: bool,
35
36    /// Workspace directory for saving generated mocks
37    #[serde(default)]
38    pub workspace_dir: Option<String>,
39
40    /// Patterns to exclude from auto-generation (e.g., ["/health", "/metrics"])
41    #[serde(default)]
42    pub exclude_patterns: Vec<String>,
43}
44
45impl RuntimeDaemonConfig {
46    /// Load configuration from environment variables
47    pub fn from_env() -> Self {
48        let enabled = std::env::var("MOCKFORGE_RUNTIME_DAEMON_ENABLED")
49            .unwrap_or_else(|_| "false".to_string())
50            .parse::<bool>()
51            .unwrap_or(false);
52
53        let auto_create_on_404 = std::env::var("MOCKFORGE_RUNTIME_DAEMON_AUTO_CREATE_ON_404")
54            .unwrap_or_else(|_| "true".to_string())
55            .parse::<bool>()
56            .unwrap_or(true);
57
58        let ai_generation = std::env::var("MOCKFORGE_RUNTIME_DAEMON_AI_GENERATION")
59            .unwrap_or_else(|_| "false".to_string())
60            .parse::<bool>()
61            .unwrap_or(false);
62
63        let generate_types = std::env::var("MOCKFORGE_RUNTIME_DAEMON_GENERATE_TYPES")
64            .unwrap_or_else(|_| "false".to_string())
65            .parse::<bool>()
66            .unwrap_or(false);
67
68        let generate_client_stubs = std::env::var("MOCKFORGE_RUNTIME_DAEMON_GENERATE_CLIENT_STUBS")
69            .unwrap_or_else(|_| "false".to_string())
70            .parse::<bool>()
71            .unwrap_or(false);
72
73        let update_openapi = std::env::var("MOCKFORGE_RUNTIME_DAEMON_UPDATE_OPENAPI")
74            .unwrap_or_else(|_| "false".to_string())
75            .parse::<bool>()
76            .unwrap_or(false);
77
78        let create_scenario = std::env::var("MOCKFORGE_RUNTIME_DAEMON_CREATE_SCENARIO")
79            .unwrap_or_else(|_| "false".to_string())
80            .parse::<bool>()
81            .unwrap_or(false);
82
83        let workspace_dir = std::env::var("MOCKFORGE_RUNTIME_DAEMON_WORKSPACE_DIR").ok();
84
85        // Parse exclude patterns from comma-separated env var
86        let exclude_patterns = std::env::var("MOCKFORGE_RUNTIME_DAEMON_EXCLUDE_PATTERNS")
87            .unwrap_or_else(|_| "/health,/metrics,/__mockforge".to_string())
88            .split(',')
89            .map(|s| s.trim().to_string())
90            .filter(|s| !s.is_empty())
91            .collect();
92
93        Self {
94            enabled,
95            auto_create_on_404,
96            ai_generation,
97            generate_types,
98            generate_client_stubs,
99            update_openapi,
100            create_scenario,
101            workspace_dir,
102            exclude_patterns,
103        }
104    }
105}
106
107impl Default for RuntimeDaemonConfig {
108    fn default() -> Self {
109        Self {
110            enabled: false,
111            auto_create_on_404: true,
112            ai_generation: false,
113            generate_types: false,
114            generate_client_stubs: false,
115            update_openapi: false,
116            create_scenario: false,
117            workspace_dir: None,
118            exclude_patterns: vec![
119                "/health".to_string(),
120                "/metrics".to_string(),
121                "/__mockforge".to_string(),
122            ],
123        }
124    }
125}
126
127fn default_enabled() -> bool {
128    false
129}
130
131fn default_true() -> bool {
132    true
133}
134
135fn default_false() -> bool {
136    false
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_runtime_daemon_config_default() {
145        let config = RuntimeDaemonConfig::default();
146        assert!(!config.enabled);
147        assert!(config.auto_create_on_404);
148        assert!(!config.ai_generation);
149        assert!(!config.generate_types);
150        assert!(!config.generate_client_stubs);
151        assert!(!config.update_openapi);
152        assert!(!config.create_scenario);
153        assert!(config.workspace_dir.is_none());
154    }
155
156    #[test]
157    fn test_runtime_daemon_config_default_exclude_patterns() {
158        let config = RuntimeDaemonConfig::default();
159        assert_eq!(config.exclude_patterns.len(), 3);
160        assert!(config.exclude_patterns.contains(&"/health".to_string()));
161        assert!(config.exclude_patterns.contains(&"/metrics".to_string()));
162        assert!(config.exclude_patterns.contains(&"/__mockforge".to_string()));
163    }
164
165    #[test]
166    fn test_runtime_daemon_config_clone() {
167        let config = RuntimeDaemonConfig {
168            enabled: true,
169            auto_create_on_404: false,
170            ai_generation: true,
171            generate_types: true,
172            generate_client_stubs: true,
173            update_openapi: true,
174            create_scenario: true,
175            workspace_dir: Some("/tmp/mocks".to_string()),
176            exclude_patterns: vec!["/custom".to_string()],
177        };
178
179        let cloned = config.clone();
180        assert_eq!(config.enabled, cloned.enabled);
181        assert_eq!(config.ai_generation, cloned.ai_generation);
182        assert_eq!(config.workspace_dir, cloned.workspace_dir);
183        assert_eq!(config.exclude_patterns, cloned.exclude_patterns);
184    }
185
186    #[test]
187    fn test_runtime_daemon_config_debug() {
188        let config = RuntimeDaemonConfig::default();
189        let debug = format!("{:?}", config);
190        assert!(debug.contains("RuntimeDaemonConfig"));
191        assert!(debug.contains("enabled"));
192    }
193
194    #[test]
195    fn test_runtime_daemon_config_serialize() {
196        let config = RuntimeDaemonConfig::default();
197        let json = serde_json::to_string(&config).unwrap();
198        assert!(json.contains("\"enabled\":false"));
199        assert!(json.contains("\"auto_create_on_404\":true"));
200    }
201
202    #[test]
203    fn test_runtime_daemon_config_deserialize() {
204        let json = r#"{
205            "enabled": true,
206            "auto_create_on_404": false,
207            "ai_generation": true,
208            "workspace_dir": "/custom/path"
209        }"#;
210
211        let config: RuntimeDaemonConfig = serde_json::from_str(json).unwrap();
212        assert!(config.enabled);
213        assert!(!config.auto_create_on_404);
214        assert!(config.ai_generation);
215        assert_eq!(config.workspace_dir, Some("/custom/path".to_string()));
216    }
217
218    #[test]
219    fn test_runtime_daemon_config_deserialize_defaults() {
220        let json = r#"{}"#;
221        let config: RuntimeDaemonConfig = serde_json::from_str(json).unwrap();
222        // Should use default values
223        assert!(!config.enabled);
224        assert!(config.auto_create_on_404);
225        assert!(!config.ai_generation);
226    }
227
228    #[test]
229    fn test_runtime_daemon_config_with_custom_exclude() {
230        let config = RuntimeDaemonConfig {
231            exclude_patterns: vec![
232                "/internal".to_string(),
233                "/admin".to_string(),
234                "secret".to_string(),
235            ],
236            ..Default::default()
237        };
238
239        assert_eq!(config.exclude_patterns.len(), 3);
240        assert!(config.exclude_patterns.contains(&"/internal".to_string()));
241    }
242
243    #[test]
244    fn test_runtime_daemon_config_all_features_enabled() {
245        let config = RuntimeDaemonConfig {
246            enabled: true,
247            auto_create_on_404: true,
248            ai_generation: true,
249            generate_types: true,
250            generate_client_stubs: true,
251            update_openapi: true,
252            create_scenario: true,
253            workspace_dir: Some("./workspace".to_string()),
254            exclude_patterns: vec![],
255        };
256
257        assert!(config.enabled);
258        assert!(config.ai_generation);
259        assert!(config.generate_types);
260        assert!(config.generate_client_stubs);
261        assert!(config.update_openapi);
262        assert!(config.create_scenario);
263    }
264
265    #[test]
266    fn test_default_enabled_function() {
267        assert!(!default_enabled());
268    }
269
270    #[test]
271    fn test_default_true_function() {
272        assert!(default_true());
273    }
274
275    #[test]
276    fn test_default_false_function() {
277        assert!(!default_false());
278    }
279}