chie_shared/config/
mod.rs

1//! Configuration types with builder patterns
2//!
3//! This module provides configuration structs for various CHIE protocol components
4//! with ergonomic builder patterns for construction.
5
6mod diff;
7mod feature_flags;
8mod merge;
9mod network;
10mod rate_limit;
11mod retry;
12mod storage;
13mod timeout;
14
15pub use diff::{ConfigChange, ConfigDiff};
16pub use feature_flags::{FeatureFlags, FeatureFlagsBuilder};
17pub use merge::ConfigMerge;
18pub use network::{NetworkConfig, NetworkConfigBuilder};
19pub use rate_limit::{RateLimitConfig, RateLimitConfigBuilder};
20pub use retry::{RetryConfig, RetryConfigBuilder};
21pub use storage::{StorageConfig, StorageConfigBuilder};
22pub use timeout::{TimeoutConfig, TimeoutConfigBuilder};
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn test_network_config_default() {
30        let config = NetworkConfig::default();
31        assert_eq!(config.max_connections, 100);
32        assert_eq!(config.connection_timeout_ms, 10_000);
33        assert!(config.enable_relay);
34        assert!(config.enable_dht);
35    }
36
37    #[test]
38    fn test_network_config_builder() {
39        let config = NetworkConfigBuilder::new()
40            .max_connections(50)
41            .connection_timeout_ms(5000)
42            .enable_relay(false)
43            .add_bootstrap_peer("/ip4/127.0.0.1/tcp/4001")
44            .build();
45
46        assert_eq!(config.max_connections, 50);
47        assert_eq!(config.connection_timeout_ms, 5000);
48        assert!(!config.enable_relay);
49        assert_eq!(config.bootstrap_peers.len(), 1);
50    }
51
52    #[test]
53    fn test_storage_config_default() {
54        let config = StorageConfig::default();
55        assert_eq!(config.data_dir, "./chie_data");
56        assert_eq!(config.max_cache_size_bytes, 10 * 1024 * 1024 * 1024);
57        assert!(config.enable_persistence);
58        assert!(config.enable_compression);
59    }
60
61    #[test]
62    fn test_storage_config_builder() {
63        let config = StorageConfigBuilder::new()
64            .data_dir("/var/lib/chie")
65            .max_cache_size_gb(20)
66            .enable_compression(false)
67            .sync_interval_secs(600)
68            .build();
69
70        assert_eq!(config.data_dir, "/var/lib/chie");
71        assert_eq!(config.max_cache_size_bytes, 20 * 1024 * 1024 * 1024);
72        assert!(!config.enable_compression);
73        assert_eq!(config.sync_interval_secs, 600);
74    }
75
76    #[test]
77    fn test_rate_limit_config_default() {
78        let config = RateLimitConfig::default();
79        assert_eq!(config.max_requests, 100);
80        assert_eq!(config.window_secs, 60);
81        assert_eq!(config.burst_size, 20);
82        assert!(config.enabled);
83    }
84
85    #[test]
86    fn test_rate_limit_config_builder() {
87        let config = RateLimitConfigBuilder::new()
88            .max_requests(200)
89            .window_secs(120)
90            .burst_size(50)
91            .enabled(false)
92            .build();
93
94        assert_eq!(config.max_requests, 200);
95        assert_eq!(config.window_secs, 120);
96        assert_eq!(config.burst_size, 50);
97        assert!(!config.enabled);
98    }
99
100    #[test]
101    fn test_retry_config_default() {
102        let config = RetryConfig::default();
103        assert_eq!(config.max_attempts, 3);
104        assert_eq!(config.initial_backoff_ms, 100);
105        assert_eq!(config.max_backoff_ms, 30_000);
106        assert_eq!(config.multiplier, 2.0);
107        assert!(config.enable_jitter);
108    }
109
110    #[test]
111    fn test_retry_config_builder() {
112        let config = RetryConfigBuilder::new()
113            .max_attempts(5)
114            .initial_backoff_ms(200)
115            .max_backoff_ms(60_000)
116            .multiplier(3.0)
117            .enable_jitter(false)
118            .build();
119
120        assert_eq!(config.max_attempts, 5);
121        assert_eq!(config.initial_backoff_ms, 200);
122        assert_eq!(config.max_backoff_ms, 60_000);
123        assert_eq!(config.multiplier, 3.0);
124        assert!(!config.enable_jitter);
125    }
126
127    #[test]
128    fn test_network_config_serde() {
129        let config = NetworkConfigBuilder::new().max_connections(75).build();
130
131        let json = serde_json::to_string(&config).unwrap();
132        let decoded: NetworkConfig = serde_json::from_str(&json).unwrap();
133
134        assert_eq!(decoded.max_connections, 75);
135    }
136
137    #[test]
138    fn test_storage_config_serde() {
139        let config = StorageConfigBuilder::new().data_dir("/custom/path").build();
140
141        let json = serde_json::to_string(&config).unwrap();
142        let decoded: StorageConfig = serde_json::from_str(&json).unwrap();
143
144        assert_eq!(decoded.data_dir, "/custom/path");
145    }
146
147    #[test]
148    fn test_builder_chaining() {
149        let config = NetworkConfigBuilder::new()
150            .max_connections(100)
151            .connection_timeout_ms(10_000)
152            .request_timeout_ms(30_000)
153            .enable_relay(true)
154            .enable_dht(true)
155            .add_bootstrap_peer("/ip4/1.2.3.4/tcp/4001")
156            .add_bootstrap_peer("/ip4/5.6.7.8/tcp/4001")
157            .add_listen_addr("/ip4/0.0.0.0/tcp/0")
158            .build();
159
160        assert_eq!(config.bootstrap_peers.len(), 2);
161        assert_eq!(config.listen_addrs.len(), 2); // 1 default + 1 added
162    }
163
164    #[test]
165    fn test_retry_config_is_exhausted() {
166        let config = RetryConfig::default();
167        assert!(!config.is_exhausted(0));
168        assert!(!config.is_exhausted(1));
169        assert!(!config.is_exhausted(2));
170        assert!(config.is_exhausted(3));
171        assert!(config.is_exhausted(4));
172    }
173
174    #[test]
175    fn test_retry_config_next_backoff() {
176        let config = RetryConfigBuilder::new()
177            .initial_backoff_ms(100)
178            .max_backoff_ms(10_000)
179            .multiplier(2.0)
180            .enable_jitter(false)
181            .build();
182
183        assert_eq!(config.next_backoff_ms(0), 100);
184        assert_eq!(config.next_backoff_ms(1), 200);
185        assert_eq!(config.next_backoff_ms(2), 400);
186        assert_eq!(config.next_backoff_ms(3), 800);
187        assert_eq!(config.next_backoff_ms(10), 10_000); // Capped at max
188    }
189
190    #[test]
191    fn test_retry_config_next_backoff_with_jitter() {
192        let config = RetryConfig::default();
193
194        // With jitter, values should vary
195        let delay1 = config.next_backoff_ms(0);
196        let delay2 = config.next_backoff_ms(0);
197
198        // Should be in expected range (100 ± 25%)
199        assert!((75..=125).contains(&delay1));
200        assert!((75..=125).contains(&delay2));
201    }
202
203    #[test]
204    fn test_timeout_config_default() {
205        let config = TimeoutConfig::default();
206        assert_eq!(config.default_timeout_ms, 30_000);
207        assert_eq!(config.connection_timeout_ms, 10_000);
208        assert_eq!(config.read_timeout_ms, 60_000);
209        assert_eq!(config.write_timeout_ms, 60_000);
210        assert_eq!(config.idle_timeout_ms, 300_000);
211        assert_eq!(config.keepalive_interval_ms, 60_000);
212        assert!(config.keepalive_enabled());
213        assert!(config.idle_timeout_enabled());
214    }
215
216    #[test]
217    fn test_timeout_config_fast() {
218        let config = TimeoutConfig::fast();
219        assert_eq!(config.default_timeout_ms, 5_000);
220        assert_eq!(config.connection_timeout_ms, 3_000);
221        assert!(config.keepalive_enabled());
222    }
223
224    #[test]
225    fn test_timeout_config_slow() {
226        let config = TimeoutConfig::slow();
227        assert_eq!(config.default_timeout_ms, 120_000);
228        assert_eq!(config.connection_timeout_ms, 30_000);
229        assert!(config.keepalive_enabled());
230    }
231
232    #[test]
233    fn test_timeout_config_builder() {
234        let config = TimeoutConfigBuilder::new()
235            .default_timeout_ms(15_000)
236            .connection_timeout_ms(5_000)
237            .read_timeout_ms(30_000)
238            .write_timeout_ms(30_000)
239            .idle_timeout_ms(0) // Disabled
240            .keepalive_interval_ms(0) // Disabled
241            .build();
242
243        assert_eq!(config.default_timeout_ms, 15_000);
244        assert_eq!(config.connection_timeout_ms, 5_000);
245        assert!(!config.idle_timeout_enabled());
246        assert!(!config.keepalive_enabled());
247    }
248
249    #[test]
250    fn test_timeout_config_serde() {
251        let config = TimeoutConfigBuilder::new()
252            .default_timeout_ms(20_000)
253            .build();
254
255        let json = serde_json::to_string(&config).unwrap();
256        let decoded: TimeoutConfig = serde_json::from_str(&json).unwrap();
257
258        assert_eq!(decoded.default_timeout_ms, 20_000);
259    }
260
261    // Validation tests
262    #[test]
263    fn test_network_config_validate_success() {
264        let config = NetworkConfig::default();
265        assert!(config.validate().is_ok());
266    }
267
268    #[test]
269    fn test_network_config_validate_zero_max_connections() {
270        let config = NetworkConfig {
271            max_connections: 0,
272            ..Default::default()
273        };
274        assert!(config.validate().is_err());
275    }
276
277    #[test]
278    fn test_network_config_validate_zero_connection_timeout() {
279        let config = NetworkConfig {
280            connection_timeout_ms: 0,
281            ..Default::default()
282        };
283        assert!(config.validate().is_err());
284    }
285
286    #[test]
287    fn test_network_config_validate_empty_listen_addrs() {
288        let mut config = NetworkConfig::default();
289        config.listen_addrs.clear();
290        assert!(config.validate().is_err());
291    }
292
293    #[test]
294    fn test_storage_config_validate_success() {
295        let config = StorageConfig::default();
296        assert!(config.validate().is_ok());
297    }
298
299    #[test]
300    fn test_storage_config_validate_empty_data_dir() {
301        let config = StorageConfig {
302            data_dir: String::new(),
303            ..Default::default()
304        };
305        assert!(config.validate().is_err());
306    }
307
308    #[test]
309    fn test_storage_config_validate_zero_cache_size() {
310        let config = StorageConfig {
311            max_cache_size_bytes: 0,
312            ..Default::default()
313        };
314        assert!(config.validate().is_err());
315    }
316
317    #[test]
318    fn test_rate_limit_config_validate_success() {
319        let config = RateLimitConfig::default();
320        assert!(config.validate().is_ok());
321    }
322
323    #[test]
324    fn test_rate_limit_config_validate_disabled() {
325        let config = RateLimitConfig {
326            enabled: false,
327            max_requests: 0, // Should be ok when disabled
328            ..Default::default()
329        };
330        assert!(config.validate().is_ok());
331    }
332
333    #[test]
334    fn test_rate_limit_config_validate_zero_max_requests() {
335        let config = RateLimitConfig {
336            max_requests: 0,
337            ..Default::default()
338        };
339        assert!(config.validate().is_err());
340    }
341
342    #[test]
343    fn test_rate_limit_config_validate_zero_window() {
344        let config = RateLimitConfig {
345            window_secs: 0,
346            ..Default::default()
347        };
348        assert!(config.validate().is_err());
349    }
350
351    #[test]
352    fn test_rate_limit_config_validate_burst_exceeds_max() {
353        let config = RateLimitConfig {
354            max_requests: 100,
355            burst_size: 101,
356            ..Default::default()
357        };
358        assert!(config.validate().is_err());
359    }
360
361    #[test]
362    fn test_retry_config_validate_success() {
363        let config = RetryConfig::default();
364        assert!(config.validate().is_ok());
365    }
366
367    #[test]
368    fn test_retry_config_validate_zero_max_attempts() {
369        let config = RetryConfig {
370            max_attempts: 0,
371            ..Default::default()
372        };
373        assert!(config.validate().is_err());
374    }
375
376    #[test]
377    fn test_retry_config_validate_zero_initial_backoff() {
378        let config = RetryConfig {
379            initial_backoff_ms: 0,
380            ..Default::default()
381        };
382        assert!(config.validate().is_err());
383    }
384
385    #[test]
386    fn test_retry_config_validate_max_less_than_initial() {
387        let config = RetryConfig {
388            initial_backoff_ms: 1000,
389            max_backoff_ms: 500,
390            ..Default::default()
391        };
392        assert!(config.validate().is_err());
393    }
394
395    #[test]
396    fn test_retry_config_validate_zero_multiplier() {
397        let config = RetryConfig {
398            multiplier: 0.0,
399            ..Default::default()
400        };
401        assert!(config.validate().is_err());
402    }
403
404    #[test]
405    fn test_retry_config_validate_negative_multiplier() {
406        let config = RetryConfig {
407            multiplier: -1.0,
408            ..Default::default()
409        };
410        assert!(config.validate().is_err());
411    }
412
413    #[test]
414    fn test_timeout_config_validate_success() {
415        let config = TimeoutConfig::default();
416        assert!(config.validate().is_ok());
417    }
418
419    #[test]
420    fn test_timeout_config_validate_zero_default_timeout() {
421        let config = TimeoutConfig {
422            default_timeout_ms: 0,
423            ..Default::default()
424        };
425        assert!(config.validate().is_err());
426    }
427
428    #[test]
429    fn test_timeout_config_validate_zero_connection_timeout() {
430        let config = TimeoutConfig {
431            connection_timeout_ms: 0,
432            ..Default::default()
433        };
434        assert!(config.validate().is_err());
435    }
436
437    #[test]
438    fn test_timeout_config_validate_zero_read_timeout() {
439        let config = TimeoutConfig {
440            read_timeout_ms: 0,
441            ..Default::default()
442        };
443        assert!(config.validate().is_err());
444    }
445
446    #[test]
447    fn test_timeout_config_validate_zero_write_timeout() {
448        let config = TimeoutConfig {
449            write_timeout_ms: 0,
450            ..Default::default()
451        };
452        assert!(config.validate().is_err());
453    }
454
455    #[test]
456    fn test_timeout_config_validate_zero_idle_ok() {
457        let config = TimeoutConfig {
458            idle_timeout_ms: 0, // Should be ok (disabled)
459            ..Default::default()
460        };
461        assert!(config.validate().is_ok());
462    }
463
464    #[test]
465    fn test_timeout_config_validate_zero_keepalive_ok() {
466        let config = TimeoutConfig {
467            keepalive_interval_ms: 0, // Should be ok (disabled)
468            ..Default::default()
469        };
470        assert!(config.validate().is_ok());
471    }
472
473    // Feature flags tests
474    #[test]
475    fn test_feature_flags_default() {
476        let flags = FeatureFlags::default();
477        assert!(!flags.experimental);
478        assert!(!flags.beta);
479        assert!(!flags.enhanced_telemetry);
480        assert!(!flags.performance_profiling);
481        assert!(!flags.debug_mode);
482        assert!(flags.compression_optimization);
483        assert!(flags.adaptive_retry);
484    }
485
486    #[test]
487    fn test_feature_flags_none() {
488        let flags = FeatureFlags::none();
489        assert!(!flags.experimental);
490        assert!(!flags.beta);
491        assert!(!flags.compression_optimization);
492        assert!(!flags.adaptive_retry);
493    }
494
495    #[test]
496    fn test_feature_flags_all() {
497        let flags = FeatureFlags::all();
498        assert!(flags.experimental);
499        assert!(flags.beta);
500        assert!(flags.enhanced_telemetry);
501        assert!(flags.performance_profiling);
502        assert!(flags.debug_mode);
503        assert!(flags.compression_optimization);
504        assert!(flags.adaptive_retry);
505    }
506
507    #[test]
508    fn test_feature_flags_has_unstable_features() {
509        let mut flags = FeatureFlags::default();
510        assert!(!flags.has_unstable_features());
511
512        flags.experimental = true;
513        assert!(flags.has_unstable_features());
514
515        flags.experimental = false;
516        flags.beta = true;
517        assert!(flags.has_unstable_features());
518    }
519
520    #[test]
521    fn test_feature_flags_has_diagnostic_features() {
522        let mut flags = FeatureFlags::default();
523        assert!(!flags.has_diagnostic_features());
524
525        flags.debug_mode = true;
526        assert!(flags.has_diagnostic_features());
527
528        flags.debug_mode = false;
529        flags.performance_profiling = true;
530        assert!(flags.has_diagnostic_features());
531
532        flags.performance_profiling = false;
533        flags.enhanced_telemetry = true;
534        assert!(flags.has_diagnostic_features());
535    }
536
537    #[test]
538    fn test_feature_flags_builder() {
539        let flags = FeatureFlagsBuilder::new()
540            .experimental(true)
541            .beta(true)
542            .debug_mode(true)
543            .build();
544
545        assert!(flags.experimental);
546        assert!(flags.beta);
547        assert!(flags.debug_mode);
548        assert!(!flags.performance_profiling);
549    }
550
551    #[test]
552    fn test_feature_flags_serde() {
553        let flags = FeatureFlagsBuilder::new()
554            .experimental(true)
555            .compression_optimization(false)
556            .build();
557
558        let json = serde_json::to_string(&flags).unwrap();
559        let decoded: FeatureFlags = serde_json::from_str(&json).unwrap();
560
561        assert!(decoded.experimental);
562        assert!(!decoded.compression_optimization);
563    }
564
565    // ConfigChange tests
566    #[test]
567    fn test_config_change_new() {
568        let change = ConfigChange::new("max_connections", &100, &200);
569        assert_eq!(change.field, "max_connections");
570        assert_eq!(change.old_value, "100");
571        assert_eq!(change.new_value, "200");
572    }
573
574    #[test]
575    fn test_config_change_serde() {
576        let change = ConfigChange::new("enable_relay", &true, &false);
577        let json = serde_json::to_string(&change).unwrap();
578        let decoded: ConfigChange = serde_json::from_str(&json).unwrap();
579
580        assert_eq!(decoded.field, "enable_relay");
581        assert_eq!(decoded.old_value, "true");
582        assert_eq!(decoded.new_value, "false");
583    }
584
585    // ConfigDiff tests for NetworkConfig
586    #[test]
587    fn test_config_diff_network_no_changes() {
588        let config1 = NetworkConfig::default();
589        let config2 = NetworkConfig::default();
590
591        let changes = ConfigDiff::network_config(&config1, &config2);
592        assert!(changes.is_empty());
593    }
594
595    #[test]
596    fn test_config_diff_network_single_change() {
597        let config1 = NetworkConfig::default();
598        let config2 = NetworkConfig {
599            max_connections: 200,
600            ..Default::default()
601        };
602
603        let changes = ConfigDiff::network_config(&config1, &config2);
604        assert_eq!(changes.len(), 1);
605        assert_eq!(changes[0].field, "max_connections");
606        assert_eq!(changes[0].old_value, "100");
607        assert_eq!(changes[0].new_value, "200");
608    }
609
610    #[test]
611    fn test_config_diff_network_multiple_changes() {
612        let config1 = NetworkConfig::default();
613        let config2 = NetworkConfig {
614            max_connections: 200,
615            enable_relay: false,
616            connection_timeout_ms: 20_000,
617            ..Default::default()
618        };
619
620        let changes = ConfigDiff::network_config(&config1, &config2);
621        assert_eq!(changes.len(), 3);
622
623        let field_names: Vec<&str> = changes.iter().map(|c| c.field.as_str()).collect();
624        assert!(field_names.contains(&"max_connections"));
625        assert!(field_names.contains(&"enable_relay"));
626        assert!(field_names.contains(&"connection_timeout_ms"));
627    }
628
629    #[test]
630    fn test_config_diff_network_vec_changes() {
631        let config1 = NetworkConfig {
632            bootstrap_peers: vec!["peer1".to_string()],
633            ..Default::default()
634        };
635
636        let config2 = NetworkConfig {
637            bootstrap_peers: vec!["peer1".to_string(), "peer2".to_string()],
638            ..Default::default()
639        };
640
641        let changes = ConfigDiff::network_config(&config1, &config2);
642        assert_eq!(changes.len(), 1);
643        assert_eq!(changes[0].field, "bootstrap_peers");
644    }
645
646    // ConfigDiff tests for RetryConfig
647    #[test]
648    fn test_config_diff_retry_no_changes() {
649        let config1 = RetryConfig::default();
650        let config2 = RetryConfig::default();
651
652        let changes = ConfigDiff::retry_config(&config1, &config2);
653        assert!(changes.is_empty());
654    }
655
656    #[test]
657    fn test_config_diff_retry_single_change() {
658        let config1 = RetryConfig::default();
659        let config2 = RetryConfig {
660            max_attempts: 5,
661            ..Default::default()
662        };
663
664        let changes = ConfigDiff::retry_config(&config1, &config2);
665        assert_eq!(changes.len(), 1);
666        assert_eq!(changes[0].field, "max_attempts");
667        assert_eq!(changes[0].old_value, "3");
668        assert_eq!(changes[0].new_value, "5");
669    }
670
671    #[test]
672    fn test_config_diff_retry_multiple_changes() {
673        let config1 = RetryConfig::default();
674        let config2 = RetryConfig {
675            max_attempts: 5,
676            multiplier: 3.0,
677            enable_jitter: false,
678            ..Default::default()
679        };
680
681        let changes = ConfigDiff::retry_config(&config1, &config2);
682        assert_eq!(changes.len(), 3);
683
684        let field_names: Vec<&str> = changes.iter().map(|c| c.field.as_str()).collect();
685        assert!(field_names.contains(&"max_attempts"));
686        assert!(field_names.contains(&"multiplier"));
687        assert!(field_names.contains(&"enable_jitter"));
688    }
689
690    // ConfigDiff tests for FeatureFlags
691    #[test]
692    fn test_config_diff_feature_flags_no_changes() {
693        let flags1 = FeatureFlags::default();
694        let flags2 = FeatureFlags::default();
695
696        let changes = ConfigDiff::feature_flags(&flags1, &flags2);
697        assert!(changes.is_empty());
698    }
699
700    #[test]
701    fn test_config_diff_feature_flags_single_change() {
702        let flags1 = FeatureFlags::default();
703        let flags2 = FeatureFlags {
704            experimental: true,
705            ..Default::default()
706        };
707
708        let changes = ConfigDiff::feature_flags(&flags1, &flags2);
709        assert_eq!(changes.len(), 1);
710        assert_eq!(changes[0].field, "experimental");
711        assert_eq!(changes[0].old_value, "false");
712        assert_eq!(changes[0].new_value, "true");
713    }
714
715    #[test]
716    fn test_config_diff_feature_flags_multiple_changes() {
717        let flags1 = FeatureFlags::default();
718        let flags2 = FeatureFlags {
719            experimental: true,
720            beta: true,
721            debug_mode: true,
722            ..Default::default()
723        };
724
725        let changes = ConfigDiff::feature_flags(&flags1, &flags2);
726        assert_eq!(changes.len(), 3);
727
728        let field_names: Vec<&str> = changes.iter().map(|c| c.field.as_str()).collect();
729        assert!(field_names.contains(&"experimental"));
730        assert!(field_names.contains(&"beta"));
731        assert!(field_names.contains(&"debug_mode"));
732    }
733
734    // ConfigMerge tests for RetryConfig
735    #[test]
736    fn test_config_merge_retry_complete_override() {
737        let base = RetryConfig::default();
738        let override_config = RetryConfig {
739            max_attempts: 10,
740            initial_backoff_ms: 500,
741            max_backoff_ms: 60_000,
742            multiplier: 3.0,
743            enable_jitter: false,
744        };
745
746        let merged = ConfigMerge::retry_config(&base, &override_config);
747
748        assert_eq!(merged.max_attempts, 10);
749        assert_eq!(merged.initial_backoff_ms, 500);
750        assert_eq!(merged.max_backoff_ms, 60_000);
751        assert!((merged.multiplier - 3.0).abs() < f64::EPSILON);
752        assert!(!merged.enable_jitter);
753    }
754
755    #[test]
756    fn test_config_merge_retry_partial_override() {
757        let base = RetryConfig {
758            max_attempts: 5,
759            initial_backoff_ms: 200,
760            max_backoff_ms: 40_000,
761            multiplier: 2.5,
762            enable_jitter: true,
763        };
764
765        let override_config = RetryConfig {
766            max_attempts: 10,
767            ..RetryConfig::default()
768        };
769
770        let merged = ConfigMerge::retry_config(&base, &override_config);
771
772        // Override takes precedence for all fields
773        assert_eq!(merged.max_attempts, 10);
774        assert_eq!(
775            merged.initial_backoff_ms,
776            RetryConfig::default().initial_backoff_ms
777        );
778    }
779
780    // ConfigMerge tests for FeatureFlags
781    #[test]
782    fn test_config_merge_feature_flags_override_all() {
783        let base = FeatureFlags::all();
784        let override_flags = FeatureFlags::none();
785
786        let merged = ConfigMerge::feature_flags(&base, &override_flags, true);
787
788        // Complete override - all should be false
789        assert!(!merged.experimental);
790        assert!(!merged.beta);
791        assert!(!merged.enhanced_telemetry);
792        assert!(!merged.compression_optimization);
793    }
794
795    #[test]
796    fn test_config_merge_feature_flags_or_merge() {
797        let base = FeatureFlags {
798            experimental: true,
799            beta: false,
800            enhanced_telemetry: false,
801            performance_profiling: false,
802            debug_mode: false,
803            compression_optimization: true,
804            adaptive_retry: false,
805        };
806
807        let override_flags = FeatureFlags {
808            experimental: false,
809            beta: true,
810            enhanced_telemetry: false,
811            performance_profiling: true,
812            debug_mode: false,
813            compression_optimization: false,
814            adaptive_retry: true,
815        };
816
817        let merged = ConfigMerge::feature_flags(&base, &override_flags, false);
818
819        // OR merge - feature enabled if enabled in either
820        assert!(merged.experimental); // base=true, override=false -> true
821        assert!(merged.beta); // base=false, override=true -> true
822        assert!(!merged.enhanced_telemetry); // base=false, override=false -> false
823        assert!(merged.performance_profiling); // base=false, override=true -> true
824        assert!(!merged.debug_mode); // base=false, override=false -> false
825        assert!(merged.compression_optimization); // base=true, override=false -> true
826        assert!(merged.adaptive_retry); // base=false, override=true -> true
827    }
828
829    #[test]
830    fn test_config_merge_feature_flags_both_enabled() {
831        let base = FeatureFlags::all();
832        let override_flags = FeatureFlags {
833            experimental: true,
834            beta: true,
835            ..FeatureFlags::none()
836        };
837
838        let merged = ConfigMerge::feature_flags(&base, &override_flags, false);
839
840        // OR merge - all should be enabled since base has all
841        assert!(merged.experimental);
842        assert!(merged.beta);
843        assert!(merged.enhanced_telemetry);
844        assert!(merged.performance_profiling);
845        assert!(merged.debug_mode);
846        assert!(merged.compression_optimization);
847        assert!(merged.adaptive_retry);
848    }
849}