Skip to main content

sz_orm_config/
lib.rs

1//! # SZ-ORM Config — 配置中心
2//!
3//! 提供配置中心抽象(Consul/Nacos 等),支持 get/set/delete/list/watch,
4//! 并可在配置变更时通过回调通知订阅者。
5//!
6//! ## 主要类型
7//!
8//! - [`ConfigCenter`] trait — 配置中心接口
9//! - [`ConfigChangeEvent`] — 配置变更事件
10
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::{Arc, Mutex};
14
15/// Callback invoked when a configuration value changes.
16/// Arguments: `(key, new_value)`. On delete, `new_value` is empty.
17pub type ConfigChangeCallback = Arc<dyn Fn(&str, &str) + Send + Sync>;
18
19/// Configuration center abstraction (Consul/Nacos/etc).
20pub trait ConfigCenter: Send + Sync {
21    fn get(&self, key: &str) -> Option<String>;
22    fn set(&mut self, key: &str, value: &str);
23    fn delete(&mut self, key: &str) -> bool;
24    fn exists(&self, key: &str) -> bool;
25    fn list(&self) -> Vec<String>;
26    /// Returns true if a watch was successfully registered.
27    /// In this in-memory implementation, registration always succeeds.
28    fn watch(&self, key: &str) -> bool;
29    /// Registers a callback for changes to `key`.
30    fn subscribe(&mut self, key: &str, callback: ConfigChangeCallback);
31}
32
33/// Configuration change event record, useful for testing and auditing.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ConfigChangeEvent {
36    pub key: String,
37    pub value: String,
38    pub deleted: bool,
39}
40
41/// Consul-style in-memory configuration center.
42pub struct ConsulConfigCenter {
43    data: HashMap<String, String>,
44    subscribers: HashMap<String, Vec<ConfigChangeCallback>>,
45    events: Mutex<Vec<ConfigChangeEvent>>,
46}
47
48impl ConsulConfigCenter {
49    pub fn new() -> Self {
50        Self {
51            data: HashMap::new(),
52            subscribers: HashMap::new(),
53            events: Mutex::new(Vec::new()),
54        }
55    }
56
57    fn notify(&self, key: &str, value: &str, deleted: bool) {
58        if let Some(callbacks) = self.subscribers.get(key) {
59            for cb in callbacks {
60                cb(key, value);
61            }
62        }
63        if let Ok(mut events) = self.events.lock() {
64            events.push(ConfigChangeEvent {
65                key: key.to_string(),
66                value: value.to_string(),
67                deleted,
68            });
69        }
70    }
71
72    /// Returns the ordered list of all change events that have occurred.
73    pub fn events(&self) -> Vec<ConfigChangeEvent> {
74        self.events.lock().map(|e| e.clone()).unwrap_or_default()
75    }
76
77    pub fn subscriber_count(&self, key: &str) -> usize {
78        self.subscribers.get(key).map(|cbs| cbs.len()).unwrap_or(0)
79    }
80}
81
82impl Default for ConsulConfigCenter {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88impl ConfigCenter for ConsulConfigCenter {
89    fn get(&self, key: &str) -> Option<String> {
90        self.data.get(key).cloned()
91    }
92
93    fn set(&mut self, key: &str, value: &str) {
94        self.data.insert(key.to_string(), value.to_string());
95        self.notify(key, value, false);
96    }
97
98    fn delete(&mut self, key: &str) -> bool {
99        let removed = self.data.remove(key).is_some();
100        if removed {
101            self.notify(key, "", true);
102        }
103        removed
104    }
105
106    fn exists(&self, key: &str) -> bool {
107        self.data.contains_key(key)
108    }
109
110    fn list(&self) -> Vec<String> {
111        let mut keys: Vec<String> = self.data.keys().cloned().collect();
112        keys.sort();
113        keys
114    }
115
116    fn watch(&self, _key: &str) -> bool {
117        true
118    }
119
120    fn subscribe(&mut self, key: &str, callback: ConfigChangeCallback) {
121        self.subscribers
122            .entry(key.to_string())
123            .or_default()
124            .push(callback);
125    }
126}
127
128/// Nacos-style in-memory configuration center.
129pub struct NacosConfigCenter {
130    data: HashMap<String, String>,
131    subscribers: HashMap<String, Vec<ConfigChangeCallback>>,
132    events: Mutex<Vec<ConfigChangeEvent>>,
133}
134
135impl NacosConfigCenter {
136    pub fn new() -> Self {
137        Self {
138            data: HashMap::new(),
139            subscribers: HashMap::new(),
140            events: Mutex::new(Vec::new()),
141        }
142    }
143
144    fn notify(&self, key: &str, value: &str, deleted: bool) {
145        if let Some(callbacks) = self.subscribers.get(key) {
146            for cb in callbacks {
147                cb(key, value);
148            }
149        }
150        if let Ok(mut events) = self.events.lock() {
151            events.push(ConfigChangeEvent {
152                key: key.to_string(),
153                value: value.to_string(),
154                deleted,
155            });
156        }
157    }
158
159    pub fn events(&self) -> Vec<ConfigChangeEvent> {
160        self.events.lock().map(|e| e.clone()).unwrap_or_default()
161    }
162
163    pub fn subscriber_count(&self, key: &str) -> usize {
164        self.subscribers.get(key).map(|cbs| cbs.len()).unwrap_or(0)
165    }
166}
167
168impl Default for NacosConfigCenter {
169    fn default() -> Self {
170        Self::new()
171    }
172}
173
174impl ConfigCenter for NacosConfigCenter {
175    fn get(&self, key: &str) -> Option<String> {
176        self.data.get(key).cloned()
177    }
178
179    fn set(&mut self, key: &str, value: &str) {
180        self.data.insert(key.to_string(), value.to_string());
181        self.notify(key, value, false);
182    }
183
184    fn delete(&mut self, key: &str) -> bool {
185        let removed = self.data.remove(key).is_some();
186        if removed {
187            self.notify(key, "", true);
188        }
189        removed
190    }
191
192    fn exists(&self, key: &str) -> bool {
193        self.data.contains_key(key)
194    }
195
196    fn list(&self) -> Vec<String> {
197        let mut keys: Vec<String> = self.data.keys().cloned().collect();
198        keys.sort();
199        keys
200    }
201
202    fn watch(&self, _key: &str) -> bool {
203        true
204    }
205
206    fn subscribe(&mut self, key: &str, callback: ConfigChangeCallback) {
207        self.subscribers
208            .entry(key.to_string())
209            .or_default()
210            .push(callback);
211    }
212}
213
214// ====================================================================
215// 配置热更新(Watch)— 轮询式监听配置变更
216// ====================================================================
217
218/// 配置热更新监听器:以固定间隔轮询配置中心,检测到值变更时通知订阅者
219///
220/// 由于当前实现为纯内存模型,Watch 通过记录上次快照并与当前值比较来检测变更。
221/// 在真实场景中可替换为 Consul/Nacos 的长轮询或 Watch API。
222pub struct ConfigWatcher {
223    /// 上次快照的配置键值对
224    last_snapshot: Mutex<HashMap<String, String>>,
225    /// 轮询间隔(毫秒)
226    pub poll_interval_ms: u64,
227    /// 变更回调列表:(key, callback)
228    watchers: Mutex<Vec<(String, ConfigChangeCallback)>>,
229}
230
231impl ConfigWatcher {
232    pub fn new(poll_interval_ms: u64) -> Self {
233        Self {
234            last_snapshot: Mutex::new(HashMap::new()),
235            poll_interval_ms: poll_interval_ms.max(100),
236            watchers: Mutex::new(Vec::new()),
237        }
238    }
239
240    /// 注册一个 key 的变更监听
241    pub fn watch(&self, key: &str, callback: ConfigChangeCallback) {
242        if let Ok(mut watchers) = self.watchers.lock() {
243            watchers.push((key.to_string(), callback));
244        }
245    }
246
247    /// 执行一次轮询检测:比较当前配置与上次快照,触发变更回调
248    /// 返回本次检测到的变更数量
249    pub fn poll<C: ConfigCenter>(&self, center: &C) -> usize {
250        let current: HashMap<String, String> = center
251            .list()
252            .into_iter()
253            .filter_map(|k| center.get(&k).map(|v| (k, v)))
254            .collect();
255
256        let mut changes = Vec::new();
257        {
258            let Ok(mut snapshot) = self.last_snapshot.lock() else {
259                return 0;
260            };
261            // 检测新增和修改
262            for (k, v) in &current {
263                match snapshot.get(k) {
264                    Some(old) if old == v => {}
265                    _ => changes.push((k.clone(), v.clone(), false)),
266                }
267            }
268            // 检测删除
269            for k in snapshot.keys() {
270                if !current.contains_key(k) {
271                    changes.push((k.clone(), String::new(), true));
272                }
273            }
274            *snapshot = current;
275        }
276
277        let watcher_count = changes.len();
278        if let Ok(watchers) = self.watchers.lock() {
279            for (key, new_value, deleted) in &changes {
280                for (watch_key, cb) in watchers.iter() {
281                    if watch_key == key {
282                        cb(key, new_value);
283                    }
284                }
285                // 标记 deleted 仅用于日志,回调已收到空值
286                let _ = deleted;
287            }
288        }
289        watcher_count
290    }
291
292    /// 返回当前注册的 watcher 数量
293    pub fn watcher_count(&self) -> usize {
294        self.watchers.lock().map(|w| w.len()).unwrap_or(0)
295    }
296}
297
298impl Default for ConfigWatcher {
299    fn default() -> Self {
300        Self::new(5000)
301    }
302}
303
304// ====================================================================
305// 多源合并(文件 + 环境变量 + 远程)— 按优先级合并配置
306// ====================================================================
307
308/// 配置来源优先级:数值越大优先级越高,高优先级覆盖低优先级
309#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
310pub enum ConfigSourcePriority {
311    /// 文件配置(最低优先级)
312    File = 0,
313    /// 远程配置中心
314    Remote = 1,
315    /// 环境变量(最高优先级)
316    Env = 2,
317}
318
319/// 多源配置合并器:从文件、环境变量、远程配置中心收集配置并按优先级合并
320pub struct MultiSourceConfig {
321    /// 文件配置
322    file_config: HashMap<String, String>,
323    /// 环境变量配置
324    env_config: HashMap<String, String>,
325    /// 远程配置中心配置
326    remote_config: HashMap<String, String>,
327    /// 合并后的缓存
328    merged: Mutex<Option<HashMap<String, String>>>,
329}
330
331impl MultiSourceConfig {
332    pub fn new() -> Self {
333        Self {
334            file_config: HashMap::new(),
335            env_config: HashMap::new(),
336            remote_config: HashMap::new(),
337            merged: Mutex::new(None),
338        }
339    }
340
341    /// 设置文件来源配置
342    pub fn set_file_config(&mut self, config: HashMap<String, String>) {
343        self.file_config = config;
344        self.invalidate_cache();
345    }
346
347    /// 设置环境变量来源配置
348    pub fn set_env_config(&mut self, config: HashMap<String, String>) {
349        self.env_config = config;
350        self.invalidate_cache();
351    }
352
353    /// 设置远程配置中心来源配置
354    pub fn set_remote_config(&mut self, config: HashMap<String, String>) {
355        self.remote_config = config;
356        self.invalidate_cache();
357    }
358
359    /// 从系统环境变量加载配置(前缀过滤)
360    pub fn load_env_vars(&mut self, prefix: &str) {
361        for (key, value) in std::env::vars() {
362            if let Some(stripped) = key.strip_prefix(prefix) {
363                // 去掉前缀并转小写
364                self.env_config.insert(stripped.to_lowercase(), value);
365            }
366        }
367        self.invalidate_cache();
368    }
369
370    /// 从 JSON 文件内容加载配置
371    pub fn load_json(&mut self, json: &str) -> Result<(), String> {
372        let parsed: HashMap<String, String> =
373            serde_json::from_str(json).map_err(|e| format!("parse json error: {}", e))?;
374        self.file_config = parsed;
375        self.invalidate_cache();
376        Ok(())
377    }
378
379    /// 合并所有来源并返回结果(高优先级覆盖低优先级)
380    pub fn merge(&self) -> HashMap<String, String> {
381        if let Ok(cache) = self.merged.lock() {
382            if let Some(cached) = cache.as_ref() {
383                return cached.clone();
384            }
385        }
386        let mut result = HashMap::new();
387        // 按 File -> Remote -> Env 顺序合并
388        for (k, v) in &self.file_config {
389            result.insert(k.clone(), v.clone());
390        }
391        for (k, v) in &self.remote_config {
392            result.insert(k.clone(), v.clone());
393        }
394        for (k, v) in &self.env_config {
395            result.insert(k.clone(), v.clone());
396        }
397        if let Ok(mut cache) = self.merged.lock() {
398            *cache = Some(result.clone());
399        }
400        result
401    }
402
403    /// 获取合并后的配置值
404    pub fn get(&self, key: &str) -> Option<String> {
405        self.merge().get(key).cloned()
406    }
407
408    /// 返回某个 key 的来源优先级
409    pub fn source_of(&self, key: &str) -> Option<ConfigSourcePriority> {
410        if self.env_config.contains_key(key) {
411            Some(ConfigSourcePriority::Env)
412        } else if self.remote_config.contains_key(key) {
413            Some(ConfigSourcePriority::Remote)
414        } else if self.file_config.contains_key(key) {
415            Some(ConfigSourcePriority::File)
416        } else {
417            None
418        }
419    }
420
421    fn invalidate_cache(&self) {
422        if let Ok(mut cache) = self.merged.lock() {
423            *cache = None;
424        }
425    }
426}
427
428impl Default for MultiSourceConfig {
429    fn default() -> Self {
430        Self::new()
431    }
432}
433
434// ====================================================================
435// 配置验证(Schema Validation)
436// ====================================================================
437
438/// 配置字段类型约束
439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
440pub enum ConfigFieldType {
441    String,
442    Integer,
443    Float,
444    Boolean,
445    Url,
446    Email,
447}
448
449/// 配置字段 schema 定义
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct ConfigFieldSchema {
452    /// 字段名
453    pub name: String,
454    /// 字段类型
455    pub field_type: ConfigFieldType,
456    /// 是否必填
457    pub required: bool,
458    /// 最小值(数值类型)
459    pub min: Option<f64>,
460    /// 最大值(数值类型)
461    pub max: Option<f64>,
462    /// 最小长度(字符串类型)
463    pub min_length: Option<usize>,
464    /// 最大长度(字符串类型)
465    pub max_length: Option<usize>,
466    /// 枚举允许值
467    pub allowed_values: Option<Vec<String>>,
468}
469
470impl ConfigFieldSchema {
471    pub fn new(name: impl Into<String>, field_type: ConfigFieldType) -> Self {
472        Self {
473            name: name.into(),
474            field_type,
475            required: false,
476            min: None,
477            max: None,
478            min_length: None,
479            max_length: None,
480            allowed_values: None,
481        }
482    }
483
484    pub fn required(mut self) -> Self {
485        self.required = true;
486        self
487    }
488
489    pub fn with_range(mut self, min: f64, max: f64) -> Self {
490        self.min = Some(min);
491        self.max = Some(max);
492        self
493    }
494
495    pub fn with_length(mut self, min: usize, max: usize) -> Self {
496        self.min_length = Some(min);
497        self.max_length = Some(max);
498        self
499    }
500
501    pub fn with_allowed_values(mut self, values: Vec<String>) -> Self {
502        self.allowed_values = Some(values);
503        self
504    }
505}
506
507/// 配置 schema 验证器:根据字段定义验证配置值合法性
508pub struct SchemaValidator {
509    fields: Vec<ConfigFieldSchema>,
510}
511
512/// 验证错误
513#[derive(Debug, Clone, PartialEq, Eq)]
514pub enum ValidationError {
515    /// 必填字段缺失
516    MissingRequired(String),
517    /// 类型不匹配
518    TypeMismatch(String, String),
519    /// 数值超出范围
520    OutOfRange(String, String),
521    /// 长度超出限制
522    LengthExceeded(String, String),
523    /// 值不在允许枚举内
524    NotAllowed(String, String),
525    /// 格式无效
526    InvalidFormat(String, String),
527}
528
529impl std::fmt::Display for ValidationError {
530    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
531        match self {
532            ValidationError::MissingRequired(k) => write!(f, "missing required field: {}", k),
533            ValidationError::TypeMismatch(k, v) => write!(f, "type mismatch for {}: {}", k, v),
534            ValidationError::OutOfRange(k, v) => write!(f, "value out of range for {}: {}", k, v),
535            ValidationError::LengthExceeded(k, v) => {
536                write!(f, "length exceeded for {}: {}", k, v)
537            }
538            ValidationError::NotAllowed(k, v) => write!(f, "value not allowed for {}: {}", k, v),
539            ValidationError::InvalidFormat(k, v) => write!(f, "invalid format for {}: {}", k, v),
540        }
541    }
542}
543
544impl SchemaValidator {
545    pub fn new() -> Self {
546        Self { fields: Vec::new() }
547    }
548
549    pub fn add_field(&mut self, schema: ConfigFieldSchema) {
550        self.fields.push(schema);
551    }
552
553    /// 验证配置 map,返回所有验证错误(空 Vec 表示通过)
554    pub fn validate(&self, config: &HashMap<String, String>) -> Vec<ValidationError> {
555        let mut errors = Vec::new();
556        for field in &self.fields {
557            match config.get(&field.name) {
558                None => {
559                    if field.required {
560                        errors.push(ValidationError::MissingRequired(field.name.clone()));
561                    }
562                }
563                Some(value) => {
564                    if let Err(e) = self.validate_field(field, value) {
565                        errors.push(e);
566                    }
567                }
568            }
569        }
570        errors
571    }
572
573    fn validate_field(
574        &self,
575        schema: &ConfigFieldSchema,
576        value: &str,
577    ) -> Result<(), ValidationError> {
578        // 枚举值检查
579        if let Some(allowed) = &schema.allowed_values {
580            if !allowed.iter().any(|v| v == value) {
581                return Err(ValidationError::NotAllowed(
582                    schema.name.clone(),
583                    value.to_string(),
584                ));
585            }
586        }
587
588        match schema.field_type {
589            ConfigFieldType::String => {
590                if let Some(max_len) = schema.max_length {
591                    if value.len() > max_len {
592                        return Err(ValidationError::LengthExceeded(
593                            schema.name.clone(),
594                            format!("len={} > {}", value.len(), max_len),
595                        ));
596                    }
597                }
598                if let Some(min_len) = schema.min_length {
599                    if value.len() < min_len {
600                        return Err(ValidationError::LengthExceeded(
601                            schema.name.clone(),
602                            format!("len={} < {}", value.len(), min_len),
603                        ));
604                    }
605                }
606            }
607            ConfigFieldType::Integer => {
608                let n: i64 = value.parse().map_err(|_| {
609                    ValidationError::TypeMismatch(schema.name.clone(), value.to_string())
610                })?;
611                if let Some(min) = schema.min {
612                    if (n as f64) < min {
613                        return Err(ValidationError::OutOfRange(
614                            schema.name.clone(),
615                            format!("{} < {}", n, min),
616                        ));
617                    }
618                }
619                if let Some(max) = schema.max {
620                    if (n as f64) > max {
621                        return Err(ValidationError::OutOfRange(
622                            schema.name.clone(),
623                            format!("{} > {}", n, max),
624                        ));
625                    }
626                }
627            }
628            ConfigFieldType::Float => {
629                let n: f64 = value.parse().map_err(|_| {
630                    ValidationError::TypeMismatch(schema.name.clone(), value.to_string())
631                })?;
632                if let Some(min) = schema.min {
633                    if n < min {
634                        return Err(ValidationError::OutOfRange(
635                            schema.name.clone(),
636                            format!("{} < {}", n, min),
637                        ));
638                    }
639                }
640                if let Some(max) = schema.max {
641                    if n > max {
642                        return Err(ValidationError::OutOfRange(
643                            schema.name.clone(),
644                            format!("{} > {}", n, max),
645                        ));
646                    }
647                }
648            }
649            ConfigFieldType::Boolean => {
650                if value != "true" && value != "false" {
651                    return Err(ValidationError::TypeMismatch(
652                        schema.name.clone(),
653                        value.to_string(),
654                    ));
655                }
656            }
657            ConfigFieldType::Url => {
658                if !value.starts_with("http://") && !value.starts_with("https://") {
659                    return Err(ValidationError::InvalidFormat(
660                        schema.name.clone(),
661                        "not a valid URL".to_string(),
662                    ));
663                }
664            }
665            ConfigFieldType::Email => {
666                if !value.contains('@') || !value.contains('.') {
667                    return Err(ValidationError::InvalidFormat(
668                        schema.name.clone(),
669                        "not a valid email".to_string(),
670                    ));
671                }
672            }
673        }
674        Ok(())
675    }
676}
677
678impl Default for SchemaValidator {
679    fn default() -> Self {
680        Self::new()
681    }
682}
683
684// ====================================================================
685// 配置加密 — 对敏感配置值进行加解密
686// ====================================================================
687
688/// 配置加密器:使用 XOR + Base64 对敏感配置值进行对称加密
689///
690/// 适用场景:数据库密码、API Key 等敏感配置不应明文存储在配置文件中。
691/// 加密格式:`ENC(base64(xor(plaintext, key)))`
692pub struct ConfigEncryption {
693    /// 加密密钥
694    key: Vec<u8>,
695}
696
697/// 加密值前缀,用于标识已加密的配置项
698pub const ENCRYPTED_PREFIX: &str = "ENC(";
699pub const ENCRYPTED_SUFFIX: &str = ")";
700
701impl ConfigEncryption {
702    pub fn new(key: impl Into<String>) -> Self {
703        Self {
704            key: key.into().into_bytes(),
705        }
706    }
707
708    /// 加密明文,返回 `ENC(base64)` 格式字符串
709    pub fn encrypt(&self, plaintext: &str) -> String {
710        let bytes = plaintext.as_bytes();
711        let encrypted: Vec<u8> = bytes
712            .iter()
713            .enumerate()
714            .map(|(i, &b)| b ^ self.key[i % self.key.len()])
715            .collect();
716        let encoded = base64_encode(&encrypted);
717        format!("{}{}{}", ENCRYPTED_PREFIX, encoded, ENCRYPTED_SUFFIX)
718    }
719
720    /// 解密 `ENC(base64)` 格式字符串,返回明文
721    pub fn decrypt(&self, ciphertext: &str) -> Result<String, String> {
722        let inner = ciphertext
723            .strip_prefix(ENCRYPTED_PREFIX)
724            .and_then(|s| s.strip_suffix(ENCRYPTED_SUFFIX))
725            .ok_or_else(|| "invalid encrypted format, expected ENC(...)".to_string())?;
726        let decoded = base64_decode(inner)?;
727        let decrypted: Vec<u8> = decoded
728            .iter()
729            .enumerate()
730            .map(|(i, &b)| b ^ self.key[i % self.key.len()])
731            .collect();
732        String::from_utf8(decrypted).map_err(|e| format!("decrypt utf8 error: {}", e))
733    }
734
735    /// 判断值是否已加密(以 ENC( 开头)
736    pub fn is_encrypted(value: &str) -> bool {
737        value.starts_with(ENCRYPTED_PREFIX) && value.ends_with(ENCRYPTED_SUFFIX)
738    }
739
740    /// 如果值已加密则解密,否则原样返回
741    pub fn decrypt_if_needed(&self, value: &str) -> Result<String, String> {
742        if Self::is_encrypted(value) {
743            self.decrypt(value)
744        } else {
745            Ok(value.to_string())
746        }
747    }
748
749    /// 批量解密配置 map 中所有已加密的值
750    pub fn decrypt_config(
751        &self,
752        config: &HashMap<String, String>,
753    ) -> Result<HashMap<String, String>, String> {
754        let mut result = HashMap::new();
755        for (k, v) in config {
756            let decrypted = self.decrypt_if_needed(v)?;
757            result.insert(k.clone(), decrypted);
758        }
759        Ok(result)
760    }
761}
762
763/// 简易 Base64 编码(不依赖外部 crate)
764fn base64_encode(data: &[u8]) -> String {
765    const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
766    let mut result = String::with_capacity(data.len().div_ceil(3) * 4);
767    let mut i = 0;
768    while i + 3 <= data.len() {
769        let n = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8) | (data[i + 2] as u32);
770        result.push(CHARS[((n >> 18) & 0x3F) as usize] as char);
771        result.push(CHARS[((n >> 12) & 0x3F) as usize] as char);
772        result.push(CHARS[((n >> 6) & 0x3F) as usize] as char);
773        result.push(CHARS[(n & 0x3F) as usize] as char);
774        i += 3;
775    }
776    let remaining = data.len() - i;
777    if remaining == 1 {
778        let n = (data[i] as u32) << 16;
779        result.push(CHARS[((n >> 18) & 0x3F) as usize] as char);
780        result.push(CHARS[((n >> 12) & 0x3F) as usize] as char);
781        result.push('=');
782        result.push('=');
783    } else if remaining == 2 {
784        let n = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8);
785        result.push(CHARS[((n >> 18) & 0x3F) as usize] as char);
786        result.push(CHARS[((n >> 12) & 0x3F) as usize] as char);
787        result.push(CHARS[((n >> 6) & 0x3F) as usize] as char);
788        result.push('=');
789    }
790    result
791}
792
793/// 简易 Base64 解码
794fn base64_decode(s: &str) -> Result<Vec<u8>, String> {
795    const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
796    fn char_to_val(c: u8) -> Result<u8, String> {
797        CHARS
798            .iter()
799            .position(|&ch| ch == c)
800            .map(|p| p as u8)
801            .ok_or_else(|| format!("invalid base64 char: {}", c as char))
802    }
803    let trimmed = s.trim_end_matches('=');
804    let bytes = trimmed.as_bytes();
805    let mut result = Vec::with_capacity(bytes.len() * 3 / 4);
806    let mut i = 0;
807    while i + 4 <= bytes.len() {
808        let a = char_to_val(bytes[i])? as u32;
809        let b = char_to_val(bytes[i + 1])? as u32;
810        let c = char_to_val(bytes[i + 2])? as u32;
811        let d = char_to_val(bytes[i + 3])? as u32;
812        let n = (a << 18) | (b << 12) | (c << 6) | d;
813        result.push((n >> 16) as u8);
814        result.push((n >> 8) as u8);
815        result.push(n as u8);
816        i += 4;
817    }
818    let remaining = bytes.len() - i;
819    if remaining == 2 {
820        let a = char_to_val(bytes[i])? as u32;
821        let b = char_to_val(bytes[i + 1])? as u32;
822        let n = (a << 18) | (b << 12);
823        result.push((n >> 16) as u8);
824    } else if remaining == 3 {
825        let a = char_to_val(bytes[i])? as u32;
826        let b = char_to_val(bytes[i + 1])? as u32;
827        let c = char_to_val(bytes[i + 2])? as u32;
828        let n = (a << 18) | (b << 12) | (c << 6);
829        result.push((n >> 16) as u8);
830        result.push((n >> 8) as u8);
831    } else if remaining != 0 {
832        return Err(format!("invalid base64 length, remainder: {}", remaining));
833    }
834    Ok(result)
835}
836
837#[cfg(test)]
838mod tests {
839    use super::*;
840    use std::sync::atomic::{AtomicU32, Ordering};
841
842    #[test]
843    fn test_consul_set_and_get() {
844        let mut c = ConsulConfigCenter::new();
845        c.set("k", "v");
846        assert_eq!(c.get("k"), Some("v".to_string()));
847        assert!(c.exists("k"));
848        assert!(!c.exists("missing"));
849    }
850
851    #[test]
852    fn test_consul_get_missing() {
853        let c = ConsulConfigCenter::new();
854        assert_eq!(c.get("missing"), None);
855    }
856
857    #[test]
858    fn test_consul_delete() {
859        let mut c = ConsulConfigCenter::new();
860        c.set("k", "v");
861        assert!(c.delete("k"));
862        assert!(!c.exists("k"));
863        assert_eq!(c.get("k"), None);
864        // Deleting a missing key returns false
865        assert!(!c.delete("missing"));
866    }
867
868    #[test]
869    fn test_consul_list_sorted() {
870        let mut c = ConsulConfigCenter::new();
871        c.set("z", "1");
872        c.set("a", "2");
873        c.set("m", "3");
874        assert_eq!(c.list(), vec!["a", "m", "z"]);
875    }
876
877    #[test]
878    fn test_consul_watch_returns_true() {
879        let c = ConsulConfigCenter::new();
880        assert!(c.watch("any-key"));
881    }
882
883    #[test]
884    fn test_nacos_set_and_get() {
885        let mut c = NacosConfigCenter::new();
886        c.set("k", "v");
887        assert_eq!(c.get("k"), Some("v".to_string()));
888    }
889
890    #[test]
891    fn test_nacos_watch_returns_true() {
892        let c = NacosConfigCenter::new();
893        assert!(c.watch("k"));
894    }
895
896    #[test]
897    fn test_nacos_delete() {
898        let mut c = NacosConfigCenter::new();
899        c.set("k", "v");
900        assert!(c.delete("k"));
901        assert_eq!(c.get("k"), None);
902    }
903
904    #[test]
905    fn test_nacos_list_sorted() {
906        let mut c = NacosConfigCenter::new();
907        c.set("b", "1");
908        c.set("a", "2");
909        assert_eq!(c.list(), vec!["a", "b"]);
910    }
911
912    // ---- Subscribe / notify tests ----
913
914    #[test]
915    fn test_consul_subscribe_receives_set_events() {
916        let mut c = ConsulConfigCenter::new();
917        let count = Arc::new(AtomicU32::new(0));
918        let last_value = Arc::new(Mutex::new(String::new()));
919
920        let cb_count = count.clone();
921        let cb_value = last_value.clone();
922        c.subscribe(
923            "app.config",
924            Arc::new(move |_key, value| {
925                cb_count.fetch_add(1, Ordering::SeqCst);
926                *cb_value.lock().unwrap() = value.to_string();
927            }),
928        );
929
930        c.set("app.config", "v1");
931        assert_eq!(count.load(Ordering::SeqCst), 1);
932        assert_eq!(*last_value.lock().unwrap(), "v1");
933
934        c.set("app.config", "v2");
935        assert_eq!(count.load(Ordering::SeqCst), 2);
936        assert_eq!(*last_value.lock().unwrap(), "v2");
937    }
938
939    #[test]
940    fn test_consul_subscribe_receives_delete_events() {
941        let mut c = ConsulConfigCenter::new();
942        let deleted = Arc::new(AtomicU32::new(0));
943        let last_value = Arc::new(Mutex::new(String::new()));
944
945        let d_count = deleted.clone();
946        let d_value = last_value.clone();
947        c.subscribe(
948            "k",
949            Arc::new(move |_key, value| {
950                d_count.fetch_add(1, Ordering::SeqCst);
951                *d_value.lock().unwrap() = value.to_string();
952            }),
953        );
954
955        c.set("k", "v");
956        c.delete("k");
957        assert_eq!(deleted.load(Ordering::SeqCst), 2); // set + delete
958        assert_eq!(*last_value.lock().unwrap(), ""); // delete sends empty
959    }
960
961    #[test]
962    fn test_consul_multiple_subscribers() {
963        let mut c = ConsulConfigCenter::new();
964        let c1 = Arc::new(AtomicU32::new(0));
965        let c2 = Arc::new(AtomicU32::new(0));
966
967        let c1_clone = c1.clone();
968        c.subscribe(
969            "k",
970            Arc::new(move |_key, _value| {
971                c1_clone.fetch_add(1, Ordering::SeqCst);
972            }),
973        );
974
975        let c2_clone = c2.clone();
976        c.subscribe(
977            "k",
978            Arc::new(move |_key, _value| {
979                c2_clone.fetch_add(1, Ordering::SeqCst);
980            }),
981        );
982
983        assert_eq!(c.subscriber_count("k"), 2);
984        c.set("k", "v");
985        assert_eq!(c1.load(Ordering::SeqCst), 1);
986        assert_eq!(c2.load(Ordering::SeqCst), 1);
987    }
988
989    #[test]
990    fn test_consul_subscribers_are_keyed() {
991        let mut c = ConsulConfigCenter::new();
992        let other_count = Arc::new(AtomicU32::new(0));
993        let oc = other_count.clone();
994        c.subscribe(
995            "other",
996            Arc::new(move |_key, _value| {
997                oc.fetch_add(1, Ordering::SeqCst);
998            }),
999        );
1000
1001        c.set("this", "v");
1002        // Should not notify subscribers of "other"
1003        assert_eq!(other_count.load(Ordering::SeqCst), 0);
1004
1005        c.set("other", "v");
1006        assert_eq!(other_count.load(Ordering::SeqCst), 1);
1007    }
1008
1009    #[test]
1010    fn test_consul_events_record() {
1011        let mut c = ConsulConfigCenter::new();
1012        c.set("a", "1");
1013        c.set("b", "2");
1014        c.delete("a");
1015
1016        let events = c.events();
1017        assert_eq!(events.len(), 3);
1018        assert_eq!(events[0].key, "a");
1019        assert_eq!(events[0].value, "1");
1020        assert!(!events[0].deleted);
1021        assert_eq!(events[2].key, "a");
1022        assert!(events[2].deleted);
1023    }
1024
1025    #[test]
1026    fn test_nacos_subscribe_receives_events() {
1027        let mut c = NacosConfigCenter::new();
1028        let received = Arc::new(Mutex::new(Vec::<(String, String)>::new()));
1029        let r = received.clone();
1030        c.subscribe(
1031            "cfg",
1032            Arc::new(move |key, value| {
1033                r.lock().unwrap().push((key.to_string(), value.to_string()));
1034            }),
1035        );
1036        c.set("cfg", "v1");
1037        c.set("cfg", "v2");
1038        let received = received.lock().unwrap();
1039        assert_eq!(
1040            *received,
1041            vec![
1042                ("cfg".to_string(), "v1".to_string()),
1043                ("cfg".to_string(), "v2".to_string()),
1044            ]
1045        );
1046    }
1047
1048    #[test]
1049    fn test_nacos_events_record() {
1050        let mut c = NacosConfigCenter::new();
1051        c.set("k", "v");
1052        let events = c.events();
1053        assert_eq!(events.len(), 1);
1054        assert_eq!(events[0].key, "k");
1055        assert_eq!(events[0].value, "v");
1056    }
1057
1058    #[test]
1059    fn test_subscribe_via_trait_object() {
1060        // Verify subscribe works through a boxed trait object.
1061        let mut boxed: Box<dyn ConfigCenter> = Box::new(ConsulConfigCenter::new());
1062        let count = Arc::new(AtomicU32::new(0));
1063        let c = count.clone();
1064        boxed.subscribe(
1065            "k",
1066            Arc::new(move |_key, _value| {
1067                c.fetch_add(1, Ordering::SeqCst);
1068            }),
1069        );
1070        boxed.set("k", "v");
1071        assert_eq!(count.load(Ordering::SeqCst), 1);
1072    }
1073
1074    #[test]
1075    fn test_overwrite_existing_value_notifies() {
1076        let mut c = ConsulConfigCenter::new();
1077        let count = Arc::new(AtomicU32::new(0));
1078        let c1 = count.clone();
1079        c.subscribe(
1080            "k",
1081            Arc::new(move |_key, _value| {
1082                c1.fetch_add(1, Ordering::SeqCst);
1083            }),
1084        );
1085        c.set("k", "v1");
1086        c.set("k", "v2");
1087        c.set("k", "v3");
1088        assert_eq!(count.load(Ordering::SeqCst), 3);
1089    }
1090
1091    // ====================================================================
1092    // ConfigWatcher 测试
1093    // ====================================================================
1094
1095    #[test]
1096    fn test_config_watcher_detects_new_key() {
1097        let mut center = ConsulConfigCenter::new();
1098        let watcher = ConfigWatcher::new(1000);
1099        let received = Arc::new(Mutex::new(String::new()));
1100        let r = received.clone();
1101        watcher.watch(
1102            "app.port",
1103            Arc::new(move |_k, v| {
1104                *r.lock().unwrap() = v.to_string();
1105            }),
1106        );
1107
1108        center.set("app.port", "8080");
1109        let changes = watcher.poll(&center);
1110        assert_eq!(changes, 1);
1111        assert_eq!(*received.lock().unwrap(), "8080");
1112    }
1113
1114    #[test]
1115    fn test_config_watcher_detects_value_change() {
1116        let mut center = ConsulConfigCenter::new();
1117        center.set("k", "v1");
1118        let watcher = ConfigWatcher::new(1000);
1119        // 第一次 poll 建立基线
1120        watcher.poll(&center);
1121
1122        let received = Arc::new(AtomicU32::new(0));
1123        let r = received.clone();
1124        watcher.watch(
1125            "k",
1126            Arc::new(move |_, _| {
1127                r.fetch_add(1, Ordering::SeqCst);
1128            }),
1129        );
1130
1131        center.set("k", "v2");
1132        let changes = watcher.poll(&center);
1133        assert_eq!(changes, 1);
1134        assert_eq!(received.load(Ordering::SeqCst), 1);
1135    }
1136
1137    #[test]
1138    fn test_config_watcher_detects_deletion() {
1139        let mut center = ConsulConfigCenter::new();
1140        center.set("k", "v");
1141        let watcher = ConfigWatcher::new(1000);
1142        watcher.poll(&center);
1143
1144        let deleted_value = Arc::new(Mutex::new("not_empty".to_string()));
1145        let d = deleted_value.clone();
1146        watcher.watch(
1147            "k",
1148            Arc::new(move |_, v| {
1149                *d.lock().unwrap() = v.to_string();
1150            }),
1151        );
1152
1153        center.delete("k");
1154        let changes = watcher.poll(&center);
1155        assert_eq!(changes, 1);
1156        assert_eq!(*deleted_value.lock().unwrap(), "");
1157    }
1158
1159    #[test]
1160    fn test_config_watcher_no_change_returns_zero() {
1161        let mut center = ConsulConfigCenter::new();
1162        center.set("k", "v");
1163        let watcher = ConfigWatcher::new(1000);
1164        watcher.poll(&center);
1165        // 再次 poll 无变更
1166        let changes = watcher.poll(&center);
1167        assert_eq!(changes, 0);
1168    }
1169
1170    #[test]
1171    fn test_config_watcher_watcher_count() {
1172        let watcher = ConfigWatcher::new(1000);
1173        assert_eq!(watcher.watcher_count(), 0);
1174        watcher.watch("a", Arc::new(|_, _| {}));
1175        watcher.watch("b", Arc::new(|_, _| {}));
1176        assert_eq!(watcher.watcher_count(), 2);
1177    }
1178
1179    #[test]
1180    fn test_config_watcher_default_interval() {
1181        let watcher = ConfigWatcher::default();
1182        assert_eq!(watcher.poll_interval_ms, 5000);
1183    }
1184
1185    #[test]
1186    fn test_config_watcher_min_interval_clamped() {
1187        let watcher = ConfigWatcher::new(10);
1188        assert_eq!(watcher.poll_interval_ms, 100);
1189    }
1190
1191    // ====================================================================
1192    // MultiSourceConfig 测试
1193    // ====================================================================
1194
1195    #[test]
1196    fn test_multi_source_env_overrides_remote_overrides_file() {
1197        let mut ms = MultiSourceConfig::new();
1198        let mut file = HashMap::new();
1199        file.insert("k".to_string(), "file_val".to_string());
1200        ms.set_file_config(file);
1201
1202        let mut remote = HashMap::new();
1203        remote.insert("k".to_string(), "remote_val".to_string());
1204        ms.set_remote_config(remote);
1205
1206        let mut env = HashMap::new();
1207        env.insert("k".to_string(), "env_val".to_string());
1208        ms.set_env_config(env);
1209
1210        assert_eq!(ms.get("k"), Some("env_val".to_string()));
1211        assert_eq!(ms.source_of("k"), Some(ConfigSourcePriority::Env));
1212    }
1213
1214    #[test]
1215    fn test_multi_source_remote_overrides_file() {
1216        let mut ms = MultiSourceConfig::new();
1217        let mut file = HashMap::new();
1218        file.insert("k".to_string(), "file_val".to_string());
1219        ms.set_file_config(file);
1220
1221        let mut remote = HashMap::new();
1222        remote.insert("k".to_string(), "remote_val".to_string());
1223        ms.set_remote_config(remote);
1224
1225        assert_eq!(ms.get("k"), Some("remote_val".to_string()));
1226        assert_eq!(ms.source_of("k"), Some(ConfigSourcePriority::Remote));
1227    }
1228
1229    #[test]
1230    fn test_multi_source_file_only() {
1231        let mut ms = MultiSourceConfig::new();
1232        let mut file = HashMap::new();
1233        file.insert("k".to_string(), "file_val".to_string());
1234        ms.set_file_config(file);
1235
1236        assert_eq!(ms.get("k"), Some("file_val".to_string()));
1237        assert_eq!(ms.source_of("k"), Some(ConfigSourcePriority::File));
1238    }
1239
1240    #[test]
1241    fn test_multi_source_missing_key_returns_none() {
1242        let ms = MultiSourceConfig::new();
1243        assert_eq!(ms.get("missing"), None);
1244        assert_eq!(ms.source_of("missing"), None);
1245    }
1246
1247    #[test]
1248    fn test_multi_source_merge_combines_all_keys() {
1249        let mut ms = MultiSourceConfig::new();
1250        let mut file = HashMap::new();
1251        file.insert("file_key".to_string(), "fv".to_string());
1252        ms.set_file_config(file);
1253
1254        let mut remote = HashMap::new();
1255        remote.insert("remote_key".to_string(), "rv".to_string());
1256        ms.set_remote_config(remote);
1257
1258        let mut env = HashMap::new();
1259        env.insert("env_key".to_string(), "ev".to_string());
1260        ms.set_env_config(env);
1261
1262        let merged = ms.merge();
1263        assert_eq!(merged.len(), 3);
1264        assert_eq!(merged.get("file_key"), Some(&"fv".to_string()));
1265        assert_eq!(merged.get("remote_key"), Some(&"rv".to_string()));
1266        assert_eq!(merged.get("env_key"), Some(&"ev".to_string()));
1267    }
1268
1269    #[test]
1270    fn test_multi_source_load_json() {
1271        let mut ms = MultiSourceConfig::new();
1272        ms.load_json(r#"{"host":"localhost","port":"3306"}"#)
1273            .unwrap();
1274        assert_eq!(ms.get("host"), Some("localhost".to_string()));
1275        assert_eq!(ms.get("port"), Some("3306".to_string()));
1276    }
1277
1278    #[test]
1279    fn test_multi_source_load_json_invalid() {
1280        let mut ms = MultiSourceConfig::new();
1281        assert!(ms.load_json("not json").is_err());
1282    }
1283
1284    #[test]
1285    fn test_multi_source_cache_invalidated_on_update() {
1286        let mut ms = MultiSourceConfig::new();
1287        let mut file = HashMap::new();
1288        file.insert("k".to_string(), "v1".to_string());
1289        ms.set_file_config(file);
1290        assert_eq!(ms.get("k"), Some("v1".to_string()));
1291
1292        let mut file2 = HashMap::new();
1293        file2.insert("k".to_string(), "v2".to_string());
1294        ms.set_file_config(file2);
1295        assert_eq!(ms.get("k"), Some("v2".to_string()));
1296    }
1297
1298    // ====================================================================
1299    // SchemaValidator 测试
1300    // ====================================================================
1301
1302    #[test]
1303    fn test_schema_validates_integer_in_range() {
1304        let mut validator = SchemaValidator::new();
1305        validator.add_field(
1306            ConfigFieldSchema::new("port", ConfigFieldType::Integer)
1307                .required()
1308                .with_range(1.0, 65535.0),
1309        );
1310        let mut config = HashMap::new();
1311        config.insert("port".to_string(), "3306".to_string());
1312        let errors = validator.validate(&config);
1313        assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1314    }
1315
1316    #[test]
1317    fn test_schema_missing_required_field() {
1318        let mut validator = SchemaValidator::new();
1319        validator.add_field(ConfigFieldSchema::new("port", ConfigFieldType::Integer).required());
1320        let config = HashMap::new();
1321        let errors = validator.validate(&config);
1322        assert_eq!(errors.len(), 1);
1323        assert_eq!(
1324            errors[0],
1325            ValidationError::MissingRequired("port".to_string())
1326        );
1327    }
1328
1329    #[test]
1330    fn test_schema_integer_out_of_range() {
1331        let mut validator = SchemaValidator::new();
1332        validator.add_field(
1333            ConfigFieldSchema::new("port", ConfigFieldType::Integer).with_range(1.0, 65535.0),
1334        );
1335        let mut config = HashMap::new();
1336        config.insert("port".to_string(), "99999".to_string());
1337        let errors = validator.validate(&config);
1338        assert_eq!(errors.len(), 1);
1339        assert!(matches!(errors[0], ValidationError::OutOfRange(_, _)));
1340    }
1341
1342    #[test]
1343    fn test_schema_type_mismatch() {
1344        let mut validator = SchemaValidator::new();
1345        validator.add_field(ConfigFieldSchema::new("port", ConfigFieldType::Integer));
1346        let mut config = HashMap::new();
1347        config.insert("port".to_string(), "not_a_number".to_string());
1348        let errors = validator.validate(&config);
1349        assert_eq!(errors.len(), 1);
1350        assert!(matches!(errors[0], ValidationError::TypeMismatch(_, _)));
1351    }
1352
1353    #[test]
1354    fn test_schema_boolean_valid() {
1355        let mut validator = SchemaValidator::new();
1356        validator.add_field(ConfigFieldSchema::new("debug", ConfigFieldType::Boolean));
1357        let mut config = HashMap::new();
1358        config.insert("debug".to_string(), "true".to_string());
1359        assert!(validator.validate(&config).is_empty());
1360
1361        config.insert("debug".to_string(), "false".to_string());
1362        assert!(validator.validate(&config).is_empty());
1363    }
1364
1365    #[test]
1366    fn test_schema_boolean_invalid() {
1367        let mut validator = SchemaValidator::new();
1368        validator.add_field(ConfigFieldSchema::new("debug", ConfigFieldType::Boolean));
1369        let mut config = HashMap::new();
1370        config.insert("debug".to_string(), "yes".to_string());
1371        let errors = validator.validate(&config);
1372        assert_eq!(errors.len(), 1);
1373        assert!(matches!(errors[0], ValidationError::TypeMismatch(_, _)));
1374    }
1375
1376    #[test]
1377    fn test_schema_string_length() {
1378        let mut validator = SchemaValidator::new();
1379        validator
1380            .add_field(ConfigFieldSchema::new("name", ConfigFieldType::String).with_length(2, 10));
1381        let mut config = HashMap::new();
1382        config.insert("name".to_string(), "ab".to_string());
1383        assert!(validator.validate(&config).is_empty());
1384
1385        config.insert("name".to_string(), "a".to_string());
1386        assert_eq!(validator.validate(&config).len(), 1);
1387
1388        config.insert("name".to_string(), "this_is_way_too_long".to_string());
1389        assert_eq!(validator.validate(&config).len(), 1);
1390    }
1391
1392    #[test]
1393    fn test_schema_allowed_values() {
1394        let mut validator = SchemaValidator::new();
1395        validator.add_field(
1396            ConfigFieldSchema::new("level", ConfigFieldType::String).with_allowed_values(vec![
1397                "info".into(),
1398                "warn".into(),
1399                "error".into(),
1400            ]),
1401        );
1402        let mut config = HashMap::new();
1403        config.insert("level".to_string(), "info".to_string());
1404        assert!(validator.validate(&config).is_empty());
1405
1406        config.insert("level".to_string(), "debug".to_string());
1407        let errors = validator.validate(&config);
1408        assert_eq!(errors.len(), 1);
1409        assert!(matches!(errors[0], ValidationError::NotAllowed(_, _)));
1410    }
1411
1412    #[test]
1413    fn test_schema_url_format() {
1414        let mut validator = SchemaValidator::new();
1415        validator.add_field(ConfigFieldSchema::new("endpoint", ConfigFieldType::Url));
1416        let mut config = HashMap::new();
1417        config.insert("endpoint".to_string(), "https://example.com".to_string());
1418        assert!(validator.validate(&config).is_empty());
1419
1420        config.insert("endpoint".to_string(), "ftp://bad".to_string());
1421        assert_eq!(validator.validate(&config).len(), 1);
1422    }
1423
1424    #[test]
1425    fn test_schema_email_format() {
1426        let mut validator = SchemaValidator::new();
1427        validator.add_field(ConfigFieldSchema::new("email", ConfigFieldType::Email));
1428        let mut config = HashMap::new();
1429        config.insert("email".to_string(), "user@example.com".to_string());
1430        assert!(validator.validate(&config).is_empty());
1431
1432        config.insert("email".to_string(), "not_an_email".to_string());
1433        assert_eq!(validator.validate(&config).len(), 1);
1434    }
1435
1436    #[test]
1437    fn test_schema_float_range() {
1438        let mut validator = SchemaValidator::new();
1439        validator.add_field(
1440            ConfigFieldSchema::new("ratio", ConfigFieldType::Float).with_range(0.0, 1.0),
1441        );
1442        let mut config = HashMap::new();
1443        config.insert("ratio".to_string(), "0.5".to_string());
1444        assert!(validator.validate(&config).is_empty());
1445
1446        config.insert("ratio".to_string(), "1.5".to_string());
1447        assert_eq!(validator.validate(&config).len(), 1);
1448    }
1449
1450    #[test]
1451    fn test_schema_multiple_errors() {
1452        let mut validator = SchemaValidator::new();
1453        validator.add_field(ConfigFieldSchema::new("a", ConfigFieldType::Integer).required());
1454        validator.add_field(ConfigFieldSchema::new("b", ConfigFieldType::Boolean).required());
1455        let config = HashMap::new();
1456        let errors = validator.validate(&config);
1457        assert_eq!(errors.len(), 2);
1458    }
1459
1460    #[test]
1461    fn test_schema_optional_field_missing_ok() {
1462        let mut validator = SchemaValidator::new();
1463        validator.add_field(ConfigFieldSchema::new("optional", ConfigFieldType::String));
1464        let config = HashMap::new();
1465        assert!(validator.validate(&config).is_empty());
1466    }
1467
1468    // ====================================================================
1469    // ConfigEncryption 测试
1470    // ====================================================================
1471
1472    #[test]
1473    fn test_encryption_roundtrip() {
1474        let enc = ConfigEncryption::new("my_secret_key");
1475        let plaintext = "database_password_123";
1476        let ciphertext = enc.encrypt(plaintext);
1477        assert!(ConfigEncryption::is_encrypted(&ciphertext));
1478        let decrypted = enc.decrypt(&ciphertext).unwrap();
1479        assert_eq!(decrypted, plaintext);
1480    }
1481
1482    #[test]
1483    fn test_encryption_is_encrypted_detection() {
1484        assert!(ConfigEncryption::is_encrypted("ENC(abc123)"));
1485        assert!(!ConfigEncryption::is_encrypted("plaintext"));
1486        assert!(!ConfigEncryption::is_encrypted("ENC(incomplete"));
1487    }
1488
1489    #[test]
1490    fn test_encryption_decrypt_if_needed_for_plaintext() {
1491        let enc = ConfigEncryption::new("key");
1492        let result = enc.decrypt_if_needed("plain_value").unwrap();
1493        assert_eq!(result, "plain_value");
1494    }
1495
1496    #[test]
1497    fn test_encryption_decrypt_if_needed_for_ciphertext() {
1498        let enc = ConfigEncryption::new("key");
1499        let ciphertext = enc.encrypt("secret");
1500        let result = enc.decrypt_if_needed(&ciphertext).unwrap();
1501        assert_eq!(result, "secret");
1502    }
1503
1504    #[test]
1505    fn test_encryption_decrypt_invalid_format_errors() {
1506        let enc = ConfigEncryption::new("key");
1507        assert!(enc.decrypt("not_encrypted").is_err());
1508        assert!(enc.decrypt("ENC(incomplete").is_err());
1509    }
1510
1511    #[test]
1512    fn test_encryption_decrypt_config_batch() {
1513        let enc = ConfigEncryption::new("master_key");
1514        let mut config = HashMap::new();
1515        config.insert("host".to_string(), "localhost".to_string());
1516        config.insert("password".to_string(), enc.encrypt("s3cr3t"));
1517        config.insert("api_key".to_string(), enc.encrypt("abc123"));
1518
1519        let decrypted = enc.decrypt_config(&config).unwrap();
1520        assert_eq!(decrypted.get("host"), Some(&"localhost".to_string()));
1521        assert_eq!(decrypted.get("password"), Some(&"s3cr3t".to_string()));
1522        assert_eq!(decrypted.get("api_key"), Some(&"abc123".to_string()));
1523    }
1524
1525    #[test]
1526    fn test_encryption_different_keys_produce_different_output() {
1527        let enc1 = ConfigEncryption::new("key1");
1528        let enc2 = ConfigEncryption::new("key2");
1529        let plaintext = "same_secret";
1530        let c1 = enc1.encrypt(plaintext);
1531        let c2 = enc2.encrypt(plaintext);
1532        assert_ne!(c1, c2);
1533    }
1534
1535    #[test]
1536    fn test_encryption_same_key_same_plaintext_deterministic() {
1537        let enc = ConfigEncryption::new("key");
1538        let c1 = enc.encrypt("secret");
1539        let c2 = enc.encrypt("secret");
1540        assert_eq!(c1, c2);
1541    }
1542
1543    #[test]
1544    fn test_base64_encode_decode_roundtrip() {
1545        let data = b"hello world";
1546        let encoded = base64_encode(data);
1547        let decoded = base64_decode(&encoded).unwrap();
1548        assert_eq!(decoded, data);
1549    }
1550
1551    #[test]
1552    fn test_base64_encode_known_value() {
1553        // "Man" -> "TWFu"
1554        assert_eq!(base64_encode(b"Man"), "TWFu");
1555        // "Ma" -> "TWE="
1556        assert_eq!(base64_encode(b"Ma"), "TWE=");
1557        // "M" -> "TQ=="
1558        assert_eq!(base64_encode(b"M"), "TQ==");
1559    }
1560
1561    #[test]
1562    fn test_base64_decode_invalid_char_errors() {
1563        assert!(base64_decode("!!!").is_err());
1564    }
1565
1566    #[test]
1567    fn test_base64_decode_empty_string() {
1568        let result = base64_decode("").unwrap();
1569        assert!(result.is_empty());
1570    }
1571}