open_lark/client/
mod.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::core::{
5    config::{Config, ConfigBuilder},
6    constants::AppType,
7};
8
9// 条件导入服务
10#[cfg(feature = "acs")]
11use crate::service::acs::AcsService;
12#[cfg(feature = "admin")]
13use crate::service::admin::AdminService;
14#[cfg(feature = "ai")]
15use crate::service::ai::AiService;
16#[cfg(feature = "aily")]
17use crate::service::aily::AilyService;
18#[cfg(feature = "apass")]
19use crate::service::apass::ApassService;
20#[cfg(feature = "application")]
21use crate::service::application::ApplicationService;
22#[cfg(feature = "approval")]
23use crate::service::approval::ApprovalService;
24#[cfg(feature = "attendance")]
25use crate::service::attendance::AttendanceService;
26#[cfg(feature = "authentication")]
27use crate::service::authentication::AuthenService;
28#[cfg(feature = "bot")]
29use crate::service::bot::BotService;
30#[cfg(feature = "calendar")]
31use crate::service::calendar::CalendarService;
32#[cfg(feature = "cardkit")]
33use crate::service::cardkit::CardkitService;
34#[cfg(feature = "cloud-docs")]
35use crate::service::cloud_docs::CloudDocsService;
36#[cfg(feature = "contact")]
37use crate::service::contact::ContactService;
38#[cfg(feature = "corehr")]
39use crate::service::corehr::CoreHRService;
40#[cfg(feature = "directory")]
41use crate::service::directory::DirectoryService;
42#[cfg(feature = "ehr")]
43use crate::service::ehr::EhrService;
44#[cfg(feature = "elearning")]
45use crate::service::elearning::ELearningService;
46#[cfg(feature = "group")]
47use crate::service::group::GroupService;
48#[cfg(feature = "helpdesk")]
49use crate::service::helpdesk::HelpdeskService;
50#[cfg(feature = "hire")]
51use crate::service::hire::HireService;
52#[cfg(feature = "human-authentication")]
53use crate::service::human_authentication::HumanAuthenticationService;
54#[cfg(feature = "im")]
55use crate::service::im::ImService;
56#[cfg(feature = "lingo")]
57use crate::service::lingo::LingoService;
58#[cfg(feature = "mail")]
59use crate::service::mail::MailService;
60#[cfg(feature = "mdm")]
61use crate::service::mdm::MdmService;
62#[cfg(feature = "minutes")]
63use crate::service::minutes::MinutesService;
64#[cfg(feature = "moments")]
65use crate::service::moments::MomentsService;
66#[cfg(feature = "okr")]
67use crate::service::okr::OkrService;
68#[cfg(feature = "payroll")]
69use crate::service::payroll::PayrollService;
70#[cfg(feature = "performance")]
71use crate::service::performance::PerformanceService;
72#[cfg(feature = "personal-settings")]
73use crate::service::personal_settings::PersonalSettingsService;
74#[cfg(feature = "report")]
75use crate::service::report::ReportService;
76#[cfg(feature = "search")]
77use crate::service::search::SearchService;
78#[cfg(feature = "security-and-compliance")]
79use crate::service::security_and_compliance::SecurityAndComplianceService;
80#[cfg(feature = "task")]
81use crate::service::task::TaskV2Service;
82#[cfg(feature = "tenant")]
83use crate::service::tenant::TenantService;
84#[cfg(feature = "tenant-tag")]
85use crate::service::tenant_tag::TenantTagService;
86#[cfg(feature = "trust-party")]
87use crate::service::trust_party::TrustPartyService;
88#[cfg(feature = "vc")]
89use crate::service::vc::VcService;
90#[cfg(feature = "verification")]
91use crate::service::verification::VerificationService;
92#[cfg(feature = "workplace")]
93use crate::service::workplace::WorkplaceService;
94
95// 向后兼容的导入
96#[cfg(feature = "cloud-docs")]
97use crate::service::{
98    AssistantService, BitableService, BoardService, CommentsService, DocsService, DriveService,
99    PermissionService, SheetsService, WikiService,
100};
101
102#[cfg(feature = "websocket")]
103pub mod ws_client;
104
105/// 飞书开放平台SDK主客户端
106///
107/// 提供对所有飞书开放平台API的统一访问接口。支持自建应用和商店应用两种类型,
108/// 自动处理认证、令牌管理、请求重试等核心功能。
109///
110/// # 主要功能
111///
112/// - 🔐 自动令牌管理和刷新
113/// - 🚀 支持所有飞书开放平台API
114/// - 🔄 内置请求重试机制
115/// - 📡 WebSocket长连接支持(需开启websocket特性)
116/// - 🎯 类型安全的API调用
117///
118/// # 快速开始
119///
120/// ```rust
121/// use open_lark::prelude::*;
122///
123/// // 创建自建应用客户端
124/// let client = LarkClient::builder("your_app_id", "your_app_secret")
125///     .with_app_type(AppType::SelfBuild)
126///     .with_enable_token_cache(true)
127///     .build();
128///
129/// // 发送文本消息
130/// let message = CreateMessageRequestBody::builder()
131///     .receive_id("ou_xxx")
132///     .msg_type("text")
133///     .content("{\"text\":\"Hello from Rust!\"}")
134///     .build();
135///
136/// let request = CreateMessageRequest::builder()
137///     .receive_id_type("open_id")
138///     .request_body(message)
139///     .build();
140///
141/// // let result = client.im.message.create(request, None).await?;
142/// ```
143///
144/// # 服务模块
145///
146/// 客户端包含以下主要服务模块:
147/// - `im`: 即时消息
148/// - `drive`: 云盘文件
149/// - `sheets`: 电子表格
150/// - `calendar`: 日历
151/// - `contact`: 通讯录
152/// - `hire`: 招聘
153/// - 更多服务请参考各字段文档
154pub struct LarkClient {
155    pub config: Config,
156    /// 共享配置(实验性):单一 `Arc<Config>`,用于内部服务扇出以减少 clone
157    #[allow(dead_code)] // Used in constructor and tests
158    shared_config: Arc<Config>,
159    // 核心服务 - 使用条件编译
160    #[cfg(feature = "acs")]
161    pub acs: AcsService,
162    #[cfg(feature = "admin")]
163    pub admin: AdminService,
164    #[cfg(feature = "ai")]
165    pub ai: AiService,
166    #[cfg(feature = "aily")]
167    pub aily: AilyService,
168    #[cfg(feature = "apass")]
169    pub apass: ApassService,
170    #[cfg(feature = "application")]
171    pub application: ApplicationService,
172    #[cfg(feature = "approval")]
173    pub approval: ApprovalService,
174    #[cfg(feature = "attendance")]
175    pub attendance: AttendanceService,
176    #[cfg(feature = "authentication")]
177    pub auth: AuthenService,
178    #[cfg(feature = "bot")]
179    pub bot: BotService,
180    #[cfg(feature = "calendar")]
181    pub calendar: CalendarService,
182    #[cfg(feature = "cardkit")]
183    pub cardkit: CardkitService,
184    #[cfg(feature = "contact")]
185    pub contact: ContactService,
186    #[cfg(feature = "corehr")]
187    pub corehr: CoreHRService,
188    #[cfg(feature = "directory")]
189    pub directory: DirectoryService,
190    #[cfg(feature = "ehr")]
191    pub ehr: EhrService,
192    #[cfg(feature = "elearning")]
193    pub elearning: ELearningService,
194    #[cfg(feature = "group")]
195    pub group: GroupService,
196    #[cfg(feature = "helpdesk")]
197    pub helpdesk: HelpdeskService,
198    #[cfg(feature = "hire")]
199    pub hire: HireService,
200    #[cfg(feature = "human-authentication")]
201    pub human_authentication: HumanAuthenticationService,
202    #[cfg(feature = "im")]
203    pub im: ImService,
204    #[cfg(feature = "lingo")]
205    pub lingo: LingoService,
206    #[cfg(feature = "mail")]
207    pub mail: MailService,
208    #[cfg(feature = "mdm")]
209    pub mdm: MdmService,
210    #[cfg(feature = "minutes")]
211    pub minutes: MinutesService,
212    #[cfg(feature = "moments")]
213    pub moments: MomentsService,
214    #[cfg(feature = "okr")]
215    pub okr: OkrService,
216    #[cfg(feature = "payroll")]
217    pub payroll: PayrollService,
218    #[cfg(feature = "performance")]
219    pub performance: PerformanceService,
220    #[cfg(feature = "personal-settings")]
221    pub personal_settings: PersonalSettingsService,
222    #[cfg(feature = "report")]
223    pub report: ReportService,
224    #[cfg(feature = "search")]
225    pub search: SearchService,
226    #[cfg(feature = "security-and-compliance")]
227    pub security_and_compliance: SecurityAndComplianceService,
228    #[cfg(feature = "task")]
229    pub task: TaskV2Service,
230    #[cfg(feature = "tenant")]
231    pub tenant: TenantService,
232    #[cfg(feature = "tenant-tag")]
233    pub tenant_tag: TenantTagService,
234    #[cfg(feature = "trust-party")]
235    pub trust_party: TrustPartyService,
236    #[cfg(feature = "vc")]
237    pub vc: VcService,
238    #[cfg(feature = "verification")]
239    pub verification: VerificationService,
240    #[cfg(feature = "workplace")]
241    pub workplace: WorkplaceService,
242    // 云文档服务聚合
243    #[cfg(feature = "cloud-docs")]
244    pub cloud_docs: CloudDocsService,
245    // 向后兼容的字段
246    #[cfg(feature = "cloud-docs")]
247    pub assistant: AssistantService,
248    #[cfg(feature = "cloud-docs")]
249    pub docs: DocsService,
250    #[cfg(feature = "cloud-docs")]
251    pub drive: DriveService,
252    #[cfg(feature = "cloud-docs")]
253    pub sheets: SheetsService,
254    #[cfg(feature = "cloud-docs")]
255    pub bitable: BitableService,
256    #[cfg(feature = "cloud-docs")]
257    pub wiki: WikiService,
258    #[cfg(feature = "cloud-docs")]
259    pub comments: CommentsService,
260    #[cfg(feature = "cloud-docs")]
261    pub permission: PermissionService,
262    #[cfg(feature = "cloud-docs")]
263    pub board: BoardService,
264}
265
266/// 飞书客户端构建器
267///
268/// 使用构建器模式配置和创建LarkClient实例。支持链式调用配置各种选项。
269///
270/// # 示例
271///
272/// ```rust
273/// use open_lark::prelude::*;
274///
275/// let client = LarkClient::builder("app_id", "app_secret")
276///     .with_app_type(AppType::SelfBuild)
277///     .with_enable_token_cache(true)
278///     .with_req_timeout(Some(30.0))
279///     .build();
280/// ```
281pub struct LarkClientBuilder {
282    config_builder: ConfigBuilder,
283}
284
285impl LarkClientBuilder {
286    /// 获取当前配置构建器(仅测试使用)
287    #[cfg(test)]
288    fn build_config(&self) -> Config {
289        self.config_builder.clone().build()
290    }
291
292    /// 设置应用类型
293    ///
294    /// # 参数
295    /// - `app_type`: 应用类型,`AppType::SelfBuild`(自建应用)或`AppType::Marketplace`(商店应用)
296    pub fn with_app_type(mut self, app_type: AppType) -> Self {
297        self.config_builder = self.config_builder.app_type(app_type);
298        self
299    }
300
301    /// 设置为商店应用(等同于 `with_app_type(AppType::Marketplace)`)
302    pub fn with_marketplace_app(mut self) -> Self {
303        self.config_builder = self.config_builder.app_type(AppType::Marketplace);
304        self
305    }
306
307    /// 设置自定义API基础URL
308    ///
309    /// # 参数
310    /// - `base_url`: 自定义的API基础URL,默认为官方地址
311    pub fn with_open_base_url(mut self, base_url: String) -> Self {
312        self.config_builder = self.config_builder.base_url(base_url);
313        self
314    }
315
316    /// 启用或禁用令牌缓存
317    ///
318    /// # 参数
319    /// - `enable`: 是否启用令牌缓存,建议启用以提高性能
320    pub fn with_enable_token_cache(mut self, enable: bool) -> Self {
321        self.config_builder = self.config_builder.enable_token_cache(enable);
322        self
323    }
324
325    /// 设置请求超时时间
326    ///
327    /// # 参数
328    /// - `timeout`: 超时时间(秒),None表示使用默认值
329    pub fn with_req_timeout(mut self, timeout: Option<f32>) -> Self {
330        if let Some(timeout) = timeout {
331            self.config_builder = self
332                .config_builder
333                .req_timeout(Duration::from_secs_f32(timeout));
334        }
335        self
336    }
337
338    /// 构建LarkClient实例
339    ///
340    /// 根据配置的参数创建最终的客户端实例。
341    pub fn build(self) -> LarkClient {
342        let config = self.config_builder.build();
343        let shared_config = Arc::new(config.clone());
344        LarkClient {
345            config: config.clone(),
346            shared_config: shared_config.clone(),
347            // 核心服务 - 使用条件编译
348            #[cfg(feature = "acs")]
349            acs: AcsService::new(config.clone()),
350            #[cfg(feature = "admin")]
351            admin: AdminService::new(config.clone()),
352            #[cfg(feature = "ai")]
353            ai: AiService::new(config.clone()),
354            #[cfg(feature = "aily")]
355            aily: AilyService::new(config.clone()),
356            #[cfg(feature = "apass")]
357            apass: ApassService::new(config.clone()),
358            #[cfg(feature = "application")]
359            application: ApplicationService::new_from_shared(shared_config.clone()),
360            #[cfg(feature = "approval")]
361            approval: ApprovalService::new(config.clone()),
362            #[cfg(feature = "attendance")]
363            attendance: AttendanceService::new(config.clone()),
364            #[cfg(feature = "authentication")]
365            auth: AuthenService::new(config.clone()),
366            #[cfg(feature = "bot")]
367            bot: BotService::new(config.clone()),
368            #[cfg(feature = "calendar")]
369            calendar: CalendarService::new_from_shared(shared_config.clone()),
370            #[cfg(feature = "cardkit")]
371            cardkit: CardkitService::new(config.clone()),
372            #[cfg(feature = "contact")]
373            contact: ContactService::new_from_shared(shared_config.clone()),
374            #[cfg(feature = "corehr")]
375            corehr: CoreHRService::new(config.clone()),
376            #[cfg(feature = "directory")]
377            directory: DirectoryService::new_from_shared(shared_config.clone()),
378            #[cfg(feature = "ehr")]
379            ehr: EhrService::new_from_shared(shared_config.clone()),
380            #[cfg(feature = "elearning")]
381            elearning: ELearningService::new(config.clone()),
382            #[cfg(feature = "group")]
383            group: GroupService::new(config.clone()),
384            #[cfg(feature = "helpdesk")]
385            helpdesk: HelpdeskService::new(config.clone()),
386            #[cfg(feature = "hire")]
387            hire: HireService::new(config.clone()),
388            #[cfg(feature = "human-authentication")]
389            human_authentication: HumanAuthenticationService::new(config.clone()),
390            #[cfg(feature = "im")]
391            im: ImService::new_from_shared(shared_config.clone()),
392            #[cfg(feature = "lingo")]
393            lingo: LingoService::new(config.clone()),
394            #[cfg(feature = "mail")]
395            mail: MailService::new(config.clone()),
396            #[cfg(feature = "mdm")]
397            mdm: MdmService::new(config.clone()),
398            #[cfg(feature = "minutes")]
399            minutes: MinutesService::new(config.clone()),
400            #[cfg(feature = "moments")]
401            moments: MomentsService::new(config.clone()),
402            #[cfg(feature = "okr")]
403            okr: OkrService::new(config.clone()),
404            #[cfg(feature = "payroll")]
405            payroll: PayrollService::new(config.clone()),
406            #[cfg(feature = "performance")]
407            performance: PerformanceService::new(config.clone()),
408            #[cfg(feature = "personal-settings")]
409            personal_settings: PersonalSettingsService::new(config.clone()),
410            #[cfg(feature = "report")]
411            report: ReportService::new_from_shared(shared_config.clone()),
412            #[cfg(feature = "search")]
413            search: SearchService::new_from_shared(shared_config.clone()),
414            #[cfg(feature = "security-and-compliance")]
415            security_and_compliance: SecurityAndComplianceService::new_from_shared(
416                shared_config.clone(),
417            ),
418            #[cfg(feature = "task")]
419            task: TaskV2Service::new_from_shared(shared_config.clone()),
420            #[cfg(feature = "tenant")]
421            tenant: TenantService::new_from_shared(shared_config.clone()),
422            #[cfg(feature = "tenant-tag")]
423            tenant_tag: TenantTagService::new(config.clone()),
424            #[cfg(feature = "trust-party")]
425            trust_party: TrustPartyService::new(config.clone()),
426            #[cfg(feature = "vc")]
427            vc: VcService::new(config.clone()),
428            #[cfg(feature = "verification")]
429            verification: VerificationService::new(config.clone()),
430            #[cfg(feature = "workplace")]
431            workplace: WorkplaceService::new(config.clone()),
432            // 云文档服务聚合
433            #[cfg(feature = "cloud-docs")]
434            cloud_docs: CloudDocsService::new_from_shared(shared_config.clone()),
435            // 向后兼容的字段(重新创建实例)
436            #[cfg(feature = "cloud-docs")]
437            assistant: AssistantService::new_from_shared(shared_config.clone()),
438            #[cfg(feature = "cloud-docs")]
439            docs: DocsService::new(config.clone()),
440            #[cfg(feature = "cloud-docs")]
441            drive: DriveService::new_from_shared(shared_config.clone()),
442            #[cfg(feature = "cloud-docs")]
443            sheets: SheetsService::new_from_shared(shared_config.clone()),
444            #[cfg(feature = "cloud-docs")]
445            bitable: BitableService::new_from_shared(shared_config.clone()),
446            #[cfg(feature = "cloud-docs")]
447            wiki: WikiService::new_from_shared(shared_config.clone()),
448            #[cfg(feature = "cloud-docs")]
449            comments: CommentsService::new_from_shared(shared_config.clone()),
450            #[cfg(feature = "cloud-docs")]
451            permission: PermissionService::new_from_shared(shared_config.clone()),
452            #[cfg(feature = "cloud-docs")]
453            board: BoardService::new_from_shared(shared_config.clone()),
454        }
455    }
456}
457
458impl LarkClient {
459    /// 创建客户端构建器
460    ///
461    /// # 参数
462    /// - `app_id`: 应用ID,从飞书开放平台获取
463    /// - `app_secret`: 应用密钥,从飞书开放平台获取
464    ///
465    /// # 示例
466    /// ```rust
467    /// use open_lark::prelude::*;
468    ///
469    /// let client = LarkClient::builder("cli_xxx", "xxx")
470    ///     .with_app_type(AppType::SelfBuild)
471    ///     .build();
472    /// ```
473    pub fn builder(app_id: &str, app_secret: &str) -> LarkClientBuilder {
474        LarkClientBuilder {
475            config_builder: Config::builder().app_id(app_id).app_secret(app_secret),
476        }
477    }
478
479    /// 获取共享配置(用于内部服务扇出,减少 clone)
480    #[allow(dead_code)] // Used by services in constructor
481    pub(crate) fn shared_config(&self) -> Arc<Config> {
482        self.shared_config.clone()
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489    use std::time::Duration;
490
491    fn create_test_builder() -> LarkClientBuilder {
492        LarkClient::builder("test_app_id", "test_app_secret")
493    }
494
495    #[test]
496    fn test_client_builder_creation() {
497        let client = LarkClient::builder("test_id", "test_secret").build();
498        assert_eq!(client.config.app_id, "test_id");
499        assert_eq!(client.config.app_secret, "test_secret");
500        assert_eq!(client.config.app_type, AppType::SelfBuild); // Default
501    }
502
503    #[test]
504    fn test_builder_with_app_type() {
505        let client = create_test_builder()
506            .with_app_type(AppType::Marketplace)
507            .build();
508        assert_eq!(client.config.app_type, AppType::Marketplace);
509    }
510
511    #[test]
512    fn test_builder_with_marketplace_app() {
513        let client = create_test_builder().with_marketplace_app().build();
514        assert_eq!(client.config.app_type, AppType::Marketplace);
515    }
516
517    #[test]
518    fn test_builder_with_custom_base_url() {
519        let custom_url = "https://custom.api.feishu.cn";
520        let client = create_test_builder()
521            .with_open_base_url(custom_url.to_string())
522            .build();
523        assert_eq!(client.config.base_url, custom_url);
524    }
525
526    #[test]
527    fn test_builder_with_enable_token_cache() {
528        let client_enabled = create_test_builder().with_enable_token_cache(true).build();
529        assert!(client_enabled.config.enable_token_cache);
530
531        let client_disabled = create_test_builder().with_enable_token_cache(false).build();
532        assert!(!client_disabled.config.enable_token_cache);
533    }
534
535    #[test]
536    fn test_builder_with_req_timeout() {
537        let timeout_seconds = 30.0;
538        let client = create_test_builder()
539            .with_req_timeout(Some(timeout_seconds))
540            .build();
541
542        let expected_duration = Duration::from_secs_f32(timeout_seconds);
543        assert_eq!(client.config.req_timeout, Some(expected_duration));
544    }
545
546    #[test]
547    fn test_builder_with_none_timeout() {
548        let client = create_test_builder().with_req_timeout(None).build();
549        assert_eq!(client.config.req_timeout, None);
550    }
551
552    #[test]
553    fn test_builder_chaining() {
554        let client = create_test_builder()
555            .with_app_type(AppType::Marketplace)
556            .with_enable_token_cache(true)
557            .with_req_timeout(Some(45.0))
558            .with_open_base_url("https://test.api.feishu.cn".to_string())
559            .build();
560
561        assert_eq!(client.config.app_type, AppType::Marketplace);
562        assert!(client.config.enable_token_cache);
563        assert_eq!(
564            client.config.req_timeout,
565            Some(Duration::from_secs_f32(45.0))
566        );
567        assert_eq!(client.config.base_url, "https://test.api.feishu.cn");
568    }
569
570    #[test]
571    fn test_client_build() {
572        let client = create_test_builder().build();
573
574        assert_eq!(client.config.app_id, "test_app_id");
575        assert_eq!(client.config.app_secret, "test_app_secret");
576        assert_eq!(client.config.app_type, AppType::SelfBuild);
577    }
578
579    #[test]
580    fn test_client_build_with_timeout() {
581        let client = create_test_builder().with_req_timeout(Some(60.0)).build();
582
583        assert_eq!(
584            client.config.req_timeout,
585            Some(Duration::from_secs_f32(60.0))
586        );
587    }
588
589    #[test]
590    fn test_client_build_marketplace_app() {
591        let client = create_test_builder().with_marketplace_app().build();
592
593        assert_eq!(client.config.app_type, AppType::Marketplace);
594    }
595
596    #[test]
597    fn test_client_build_with_custom_config() {
598        let client = create_test_builder()
599            .with_app_type(AppType::Marketplace)
600            .with_enable_token_cache(false)
601            .with_open_base_url("https://custom.feishu.cn".to_string())
602            .with_req_timeout(Some(120.0))
603            .build();
604
605        assert_eq!(client.config.app_type, AppType::Marketplace);
606        assert!(!client.config.enable_token_cache);
607        assert_eq!(client.config.base_url, "https://custom.feishu.cn");
608        assert_eq!(
609            client.config.req_timeout,
610            Some(Duration::from_secs_f32(120.0))
611        );
612    }
613
614    #[test]
615    fn test_builder_empty_credentials() {
616        let builder = LarkClient::builder("", "");
617        let config = builder.build_config();
618        assert_eq!(config.app_id, "");
619        assert_eq!(config.app_secret, "");
620    }
621
622    #[test]
623    fn test_builder_unicode_credentials() {
624        let app_id = "测试_app_id_🔑";
625        let app_secret = "测试_secret_🔐";
626        let builder = LarkClient::builder(app_id, app_secret);
627        let config = builder.build_config();
628
629        assert_eq!(config.app_id, app_id);
630        assert_eq!(config.app_secret, app_secret);
631    }
632
633    #[test]
634    fn test_builder_very_long_credentials() {
635        let long_id = "a".repeat(1000);
636        let long_secret = "b".repeat(1000);
637        let builder = LarkClient::builder(&long_id, &long_secret);
638        let config = builder.build_config();
639
640        assert_eq!(config.app_id, long_id);
641        assert_eq!(config.app_secret, long_secret);
642    }
643
644    #[test]
645    fn test_builder_special_characters() {
646        let special_id = "app-id_123!@#$%^&*()";
647        let special_secret = "secret/\\?<>|:\"{}";
648        let builder = LarkClient::builder(special_id, special_secret);
649        let config = builder.build_config();
650
651        assert_eq!(config.app_id, special_id);
652        assert_eq!(config.app_secret, special_secret);
653    }
654
655    #[test]
656    fn test_builder_extreme_timeout_values() {
657        // Very small timeout
658        let small_timeout = create_test_builder().with_req_timeout(Some(0.001)).build();
659        assert_eq!(
660            small_timeout.config.req_timeout,
661            Some(Duration::from_secs_f32(0.001))
662        );
663
664        // Very large timeout
665        let large_timeout = create_test_builder()
666            .with_req_timeout(Some(3600.0)) // 1 hour
667            .build();
668        assert_eq!(
669            large_timeout.config.req_timeout,
670            Some(Duration::from_secs_f32(3600.0))
671        );
672    }
673
674    #[test]
675    fn test_config_independence() {
676        // Test that multiple builders don't interfere with each other
677        let builder1 = create_test_builder().with_app_type(AppType::Marketplace);
678
679        let builder2 = create_test_builder().with_app_type(AppType::SelfBuild);
680
681        assert_eq!(builder1.build_config().app_type, AppType::Marketplace);
682        assert_eq!(builder2.build_config().app_type, AppType::SelfBuild);
683    }
684
685    #[test]
686    fn test_builder_default_values() {
687        let builder = create_test_builder();
688        let config = builder.build_config();
689
690        // Verify default values match Config defaults
691        assert_eq!(config.app_type, AppType::SelfBuild);
692        assert!(config.enable_token_cache); // Default from Config
693        assert_eq!(config.req_timeout, None);
694        assert!(!config.base_url.is_empty()); // Should have default URL
695    }
696
697    #[cfg(feature = "cloud-docs")]
698    #[test]
699    fn test_client_cloud_docs_services() {
700        let client = create_test_builder().build();
701
702        // Verify cloud docs services are available when feature is enabled
703        // This is mainly to check that the struct is properly constructed
704        // We can't test much without actual functionality, but we can verify the services exist
705        let _assistant = &client.assistant;
706        let _drive = &client.drive;
707        let _sheets = &client.sheets;
708        let _bitable = &client.bitable;
709        let _wiki = &client.wiki;
710        let _docs = &client.docs;
711    }
712
713    #[test]
714    fn test_client_builder_multiple_configurations() {
715        // Test that the builder can be used to create multiple different clients
716        let client1 = create_test_builder().with_marketplace_app().build();
717
718        let client2 = LarkClient::builder("different_id", "different_secret")
719            .with_enable_token_cache(false)
720            .build();
721
722        assert_eq!(client1.config.app_type, AppType::Marketplace);
723        assert_eq!(client1.config.app_id, "test_app_id");
724
725        assert_eq!(client2.config.app_type, AppType::SelfBuild);
726        assert_eq!(client2.config.app_id, "different_id");
727        assert!(!client2.config.enable_token_cache);
728    }
729}