1#![allow(unused_variables)] use super::{validators, ConfigSerializable, StandardConfig};
9use crate::errors::Result;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::path::PathBuf;
13
14#[derive(Debug, Clone, Serialize, Deserialize, Default)]
16pub struct UnifiedConfig {
17 pub metadata: ConfigMetadata,
19 pub resources: ResourceConfig,
21 pub logging: LoggingConfig,
23 pub performance: PerformanceConfig,
25 pub security: SecurityConfig,
27 pub environment: EnvironmentConfig,
29}
30
31impl StandardConfig for UnifiedConfig {
32 fn validate(&self) -> Result<()> {
33 self.metadata.validate()?;
34 self.resources.validate()?;
35 self.logging.validate()?;
36 self.performance.validate()?;
37 self.security.validate()?;
38 self.environment.validate()?;
39 Ok(())
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ConfigMetadata {
46 pub name: String,
48 pub description: Option<String>,
50 pub version: String,
52 pub tags: Vec<String>,
54 pub enabled: bool,
56 #[serde(default = "chrono::Utc::now")]
58 pub created_at: chrono::DateTime<chrono::Utc>,
59 #[serde(default = "chrono::Utc::now")]
61 pub modified_at: chrono::DateTime<chrono::Utc>,
62 pub author: Option<String>,
64 pub source: ConfigSource,
66 pub priority: u32,
68}
69
70impl Default for ConfigMetadata {
71 fn default() -> Self {
72 let now = chrono::Utc::now();
73 Self {
74 name: "default".to_string(),
75 description: None,
76 version: "1.0.0".to_string(),
77 tags: Vec::new(),
78 enabled: true,
79 created_at: now,
80 modified_at: now,
81 author: None,
82 source: ConfigSource::Default,
83 priority: 100,
84 }
85 }
86}
87
88impl StandardConfig for ConfigMetadata {
89 fn validate(&self) -> Result<()> {
90 validators::non_empty_string(&self.name, "name")?;
91 validators::non_empty_string(&self.version, "version")?;
92 Ok(())
93 }
94}
95
96#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
98pub enum ConfigSource {
99 Default,
100 File,
101 Environment,
102 Database,
103 Remote,
104 CommandLine,
105 Code,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, Default)]
110pub struct ResourceConfig {
111 pub memory: MemoryLimits,
113 pub cpu: CpuLimits,
115 pub gpu: GpuLimits,
117 pub storage: StorageLimits,
119 pub network: NetworkLimits,
121 pub timeouts: TimeoutConfig,
123}
124
125impl StandardConfig for ResourceConfig {
126 fn validate(&self) -> Result<()> {
127 self.memory.validate()?;
128 self.cpu.validate()?;
129 self.gpu.validate()?;
130 self.storage.validate()?;
131 self.network.validate()?;
132 self.timeouts.validate()?;
133 Ok(())
134 }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct MemoryLimits {
140 pub max_heap_bytes: Option<u64>,
142 pub max_gpu_bytes: Option<u64>,
144 pub max_shared_bytes: Option<u64>,
146 pub warning_threshold_percent: f64,
148 pub monitoring_enabled: bool,
150 pub pressure_detection: bool,
152}
153
154impl Default for MemoryLimits {
155 fn default() -> Self {
156 Self {
157 max_heap_bytes: None,
158 max_gpu_bytes: None,
159 max_shared_bytes: None,
160 warning_threshold_percent: 80.0,
161 monitoring_enabled: true,
162 pressure_detection: true,
163 }
164 }
165}
166
167impl StandardConfig for MemoryLimits {
168 fn validate(&self) -> Result<()> {
169 validators::numeric_range(
170 self.warning_threshold_percent,
171 0.0,
172 100.0,
173 "warning_threshold_percent",
174 )?;
175 Ok(())
176 }
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct CpuLimits {
182 pub max_cores: Option<usize>,
184 pub max_usage_percent: Option<f64>,
186 pub thread_pool_size: Option<usize>,
188 pub affinity: Vec<usize>,
190 pub numa_nodes: Vec<usize>,
192}
193
194impl Default for CpuLimits {
195 fn default() -> Self {
196 Self {
197 max_cores: None,
198 max_usage_percent: Some(80.0),
199 thread_pool_size: None,
200 affinity: Vec::new(),
201 numa_nodes: Vec::new(),
202 }
203 }
204}
205
206impl StandardConfig for CpuLimits {
207 fn validate(&self) -> Result<()> {
208 if let Some(usage) = self.max_usage_percent {
209 validators::numeric_range(usage, 0.0, 100.0, "max_usage_percent")?;
210 }
211 if let Some(cores) = self.max_cores {
212 validators::positive(cores, "max_cores")?;
213 }
214 Ok(())
215 }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct GpuLimits {
221 pub max_devices: Option<usize>,
223 pub device_ids: Vec<usize>,
225 pub memory_per_device_bytes: Option<u64>,
227 pub max_usage_percent: Option<f64>,
229 pub monitoring_enabled: bool,
231}
232
233impl Default for GpuLimits {
234 fn default() -> Self {
235 Self {
236 max_devices: None,
237 device_ids: Vec::new(),
238 memory_per_device_bytes: None,
239 max_usage_percent: Some(90.0),
240 monitoring_enabled: true,
241 }
242 }
243}
244
245impl StandardConfig for GpuLimits {
246 fn validate(&self) -> Result<()> {
247 if let Some(usage) = self.max_usage_percent {
248 validators::numeric_range(usage, 0.0, 100.0, "max_usage_percent")?;
249 }
250 Ok(())
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct StorageLimits {
257 pub max_disk_bytes: Option<u64>,
259 pub temp_dir: Option<PathBuf>,
261 pub cache_dir: Option<PathBuf>,
263 pub max_cache_bytes: Option<u64>,
265 pub warning_threshold_percent: f64,
267}
268
269impl Default for StorageLimits {
270 fn default() -> Self {
271 Self {
272 max_disk_bytes: None,
273 temp_dir: Some(std::env::temp_dir()),
274 cache_dir: None,
275 max_cache_bytes: Some(1024 * 1024 * 1024), warning_threshold_percent: 85.0,
277 }
278 }
279}
280
281impl StandardConfig for StorageLimits {
282 fn validate(&self) -> Result<()> {
283 validators::numeric_range(
284 self.warning_threshold_percent,
285 0.0,
286 100.0,
287 "warning_threshold_percent",
288 )?;
289
290 if let Some(temp_dir) = &self.temp_dir {
291 if !temp_dir.exists() {
292 return Err(anyhow::anyhow!(
293 "Temp directory does not exist: {}",
294 temp_dir.display()
295 )
296 .into());
297 }
298 }
299
300 Ok(())
301 }
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct NetworkLimits {
307 pub max_bandwidth_bps: Option<u64>,
309 pub connection_timeout_ms: u64,
311 pub request_timeout_ms: u64,
313 pub max_connections: Option<usize>,
315 pub max_retries: u32,
317}
318
319impl Default for NetworkLimits {
320 fn default() -> Self {
321 Self {
322 max_bandwidth_bps: None,
323 connection_timeout_ms: 30_000, request_timeout_ms: 300_000, max_connections: Some(100),
326 max_retries: 3,
327 }
328 }
329}
330
331impl StandardConfig for NetworkLimits {
332 fn validate(&self) -> Result<()> {
333 validators::positive(self.connection_timeout_ms, "connection_timeout_ms")?;
334 validators::positive(self.request_timeout_ms, "request_timeout_ms")?;
335 Ok(())
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct TimeoutConfig {
342 pub default_timeout_ms: u64,
344 pub training_timeout_ms: Option<u64>,
346 pub inference_timeout_ms: Option<u64>,
348 pub export_timeout_ms: Option<u64>,
350 pub loading_timeout_ms: Option<u64>,
352}
353
354impl Default for TimeoutConfig {
355 fn default() -> Self {
356 Self {
357 default_timeout_ms: 300_000, training_timeout_ms: None,
359 inference_timeout_ms: Some(30_000), export_timeout_ms: Some(1_800_000), loading_timeout_ms: Some(600_000), }
363 }
364}
365
366impl StandardConfig for TimeoutConfig {
367 fn validate(&self) -> Result<()> {
368 validators::positive(self.default_timeout_ms, "default_timeout_ms")?;
369 Ok(())
370 }
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct LoggingConfig {
376 pub level: LogLevel,
378 pub format: LogFormat,
380 pub outputs: Vec<LogOutput>,
382 pub include_timestamps: bool,
384 pub include_source: bool,
386 pub include_thread: bool,
388 pub max_file_size_bytes: Option<u64>,
390 pub max_files: Option<usize>,
392 pub rotation: LogRotation,
394 pub structured_fields: HashMap<String, String>,
396}
397
398impl Default for LoggingConfig {
399 fn default() -> Self {
400 Self {
401 level: LogLevel::Info,
402 format: LogFormat::Text,
403 outputs: vec![LogOutput::Stdout],
404 include_timestamps: true,
405 include_source: false,
406 include_thread: false,
407 max_file_size_bytes: Some(10 * 1024 * 1024), max_files: Some(5),
409 rotation: LogRotation::Size,
410 structured_fields: HashMap::new(),
411 }
412 }
413}
414
415impl StandardConfig for LoggingConfig {}
416
417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
419pub enum LogLevel {
420 Trace,
421 Debug,
422 Info,
423 Warn,
424 Error,
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
429pub enum LogFormat {
430 Text,
431 Json,
432 Pretty,
433 Compact,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
438pub enum LogOutput {
439 Stdout,
440 Stderr,
441 File(PathBuf),
442 Syslog,
443 Network(String),
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
448pub enum LogRotation {
449 None,
450 Size,
451 Time,
452 Daily,
453 Weekly,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct PerformanceConfig {
459 pub monitoring_enabled: bool,
461 pub profiling_level: ProfilingLevel,
463 pub benchmarking: BenchmarkConfig,
465 pub optimization: OptimizationConfig,
467 pub caching: CacheConfig,
469}
470
471impl Default for PerformanceConfig {
472 fn default() -> Self {
473 Self {
474 monitoring_enabled: true,
475 profiling_level: ProfilingLevel::Basic,
476 benchmarking: BenchmarkConfig::default(),
477 optimization: OptimizationConfig::default(),
478 caching: CacheConfig::default(),
479 }
480 }
481}
482
483impl StandardConfig for PerformanceConfig {}
484
485#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
487pub enum ProfilingLevel {
488 None,
489 Basic,
490 Detailed,
491 Comprehensive,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct BenchmarkConfig {
497 pub auto_benchmark: bool,
499 pub frequency: BenchmarkFrequency,
501 pub warmup_iterations: usize,
503 pub measurement_iterations: usize,
505 pub confidence_level: f64,
507}
508
509impl Default for BenchmarkConfig {
510 fn default() -> Self {
511 Self {
512 auto_benchmark: false,
513 frequency: BenchmarkFrequency::Never,
514 warmup_iterations: 3,
515 measurement_iterations: 10,
516 confidence_level: 0.95,
517 }
518 }
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
523pub enum BenchmarkFrequency {
524 Never,
525 OnStartup,
526 Daily,
527 Weekly,
528 OnConfigChange,
529 OnDemand,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct OptimizationConfig {
535 pub level: OptimizationLevel,
537 pub optimizations: HashMap<String, bool>,
539 pub target_hardware: Option<String>,
541 pub precision: PrecisionConfig,
543}
544
545impl Default for OptimizationConfig {
546 fn default() -> Self {
547 let mut optimizations = HashMap::new();
548 optimizations.insert("simd".to_string(), true);
549 optimizations.insert("vectorization".to_string(), true);
550 optimizations.insert("loop_unrolling".to_string(), true);
551 optimizations.insert("kernel_fusion".to_string(), true);
552
553 Self {
554 level: OptimizationLevel::Balanced,
555 optimizations,
556 target_hardware: None,
557 precision: PrecisionConfig::default(),
558 }
559 }
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
564pub enum OptimizationLevel {
565 None,
566 Basic,
567 Balanced,
568 Aggressive,
569 Maximum,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct PrecisionConfig {
575 pub default_precision: PrecisionType,
577 pub mixed_precision: bool,
579 pub auto_precision: bool,
581 pub loss_threshold: f64,
583}
584
585impl Default for PrecisionConfig {
586 fn default() -> Self {
587 Self {
588 default_precision: PrecisionType::FP32,
589 mixed_precision: false,
590 auto_precision: false,
591 loss_threshold: 1e-6,
592 }
593 }
594}
595
596#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
598pub enum PrecisionType {
599 FP16,
600 BF16,
601 FP32,
602 FP64,
603 INT8,
604 INT16,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
609pub struct CacheConfig {
610 pub enabled: bool,
612 pub size_bytes: Option<u64>,
614 pub eviction_policy: CacheEvictionPolicy,
616 pub ttl_seconds: Option<u64>,
618 pub cache_dir: Option<PathBuf>,
620}
621
622impl Default for CacheConfig {
623 fn default() -> Self {
624 Self {
625 enabled: true,
626 size_bytes: Some(1024 * 1024 * 1024), eviction_policy: CacheEvictionPolicy::LRU,
628 ttl_seconds: Some(3600), cache_dir: None,
630 }
631 }
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
636pub enum CacheEvictionPolicy {
637 LRU,
638 LFU,
639 FIFO,
640 Random,
641 TTL,
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize)]
646pub struct SecurityConfig {
647 pub enabled: bool,
649 pub level: SecurityLevel,
651 pub encryption: EncryptionConfig,
653 pub authentication: AuthenticationConfig,
655 pub access_control: AccessControlConfig,
657}
658
659impl Default for SecurityConfig {
660 fn default() -> Self {
661 Self {
662 enabled: true,
663 level: SecurityLevel::Standard,
664 encryption: EncryptionConfig::default(),
665 authentication: AuthenticationConfig::default(),
666 access_control: AccessControlConfig::default(),
667 }
668 }
669}
670
671impl StandardConfig for SecurityConfig {}
672
673#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
675pub enum SecurityLevel {
676 Minimal,
677 Standard,
678 High,
679 Maximum,
680}
681
682#[derive(Debug, Clone, Serialize, Deserialize)]
684pub struct EncryptionConfig {
685 pub at_rest: bool,
687 pub in_transit: bool,
689 pub algorithm: Option<String>,
691 pub key_management: KeyManagementConfig,
693}
694
695impl Default for EncryptionConfig {
696 fn default() -> Self {
697 Self {
698 at_rest: false,
699 in_transit: true,
700 algorithm: Some("AES-256-GCM".to_string()),
701 key_management: KeyManagementConfig::default(),
702 }
703 }
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
708pub struct KeyManagementConfig {
709 pub rotation_enabled: bool,
711 pub rotation_interval_days: Option<u32>,
713 pub storage_location: KeyStorageLocation,
715}
716
717impl Default for KeyManagementConfig {
718 fn default() -> Self {
719 Self {
720 rotation_enabled: false,
721 rotation_interval_days: Some(90),
722 storage_location: KeyStorageLocation::Local,
723 }
724 }
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
729pub enum KeyStorageLocation {
730 Local,
731 Environment,
732 Vault,
733 HSM,
734 Cloud,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize)]
739pub struct AuthenticationConfig {
740 pub method: AuthenticationMethod,
742 pub session_timeout_seconds: Option<u64>,
744 pub mfa_enabled: bool,
746}
747
748impl Default for AuthenticationConfig {
749 fn default() -> Self {
750 Self {
751 method: AuthenticationMethod::None,
752 session_timeout_seconds: Some(3600), mfa_enabled: false,
754 }
755 }
756}
757
758#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
760pub enum AuthenticationMethod {
761 None,
762 ApiKey,
763 JWT,
764 OAuth2,
765 LDAP,
766 SAML,
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize)]
771pub struct AccessControlConfig {
772 pub model: AccessControlModel,
774 pub default_permissions: Vec<String>,
776 pub inheritance_enabled: bool,
778}
779
780impl Default for AccessControlConfig {
781 fn default() -> Self {
782 Self {
783 model: AccessControlModel::None,
784 default_permissions: vec!["read".to_string()],
785 inheritance_enabled: true,
786 }
787 }
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
792pub enum AccessControlModel {
793 None,
794 RBAC, ABAC, DAC, MAC, }
799
800#[derive(Debug, Clone, Serialize, Deserialize)]
802pub struct EnvironmentConfig {
803 pub environment_type: EnvironmentType,
805 pub variables: HashMap<String, String>,
807 pub feature_flags: HashMap<String, bool>,
809 pub debug: DebugConfig,
811}
812
813impl Default for EnvironmentConfig {
814 fn default() -> Self {
815 Self {
816 environment_type: EnvironmentType::Development,
817 variables: HashMap::new(),
818 feature_flags: HashMap::new(),
819 debug: DebugConfig::default(),
820 }
821 }
822}
823
824impl StandardConfig for EnvironmentConfig {}
825
826#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
828pub enum EnvironmentType {
829 Development,
830 Testing,
831 Staging,
832 Production,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct DebugConfig {
838 pub enabled: bool,
840 pub level: DebugLevel,
842 pub verbose: bool,
844 pub stack_traces: bool,
846 pub output_format: DebugOutputFormat,
848}
849
850impl Default for DebugConfig {
851 fn default() -> Self {
852 Self {
853 enabled: cfg!(debug_assertions),
854 level: DebugLevel::Info,
855 verbose: false,
856 stack_traces: cfg!(debug_assertions),
857 output_format: DebugOutputFormat::Pretty,
858 }
859 }
860}
861
862#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
864pub enum DebugLevel {
865 None,
866 Basic,
867 Info,
868 Verbose,
869 Trace,
870}
871
872#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
874pub enum DebugOutputFormat {
875 Plain,
876 Pretty,
877 Json,
878 Structured,
879}
880
881pub struct ConfigManager {
883 configs: HashMap<String, UnifiedConfig>,
884 active_config: Option<String>,
885}
886
887impl ConfigManager {
888 pub fn new() -> Self {
890 Self {
891 configs: HashMap::new(),
892 active_config: None,
893 }
894 }
895
896 pub fn add_config(&mut self, name: String, config: UnifiedConfig) -> Result<()> {
898 config.validate()?;
899 self.configs.insert(name, config);
900 Ok(())
901 }
902
903 pub fn get_config(&self, name: &str) -> Option<&UnifiedConfig> {
905 self.configs.get(name)
906 }
907
908 pub fn set_active(&mut self, name: String) -> Result<()> {
910 if !self.configs.contains_key(&name) {
911 return Err(anyhow::anyhow!("Configuration '{}' not found", name).into());
912 }
913 self.active_config = Some(name);
914 Ok(())
915 }
916
917 pub fn get_active(&self) -> Option<&UnifiedConfig> {
919 self.active_config.as_ref().and_then(|name| self.configs.get(name))
920 }
921
922 pub fn merge_configs(&self, names: &[String]) -> Result<UnifiedConfig> {
924 if names.is_empty() {
925 return Ok(UnifiedConfig::default());
926 }
927
928 let mut result = self
929 .get_config(&names[0])
930 .ok_or_else(|| anyhow::anyhow!("Configuration '{}' not found", names[0]))?
931 .clone();
932
933 for name in &names[1..] {
934 let config = self
935 .get_config(name)
936 .ok_or_else(|| anyhow::anyhow!("Configuration '{}' not found", name))?;
937 result = merge_unified_configs(result, config.clone())?;
938 }
939
940 Ok(result)
941 }
942
943 pub fn load_from_file(&mut self, name: String, path: &std::path::Path) -> Result<()> {
945 let config = UnifiedConfig::load_from_file(path)?;
946 self.add_config(name, config)
947 }
948
949 pub fn save_to_file(&self, name: &str, path: &std::path::Path) -> Result<()> {
951 let config = self
952 .get_config(name)
953 .ok_or_else(|| anyhow::anyhow!("Configuration '{}' not found", name))?;
954 config.save_to_file(path)
955 }
956}
957
958impl Default for ConfigManager {
959 fn default() -> Self {
960 Self::new()
961 }
962}
963
964pub fn merge_unified_configs(
966 base: UnifiedConfig,
967 override_config: UnifiedConfig,
968) -> Result<UnifiedConfig> {
969 Ok(override_config)
972}
973
974#[cfg(test)]
975mod tests {
976 use super::*;
977 use tempfile::tempdir;
978
979 #[test]
980 fn test_unified_config_creation() {
981 let config = UnifiedConfig::default();
982 assert!(config.validate().is_ok());
983 assert_eq!(config.metadata.name, "default");
984 assert!(config.metadata.enabled);
985 }
986
987 #[test]
988 fn test_config_serialization() {
989 let config = UnifiedConfig::default();
990 let json = config.to_json().expect("operation failed in test");
991 let deserialized = UnifiedConfig::from_json(&json).expect("operation failed in test");
992
993 assert_eq!(config.metadata.name, deserialized.metadata.name);
994 assert_eq!(config.metadata.enabled, deserialized.metadata.enabled);
995 }
996
997 #[test]
998 fn test_config_manager() {
999 let mut manager = ConfigManager::new();
1000 let config = UnifiedConfig::default();
1001
1002 manager.add_config("test".to_string(), config).expect("add operation failed");
1003 manager.set_active("test".to_string()).expect("operation failed in test");
1004
1005 let active = manager.get_active().expect("operation failed in test");
1006 assert_eq!(active.metadata.name, "default");
1007 }
1008
1009 #[test]
1010 fn test_config_file_operations() {
1011 let temp_dir = tempdir().expect("temp file creation failed");
1012 let config_path = temp_dir.path().join("test_config.json");
1013
1014 let config = UnifiedConfig::default();
1015 config.save_to_file(&config_path).expect("operation failed in test");
1016
1017 let loaded_config =
1018 UnifiedConfig::load_from_file(&config_path).expect("operation failed in test");
1019 assert_eq!(config.metadata.name, loaded_config.metadata.name);
1020 }
1021
1022 #[test]
1023 fn test_validation() {
1024 let mut config = UnifiedConfig::default();
1025 config.metadata.name = "".to_string(); let result = config.validate();
1028 assert!(result.is_err());
1029 }
1030
1031 #[test]
1032 fn test_resource_limits_validation() {
1033 let limits = MemoryLimits {
1034 warning_threshold_percent: 150.0, ..Default::default()
1036 };
1037
1038 let result = limits.validate();
1039 assert!(result.is_err());
1040 }
1041}