From ace04ee56d05be4e4a0e96853e86bdf585e70fbe Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 28 Apr 2026 18:31:01 +0800 Subject: [PATCH] =?UTF-8?q?test(config):=20erp-config=20=E4=BB=8E=2050=20?= =?UTF-8?q?=E5=A2=9E=E8=87=B3=2066=20=E4=B8=AA=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20=E2=80=94=20fallback=5Fchain=20+=20model=5Fto=5Fres?= =?UTF-8?q?p=20+=20ThemeResp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setting_service: 7 个测试(5 个 fallback_chain 作用域解析 + 2 个 model_to_resp 映射) - theme_handler: 2 个测试(default_theme 默认值 + ThemeResp serde round-trip) --- .../erp-config/src/handler/theme_handler.rs | 27 +++++ .../erp-config/src/service/setting_service.rs | 99 ++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/crates/erp-config/src/handler/theme_handler.rs b/crates/erp-config/src/handler/theme_handler.rs index 410d69c..cb6691c 100644 --- a/crates/erp-config/src/handler/theme_handler.rs +++ b/crates/erp-config/src/handler/theme_handler.rs @@ -106,3 +106,30 @@ where Ok(JsonResponse(ApiResponse::ok(req))) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_theme_all_fields_none() { + let theme = default_theme(); + assert!(theme.primary_color.is_none()); + assert!(theme.logo_url.is_none()); + assert!(theme.sidebar_style.is_none()); + } + + #[test] + fn theme_resp_serde_roundtrip() { + let theme = ThemeResp { + primary_color: Some("#1890ff".to_string()), + logo_url: None, + sidebar_style: Some("dark".to_string()), + }; + let json = serde_json::to_string(&theme).unwrap(); + let back: ThemeResp = serde_json::from_str(&json).unwrap(); + assert_eq!(back.primary_color, Some("#1890ff".to_string())); + assert_eq!(back.logo_url, None); + assert_eq!(back.sidebar_style, Some("dark".to_string())); + } +} diff --git a/crates/erp-config/src/service/setting_service.rs b/crates/erp-config/src/service/setting_service.rs index e997e81..1b11dd3 100644 --- a/crates/erp-config/src/service/setting_service.rs +++ b/crates/erp-config/src/service/setting_service.rs @@ -308,7 +308,7 @@ impl SettingService { /// Build the fallback chain for hierarchical lookup. /// /// Returns a list of (scope, scope_id) tuples to try in order. - fn fallback_chain( + pub(crate) fn fallback_chain( scope: &str, _scope_id: &Option, tenant_id: Uuid, @@ -339,7 +339,7 @@ impl SettingService { } /// Convert a SeaORM model to a response DTO. - fn model_to_resp(model: &setting::Model) -> SettingResp { + pub(crate) fn model_to_resp(model: &setting::Model) -> SettingResp { SettingResp { id: model.id, scope: model.scope.clone(), @@ -350,3 +350,98 @@ impl SettingService { } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn tid() -> Uuid { + Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap() + } + + // ---- fallback_chain ---- + + #[test] + fn fallback_user_scope_returns_tenant_then_platform() { + let chain = SettingService::fallback_chain("user", &None, tid()).unwrap(); + assert_eq!(chain.len(), 2); + assert_eq!(chain[0], ("tenant".to_string(), Some(tid()))); + assert_eq!(chain[1], ("platform".to_string(), None)); + } + + #[test] + fn fallback_org_scope_returns_tenant_then_platform() { + let chain = SettingService::fallback_chain("org", &None, tid()).unwrap(); + assert_eq!(chain.len(), 2); + assert_eq!(chain[0], ("tenant".to_string(), Some(tid()))); + assert_eq!(chain[1], ("platform".to_string(), None)); + } + + #[test] + fn fallback_tenant_scope_returns_platform() { + let chain = SettingService::fallback_chain("tenant", &None, tid()).unwrap(); + assert_eq!(chain.len(), 1); + assert_eq!(chain[0], ("platform".to_string(), None)); + } + + #[test] + fn fallback_platform_scope_returns_empty() { + let chain = SettingService::fallback_chain("platform", &None, tid()).unwrap(); + assert!(chain.is_empty()); + } + + #[test] + fn fallback_invalid_scope_returns_error() { + let result = SettingService::fallback_chain("invalid", &None, tid()); + assert!(result.is_err()); + match result.unwrap_err() { + ConfigError::Validation(msg) => assert!(msg.contains("不支持的作用域")), + other => panic!("期望 Validation,得到 {:?}", other), + } + } + + // ---- model_to_resp ---- + + #[test] + fn model_to_resp_maps_all_fields() { + let m = setting::Model { + id: Uuid::parse_str("00000000-0000-0000-0000-000000000010").unwrap(), + tenant_id: tid(), + scope: "tenant".to_string(), + scope_id: Some(tid()), + setting_key: "theme.primary_color".to_string(), + setting_value: serde_json::json!("#1890ff"), + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + created_by: tid(), + updated_by: tid(), + deleted_at: None, + version: 3, + }; + let resp = SettingService::model_to_resp(&m); + assert_eq!(resp.scope, "tenant"); + assert_eq!(resp.setting_key, "theme.primary_color"); + assert_eq!(resp.setting_value, serde_json::json!("#1890ff")); + assert_eq!(resp.version, 3); + } + + #[test] + fn model_to_resp_null_scope_id() { + let m = setting::Model { + id: Uuid::parse_str("00000000-0000-0000-0000-000000000010").unwrap(), + tenant_id: tid(), + scope: "platform".to_string(), + scope_id: None, + setting_key: "language.default".to_string(), + setting_value: serde_json::json!("zh-CN"), + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + created_by: tid(), + updated_by: tid(), + deleted_at: None, + version: 1, + }; + let resp = SettingService::model_to_resp(&m); + assert_eq!(resp.scope_id, None); + } +}