Skip to main content

openlark_client/
lib.rs

1//! 🚀 OpenLark Client Library
2//!
3//! 现代化的飞书开放平台 Rust SDK,提供简洁、类型安全的 API 访问
4//! 集成 CoreError 企业级错误处理系统,提供全面的错误管理和恢复建议
5//!
6//! > 普通用户请优先使用根 crate `openlark`。
7//! >
8//! > `openlark-client` 保留为高级入口:适合只想复用统一客户端层,或明确需要直接控制客户端 feature 组合的场景。
9//!
10//! ## 核心特性
11//!
12//! - **🎯 Feature-driven**: 基于编译时功能标志的模块化设计
13//! - **⚡ 零配置**: 支持从环境变量自动配置客户端
14//! - **🔒 类型安全**: 完全编译时验证的 API 调用
15//! - **🚀 异步优先**: 完全异步的客户端实现
16//! - **🏗️ 现代构建器**: 流畅的构建器模式 API
17//! - **🔍 服务发现**: 动态服务注册和管理
18//! - **🛡️ 企业级**: 基于 CoreError 的高级错误处理、重试和监控支持
19//! - **🌐 中文优先**: 100% 中文错误消息和文档,专为中国开发者优化
20//!
21//! ## 快速开始
22//!
23//! ### 基础用法
24//!
25//! ```rust,no_run
26//! use openlark_client::prelude::*;
27//!
28//! #[tokio::main]
29//! async fn main() -> Result<()> {
30//!     // 从环境变量创建客户端(推荐)
31//!     let client = Client::from_env()?;
32//!
33//!     // 单入口:meta 链式字段访问(需要对应 feature)
34//!     // - 通讯:client.communication.im...
35//!     // - 文档:client.docs.ccm...
36//!     // - 认证:client.auth.app / client.auth.user / client.auth.oauth
37//!
38//!     Ok(())
39//! }
40//! ```
41//!
42//! ### 构建器模式
43//!
44//! ```rust,no_run
45//! use openlark_client::prelude::*;
46//! use std::time::Duration;
47//!
48//! fn main() -> Result<()> {
49//!     let _client = Client::builder()
50//!         .app_id("your_app_id")
51//!         .app_secret("your_app_secret")
52//!         .base_url("https://open.feishu.cn")
53//!         .timeout(Duration::from_secs(30))
54//!         .enable_log(true)
55//!         .build()?;
56//!     Ok(())
57//! }
58//! ```
59//!
60//! ### Endpoint 切换
61//!
62//! OpenLark 默认使用国内飞书 endpoint:`https://open.feishu.cn`。
63//! 如果你的应用运行在国际版 Lark,请将 `base_url` 切换为 `https://open.larksuite.com`。
64//!
65//! ```rust,no_run
66//! use openlark_client::prelude::*;
67//!
68//! fn main() -> Result<()> {
69//!     let _client = Client::builder()
70//!         .app_id("your_app_id")
71//!         .app_secret("your_app_secret")
72//!         .base_url("https://open.larksuite.com")
73//!         .build()?;
74//!     Ok(())
75//! }
76//! ```
77//!
78//! ### 环境变量配置
79//!
80//! 设置以下环境变量:
81//!
82//! ```bash
83//! export OPENLARK_APP_ID="your_app_id"
84//! export OPENLARK_APP_SECRET="your_app_secret"
85//! export OPENLARK_BASE_URL="https://open.feishu.cn"  # 可选,国际版请改为 https://open.larksuite.com
86//! export OPENLARK_TIMEOUT="30"  # 可选,秒
87//! export OPENLARK_ENABLE_LOG="true"  # 可选
88//! ```
89//!
90//! ## 功能标志
91//!
92//! 客户端使用 Rust 功能标志进行模块化编译:
93//!
94//! ```toml
95//! [dependencies]
96//! openlark-client = { version = "0.1", features = [
97//!     "communication",  # 通讯服务
98//!     "hr",           # 人力资源服务
99//!     "docs",         # 文档服务
100//!     "ai",           # AI 服务
101//!     "auth",         # 认证服务
102//!     "websocket",    # WebSocket 支持
103//! ]}
104//! ```
105//!
106//! ## 服务访问
107//!
108//! 每个启用功能都提供对应的 meta 链式入口(字段访问):
109//!
110//! ```rust,no_run
111//! use openlark_client::prelude::*;
112//!
113//! fn main() -> Result<()> {
114//! let client = Client::from_env()?;
115//!
116//! // 通讯入口(communication feature)
117//! #[cfg(feature = "communication")]
118//! let _comm = &client.communication;
119//!
120//! // 文档入口(docs feature)
121//! #[cfg(feature = "docs")]
122//! let _docs = &client.docs;
123//!
124//! // 认证入口(auth feature)
125//! #[cfg(feature = "auth")]
126//! let _auth = &client.auth;
127//! Ok(())
128//! }
129//! ```
130//!
131//! ## 高级用法
132//!
133//! ### 服务注册和管理
134//!
135//! ```rust,no_run
136//! use openlark_client::prelude::*;
137//!
138//! fn main() -> Result<()> {
139//! let client = Client::from_env()?;
140//! let registry = client.registry();
141//!
142//! // 检查可用服务
143//! println!("可用服务: {:?}", registry.list_services());
144//!
145//! // 检查特定服务是否可用
146//! if registry.has_service("communication") {
147//!     println!("通讯服务可用");
148//! }
149//! Ok(())
150//! }
151//! ```
152//!
153//! ### 自定义配置
154//!
155//! ```rust,no_run
156//! use openlark_client::prelude::*;
157//! use std::time::Duration;
158//!
159//! fn main() -> Result<()> {
160//!     let _client = Client::builder()
161//!         .app_id("app_id")
162//!         .app_secret("app_secret")
163//!         .base_url("https://open.feishu.cn")
164//!         .timeout(Duration::from_secs(60))
165//!         .retry_count(3)
166//!         .enable_log(true)
167//!         .build()?;
168//!     Ok(())
169//! }
170//! ```
171//!
172//! ## 错误处理
173//!
174//! 客户端基于 CoreError 提供企业级错误处理,包含详细的错误分析、恢复建议和中文友好的错误消息:
175//!
176//! ```rust,no_run
177//! use openlark_client::prelude::*;
178//!
179//! match Client::from_env() {
180//!     Ok(client) => {
181//!         println!("客户端创建成功");
182//!         // 使用客户端...
183//!     },
184//!     Err(error) => {
185//!         // 用户友好的错误消息(中文)
186//!         eprintln!("❌ {}", error.user_message().unwrap_or("未知错误"));
187//!
188//!         // 获取错误恢复建议
189//!         eprintln!("💡 建议: {}", error.suggestion());
190//!
191//!         // 获取详细的恢复步骤
192//!         for (i, step) in error.recovery_steps().iter().enumerate() {
193//!             eprintln!("{}. {}", i + 1, step);
194//!         }
195//!
196//!         // 获取完整的错误分析报告
197//!         eprintln!("\n{}", ErrorAnalyzer::new(&error).detailed_report());
198//!
199//!         // 根据错误类型进行特定处理
200//!         if error.is_validation_error() {
201//!             eprintln!("请检查配置参数是否正确");
202//!         } else if error.is_network_error() {
203//!             eprintln!("请检查网络连接并稍后重试");
204//!         } else if error.is_auth_error() {
205//!             eprintln!("请检查应用凭据是否有效");
206//!         }
207//!     }
208//! }
209//! ```
210//!
211//! ### 错误类型和处理
212//!
213//! ```rust,no_run
214//! use openlark_client::prelude::*;
215//!
216//! // 捕获和处理特定类型的错误
217//! async fn send_message_with_error_handling() -> Result<()> {
218//!     let client = Client::from_env()?;
219//!
220//!     // 单入口:meta 链式字段访问。这里演示“拿到入口 + 挂上错误上下文”的模式。
221//!     #[cfg(feature = "communication")]
222//!     let _comm = &client.communication;
223//!
224//!     // 具体 API 调用请使用 openlark-communication 的强类型请求/构建器并在 `.await` 处处理 Result。
225//!     Ok(())
226//! }
227//! ```
228
229//#![deny(missing_docs)]  // 暂时禁用以完成基本编译
230// async_fn_in_trait: 保留以兼容 MSRV 1.75(该 lint 在 Rust 1.80+ 才稳定)
231#![allow(async_fn_in_trait)]
232
233// 核心模块
234pub mod client;
235pub mod config;
236pub mod error;
237pub mod features;
238pub mod registry;
239pub mod traits;
240pub mod types;
241
242/// 延迟初始化工具模块
243///
244/// 提供 `LazyService` 包装器,用于延迟初始化服务实例。
245/// 这在客户端构造时不想立即初始化所有服务时很有用。
246pub mod lazy;
247
248#[cfg(test)]
249mod test_utils;
250
251// meta.Project 维度的 API 调用链(数据源:api_list_export.csv)
252// CardKit 由 openlark-cardkit 提供链式调用;openlark-client 仅负责挂载到 Client 上。
253
254// WebSocket 模块(条件编译)
255/// WebSocket 客户端模块
256///
257/// 提供与飞书WebSocket服务的实时连接功能,支持事件接收和状态管理。
258/// 此模块重新导出了openlark-core中的WebSocket实现。
259#[cfg(feature = "websocket")]
260pub mod ws_client;
261
262// ============================================================================
263// 核心类型重新导出
264// ============================================================================
265
266// 客户端和配置
267pub use client::{Client, ClientBuilder};
268pub use config::Config;
269
270// 企业级错误处理系统 - 基于 CoreError
271pub use error::{Error, Result};
272
273// 错误扩展功能
274pub use error::{
275    with_context,           // 上下文错误处理
276    with_operation_context, // 操作上下文错误处理
277    ClientErrorExt,         // 客户端错误扩展特征
278    ErrorAnalyzer,          // 错误分析器
279};
280
281// 错误创建便利函数
282pub use error::{
283    api_error,                 // API错误
284    authentication_error,      // 认证错误
285    business_error,            // 业务错误
286    configuration_error,       // 配置错误
287    internal_error,            // 内部错误
288    network_error,             // 网络错误
289    rate_limit_error,          // 限流错误
290    registry_error,            // 注册表错误
291    serialization_error,       // 序列化错误
292    service_unavailable_error, // 服务不可用错误
293    timeout_error,             // 超时错误
294    validation_error,          // 验证错误
295};
296
297// 功能管理和服务注册
298pub use features::FeatureLoader;
299pub use registry::{
300    DefaultServiceRegistry, ServiceEntry, ServiceMetadata, ServiceRegistry, ServiceStatus,
301};
302
303// 客户端特征
304pub use traits::{LarkClient, ServiceLifecycle};
305
306// 注意:legacy_client 已在 v0.15.0 中移除
307// 请使用 `Client` 与 `ClientBuilder`
308// 迁移指南:https://github.com/foxzool/openlark/blob/main/docs/migration-guide.md
309
310// CardKit meta 调用链
311#[cfg(feature = "cardkit")]
312pub use openlark_cardkit::CardkitClient;
313
314// Docs / Communication / Meeting meta 调用链入口(字段链式)
315#[cfg(feature = "auth")]
316pub use client::AuthClient;
317
318#[cfg(feature = "docs")]
319pub use openlark_docs::DocsClient;
320
321#[cfg(feature = "communication")]
322pub use openlark_communication::CommunicationClient;
323
324#[cfg(feature = "hr")]
325pub use openlark_hr::HrClient;
326
327#[cfg(feature = "meeting")]
328pub use openlark_meeting::MeetingClient;
329
330// 其他服务(当前未启用但已规划)
331//(历史上曾尝试在 openlark-client 内重复实现业务服务包装层,但现已收敛为 meta 单入口。)
332
333// ============================================================================
334// Core 系统类型重新导出
335// ============================================================================
336
337// 重新导出 openlark-core 核心类型
338pub use openlark_core::{config::Config as CoreConfig, SDKResult as CoreResult};
339
340// 错误系统核心类型
341pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
342
343// ============================================================================
344// 类型别名和便利定义
345// ============================================================================
346
347/// 🚨 SDK 结果类型别名(与 Core 系统兼容)
348pub type SDKResult<T> = openlark_core::SDKResult<T>;
349
350/// 🚀 预导出模块 - 包含最常用的类型和特征
351///
352/// 使用预导出可以简化导入,提供一站式类型访问:
353///
354/// ```rust,no_run
355/// use openlark_client::prelude::*;
356///
357/// fn main() -> Result<()> {
358///     let client = Client::from_env()?;
359///     #[cfg(feature = "docs")]
360///     let _docs = &client.docs;
361///     Ok(())
362/// }
363/// ```
364pub mod prelude {
365    // ============================================================================
366    // 核心客户端类型
367    // ============================================================================
368
369    // 客户端和配置
370    pub use crate::{Client, ClientBuilder, Config};
371
372    // 企业级错误处理系统
373    pub use crate::{Error, Result};
374
375    // ============================================================================
376    // 错误处理扩展
377    // ============================================================================
378
379    // 错误扩展特征和分析器
380    pub use crate::{
381        with_context,           // 上下文错误处理
382        with_operation_context, // 操作上下文错误处理
383        ClientErrorExt,         // 客户端错误扩展特征
384        ErrorAnalyzer,          // 错误分析器
385    };
386
387    // 错误创建便利函数
388    pub use crate::{
389        api_error,                 // API错误
390        authentication_error,      // 认证错误
391        business_error,            // 业务错误
392        configuration_error,       // 配置错误
393        internal_error,            // 内部错误
394        network_error,             // 网络错误
395        rate_limit_error,          // 限流错误
396        registry_error,            // 注册表错误
397        serialization_error,       // 序列化错误
398        service_unavailable_error, // 服务不可用错误
399        timeout_error,             // 超时错误
400        validation_error,          // 验证错误
401    };
402
403    // Core 错误系统类型
404    pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
405
406    // ============================================================================
407    // 客户端特征
408    // ============================================================================
409
410    // 服务特征
411    pub use crate::traits::{LarkClient, ServiceLifecycle, ServiceTrait};
412
413    // 服务注册
414    pub use crate::ServiceRegistry;
415
416    // ============================================================================
417    // 功能管理
418    // ============================================================================
419
420    pub use crate::FeatureLoader;
421
422    // meta 风格链式入口(字段链式)
423    #[cfg(feature = "cardkit")]
424    pub use openlark_cardkit::CardkitClient;
425
426    #[cfg(feature = "auth")]
427    pub use crate::AuthClient;
428
429    #[cfg(feature = "docs")]
430    pub use openlark_docs::DocsClient;
431
432    #[cfg(feature = "communication")]
433    pub use openlark_communication::CommunicationClient;
434
435    #[cfg(feature = "hr")]
436    pub use openlark_hr::HrClient;
437
438    #[cfg(feature = "meeting")]
439    pub use openlark_meeting::MeetingClient;
440
441    // 其他服务(当前未启用但已规划)
442    //(历史上曾尝试在 openlark-client 内重复实现业务服务包装层,但现已收敛为 meta 单入口。)
443
444    // ============================================================================
445    // 便利类型别名
446    // ============================================================================
447
448    /// 🚨 SDK 结果类型别名(与 Core 系统兼容)
449    pub type SDKResult<T> = openlark_core::SDKResult<T>;
450
451    // ============================================================================
452    // 常用宏和便利导入
453    // ============================================================================
454
455    pub use openlark_core::{config::Config as CoreConfig, SDKResult as CoreResult};
456}
457
458/// 🏷️ 库信息
459pub mod info {
460    /// 库名称
461    pub const NAME: &str = "OpenLark Client";
462    /// 库版本
463    pub const VERSION: &str = env!("CARGO_PKG_VERSION");
464    /// 库描述
465    pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
466    /// 仓库地址
467    pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
468}
469
470/// 🔧 实用工具函数
471pub mod utils;
472
473#[cfg(test)]
474#[allow(unused_imports)]
475mod tests {
476    use super::*;
477
478    #[test]
479    fn test_library_info() {
480        assert!(!info::NAME.is_empty());
481        assert!(!info::VERSION.is_empty());
482        assert!(!info::DESCRIPTION.is_empty());
483    }
484
485    #[test]
486    fn test_enabled_features() {
487        let features = utils::get_enabled_features();
488        // auth 功能始终启用
489        assert!(features.contains(&"auth"));
490    }
491
492    #[test]
493    fn test_prelude_reexports() {
494        // 确保 prelude 模块正确导出了核心类型
495        use prelude::*;
496
497        // 这些导入应该能够工作
498        let _builder: ClientBuilder = ClientBuilder::new();
499
500        // 测试配置创建
501        let _config = Config::builder().app_id("test").app_secret("test").build();
502    }
503
504    #[test]
505    fn test_check_env_config_success() {
506        test_utils::with_env_vars(
507            &[
508                ("OPENLARK_APP_ID", Some("test_app_id")),
509                ("OPENLARK_APP_SECRET", Some("test_secret")),
510            ],
511            || {
512                let result = utils::check_env_config();
513                assert!(result.is_ok());
514            },
515        );
516    }
517
518    #[test]
519    fn test_check_env_config_missing_app_id() {
520        test_utils::with_env_vars(
521            &[
522                ("OPENLARK_APP_ID", None),
523                ("OPENLARK_APP_SECRET", Some("test_secret")),
524            ],
525            || {
526                let result = utils::check_env_config();
527                assert!(result.is_err());
528            },
529        );
530    }
531
532    #[test]
533    fn test_check_env_config_empty_app_id() {
534        test_utils::with_env_vars(
535            &[
536                ("OPENLARK_APP_ID", Some("")),
537                ("OPENLARK_APP_SECRET", Some("test_secret")),
538            ],
539            || {
540                let result = utils::check_env_config();
541                assert!(result.is_err());
542            },
543        );
544    }
545
546    #[test]
547    fn test_check_env_config_missing_app_secret() {
548        test_utils::with_env_vars(
549            &[
550                ("OPENLARK_APP_ID", Some("test_app_id")),
551                ("OPENLARK_APP_SECRET", None),
552            ],
553            || {
554                let result = utils::check_env_config();
555                assert!(result.is_err());
556            },
557        );
558    }
559
560    #[test]
561    fn test_check_env_config_empty_app_secret() {
562        test_utils::with_env_vars(
563            &[
564                ("OPENLARK_APP_ID", Some("test_app_id")),
565                ("OPENLARK_APP_SECRET", Some("")),
566            ],
567            || {
568                let result = utils::check_env_config();
569                assert!(result.is_err());
570            },
571        );
572    }
573
574    #[test]
575    fn test_check_env_config_invalid_base_url() {
576        test_utils::with_env_vars(
577            &[
578                ("OPENLARK_APP_ID", Some("test_app_id")),
579                ("OPENLARK_APP_SECRET", Some("test_secret")),
580                ("OPENLARK_BASE_URL", Some("invalid_url")),
581            ],
582            || {
583                let result = utils::check_env_config();
584                assert!(result.is_err());
585            },
586        );
587    }
588
589    #[test]
590    fn test_check_env_config_valid_base_url() {
591        test_utils::with_env_vars(
592            &[
593                ("OPENLARK_APP_ID", Some("test_app_id")),
594                ("OPENLARK_APP_SECRET", Some("test_secret")),
595                ("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
596            ],
597            || {
598                let result = utils::check_env_config();
599                assert!(result.is_ok());
600            },
601        );
602    }
603
604    #[test]
605    fn test_check_env_config_invalid_timeout() {
606        test_utils::with_env_vars(
607            &[
608                ("OPENLARK_APP_ID", Some("test_app_id")),
609                ("OPENLARK_APP_SECRET", Some("test_secret")),
610                ("OPENLARK_TIMEOUT", Some("not_a_number")),
611            ],
612            || {
613                let result = utils::check_env_config();
614                assert!(result.is_err());
615            },
616        );
617    }
618
619    #[test]
620    fn test_check_env_config_valid_timeout() {
621        test_utils::with_env_vars(
622            &[
623                ("OPENLARK_APP_ID", Some("test_app_id")),
624                ("OPENLARK_APP_SECRET", Some("test_secret")),
625                ("OPENLARK_TIMEOUT", Some("30")),
626            ],
627            || {
628                let result = utils::check_env_config();
629                assert!(result.is_ok());
630            },
631        );
632    }
633
634    #[test]
635    fn test_create_config_from_env_success() {
636        test_utils::with_env_vars(
637            &[
638                ("OPENLARK_APP_ID", Some("test_app_id")),
639                ("OPENLARK_APP_SECRET", Some("test_secret")),
640                ("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
641            ],
642            || {
643                let result = utils::create_config_from_env();
644                assert!(result.is_ok());
645                let config = result.unwrap();
646                assert_eq!(config.app_id, "test_app_id");
647                assert_eq!(config.app_secret, "test_secret");
648            },
649        );
650    }
651
652    #[test]
653    fn test_create_config_from_env_missing_vars() {
654        test_utils::with_env_vars(
655            &[("OPENLARK_APP_ID", None), ("OPENLARK_APP_SECRET", None)],
656            || {
657                let result = utils::create_config_from_env();
658                assert!(result.is_err());
659            },
660        );
661    }
662
663    #[test]
664    fn test_get_config_summary() {
665        let config = Config::builder()
666            .app_id("test_app_id")
667            .app_secret("test_secret_key")
668            .base_url("https://open.feishu.cn")
669            .timeout(std::time::Duration::from_secs(30))
670            .build()
671            .unwrap();
672
673        let summary = utils::get_config_summary(&config);
674        assert_eq!(summary.app_id, "test_app_id");
675        assert!(summary.app_secret_set);
676        assert_eq!(summary.base_url, "https://open.feishu.cn");
677        assert!(summary.timeout > std::time::Duration::ZERO);
678    }
679
680    #[test]
681    fn test_config_summary_friendly_description() {
682        let summary = config::ConfigSummary {
683            app_id: "test_app".to_string(),
684            app_secret_set: true,
685            app_type: openlark_core::constants::AppType::SelfBuild,
686            enable_token_cache: true,
687            base_url: "https://open.feishu.cn".to_string(),
688            timeout: std::time::Duration::from_secs(30),
689            retry_count: 3,
690            enable_log: false,
691            header_count: 0,
692        };
693
694        let description = summary.friendly_description();
695        assert!(description.contains("test_app"));
696        assert!(description.contains("open.feishu.cn"));
697        assert!(description.contains("30s"));
698    }
699
700    #[test]
701    fn test_config_summary_friendly_description_no_timeout() {
702        let summary = config::ConfigSummary {
703            app_id: "test_app".to_string(),
704            app_secret_set: true,
705            app_type: openlark_core::constants::AppType::SelfBuild,
706            enable_token_cache: true,
707            base_url: "https://open.feishu.cn".to_string(),
708            timeout: std::time::Duration::ZERO,
709            retry_count: 3,
710            enable_log: false,
711            header_count: 0,
712        };
713
714        let description = summary.friendly_description();
715        assert!(description.contains("test_app"));
716        assert!(description.contains("0ns"));
717    }
718
719    #[test]
720    fn test_validate_feature_dependencies_success() {
721        // auth 始终启用,应该没有依赖问题
722        let result = utils::validate_feature_dependencies();
723        assert!(result.is_ok());
724    }
725
726    #[test]
727    fn test_diagnose_system_success() {
728        test_utils::with_env_vars(
729            &[
730                ("OPENLARK_APP_ID", Some("test_app_id")),
731                ("OPENLARK_APP_SECRET", Some("test_secret")),
732            ],
733            || {
734                let diagnostics = utils::diagnose_system();
735                assert!(
736                    diagnostics.env_config_status.contains("✅")
737                        || diagnostics.env_config_status.contains("❌")
738                );
739                assert!(diagnostics.feature_deps_status.contains("✅"));
740                assert!(!diagnostics.enabled_features.is_empty());
741            },
742        );
743    }
744
745    #[test]
746    fn test_system_diagnostics_new() {
747        let diagnostics = utils::SystemDiagnostics::new();
748        assert_eq!(diagnostics.env_config_status, "未检查");
749        assert_eq!(diagnostics.feature_deps_status, "未检查");
750        assert!(diagnostics.enabled_features.is_empty());
751        assert!(diagnostics.issues.is_empty());
752    }
753
754    #[test]
755    fn test_system_diagnostics_add_issue() {
756        let mut diagnostics = utils::SystemDiagnostics::new();
757        diagnostics.add_issue("测试类别", "测试描述");
758        assert_eq!(diagnostics.issues.len(), 1);
759        assert_eq!(diagnostics.issues[0].category, "测试类别");
760        assert_eq!(diagnostics.issues[0].description, "测试描述");
761    }
762
763    #[test]
764    fn test_system_diagnostics_health_summary_healthy() {
765        let diagnostics = utils::SystemDiagnostics::new();
766        let summary = diagnostics.health_summary();
767        assert!(summary.contains("🟢"));
768        assert!(summary.contains("健康"));
769    }
770
771    #[test]
772    fn test_system_diagnostics_health_summary_with_issues() {
773        let mut diagnostics = utils::SystemDiagnostics::new();
774        diagnostics.add_issue("测试类别", "测试描述");
775        let summary = diagnostics.health_summary();
776        assert!(summary.contains("🟡"));
777        assert!(summary.contains("1"));
778    }
779
780    #[test]
781    fn test_system_diagnostics_has_critical_issues_true() {
782        let mut diagnostics = utils::SystemDiagnostics::new();
783        diagnostics.add_issue("环境变量", "配置错误");
784        assert!(diagnostics.has_critical_issues());
785    }
786
787    #[test]
788    fn test_system_diagnostics_has_critical_issues_false() {
789        let mut diagnostics = utils::SystemDiagnostics::new();
790        diagnostics.add_issue("其他问题", "一般错误");
791        assert!(!diagnostics.has_critical_issues());
792    }
793
794    #[test]
795    fn test_system_diagnostics_default() {
796        let diagnostics: utils::SystemDiagnostics = Default::default();
797        assert_eq!(diagnostics.env_config_status, "未检查");
798    }
799
800    #[test]
801    fn test_diagnostic_issue_clone() {
802        let issue = utils::DiagnosticIssue {
803            category: "测试".to_string(),
804            description: "描述".to_string(),
805        };
806        let cloned = issue.clone();
807        assert_eq!(cloned.category, "测试");
808        assert_eq!(cloned.description, "描述");
809    }
810}