praxis_core/config/
runtime.rs1use std::collections::HashMap;
7
8use serde::Deserialize;
9
10#[derive(Debug, Clone, Deserialize)]
32pub struct RuntimeConfig {
33 #[serde(default)]
37 pub threads: usize,
38
39 #[serde(default = "default_work_stealing")]
41 pub work_stealing: bool,
42
43 #[serde(default)]
58 pub log_overrides: HashMap<String, String>,
59
60 #[serde(default = "default_global_queue_interval")]
72 pub global_queue_interval: Option<u32>,
73
74 #[serde(default)]
94 pub upstream_ca_file: Option<String>,
95
96 #[serde(default = "default_upstream_keepalive_pool_size")]
108 pub upstream_keepalive_pool_size: Option<usize>,
109}
110
111impl Default for RuntimeConfig {
112 fn default() -> Self {
113 Self {
114 threads: 0,
115 work_stealing: default_work_stealing(),
116 global_queue_interval: default_global_queue_interval(),
117 log_overrides: HashMap::new(),
118 upstream_ca_file: None,
119 upstream_keepalive_pool_size: default_upstream_keepalive_pool_size(),
120 }
121 }
122}
123
124fn default_work_stealing() -> bool {
126 true
127}
128
129#[allow(clippy::unnecessary_wraps, reason = "serde default")]
131fn default_upstream_keepalive_pool_size() -> Option<usize> {
132 Some(64)
133}
134
135#[allow(clippy::unnecessary_wraps, reason = "serde default")]
137fn default_global_queue_interval() -> Option<u32> {
138 Some(61)
139}
140
141#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn default_has_zero_threads_and_work_stealing_true() {
151 let cfg = RuntimeConfig::default();
152 assert_eq!(cfg.threads, 0, "default threads should be 0");
153 assert!(cfg.work_stealing, "default work_stealing should be true");
154 }
155
156 #[test]
157 fn deserialise_empty_yaml_gives_defaults() {
158 let cfg: RuntimeConfig = serde_yaml::from_str("{}").unwrap();
159 assert_eq!(cfg.threads, 0, "empty yaml should give 0 threads");
160 assert!(cfg.work_stealing, "empty yaml should give work_stealing=true");
161 }
162
163 #[test]
164 fn deserialise_explicit_threads() {
165 let cfg: RuntimeConfig = serde_yaml::from_str("threads: 4").unwrap();
166 assert_eq!(cfg.threads, 4, "explicit threads should be preserved");
167 assert!(cfg.work_stealing, "unset work_stealing should default to true");
168 }
169
170 #[test]
171 fn deserialise_work_stealing_disabled() {
172 let cfg: RuntimeConfig = serde_yaml::from_str("work_stealing: false").unwrap();
173 assert_eq!(cfg.threads, 0, "unset threads should default to 0");
174 assert!(!cfg.work_stealing, "explicit work_stealing=false should be preserved");
175 }
176
177 #[test]
178 fn deserialise_all_fields() {
179 let yaml = "threads: 8\nwork_stealing: true";
180 let cfg: RuntimeConfig = serde_yaml::from_str(yaml).unwrap();
181 assert_eq!(cfg.threads, 8, "threads should be 8");
182 assert!(cfg.work_stealing, "work_stealing should be true");
183 }
184
185 #[test]
186 fn deserialise_log_overrides() {
187 let yaml = r#"
188log_overrides:
189 praxis_filter::pipeline: trace
190 praxis_protocol: debug
191"#;
192 let cfg: RuntimeConfig = serde_yaml::from_str(yaml).unwrap();
193 assert_eq!(cfg.log_overrides.len(), 2, "should have 2 log overrides");
194 assert_eq!(
195 cfg.log_overrides["praxis_filter::pipeline"], "trace",
196 "pipeline override mismatch"
197 );
198 assert_eq!(
199 cfg.log_overrides["praxis_protocol"], "debug",
200 "protocol override mismatch"
201 );
202 }
203
204 #[test]
205 fn default_log_overrides_is_empty() {
206 let cfg: RuntimeConfig = serde_yaml::from_str("{}").unwrap();
207 assert!(cfg.log_overrides.is_empty(), "log_overrides should default to empty");
208 }
209
210 #[test]
211 fn global_queue_interval_defaults_to_61() {
212 let cfg = RuntimeConfig::default();
213 assert_eq!(cfg.global_queue_interval, Some(61), "default interval should be 61");
214 }
215
216 #[test]
217 fn deserialise_global_queue_interval() {
218 let cfg: RuntimeConfig = serde_yaml::from_str("global_queue_interval: 128").unwrap();
219 assert_eq!(cfg.global_queue_interval, Some(128), "explicit interval should be 128");
220 }
221
222 #[test]
223 fn deserialise_global_queue_interval_null() {
224 let cfg: RuntimeConfig = serde_yaml::from_str("global_queue_interval: null").unwrap();
225 assert!(cfg.global_queue_interval.is_none(), "null interval should be None");
226 }
227
228 #[test]
229 fn upstream_keepalive_pool_size_defaults_to_64() {
230 let cfg: RuntimeConfig = serde_yaml::from_str("{}").unwrap();
231 assert_eq!(
232 cfg.upstream_keepalive_pool_size,
233 Some(64),
234 "default pool size should be 64"
235 );
236 }
237
238 #[test]
239 fn deserialise_upstream_keepalive_pool_size() {
240 let cfg: RuntimeConfig = serde_yaml::from_str("upstream_keepalive_pool_size: 64").unwrap();
241 assert_eq!(
242 cfg.upstream_keepalive_pool_size,
243 Some(64),
244 "explicit pool size should be 64"
245 );
246 }
247
248 #[test]
249 fn upstream_ca_file_defaults_to_none() {
250 let cfg: RuntimeConfig = serde_yaml::from_str("{}").unwrap();
251 assert!(
252 cfg.upstream_ca_file.is_none(),
253 "upstream_ca_file should default to None"
254 );
255 }
256
257 #[test]
258 fn deserialise_upstream_ca_file() {
259 let cfg: RuntimeConfig = serde_yaml::from_str("upstream_ca_file: /etc/ssl/ca.pem").unwrap();
260 assert_eq!(
261 cfg.upstream_ca_file.as_deref(),
262 Some("/etc/ssl/ca.pem"),
263 "explicit upstream_ca_file should be preserved"
264 );
265 }
266}