open_lark/service/cloud_docs/sheets/v2/spreadsheet_sheet/
operate_sheets.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    core::{
5        api_req::ApiRequest,
6        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
7        constants::AccessTokenType,
8        endpoints::cloud_docs::*,
9        req_option, SDKResult,
10    },
11    impl_executable_builder_owned,
12    service::cloud_docs::sheets::v2::{
13        spreadsheet_sheet::UpdateSheetProperty, SpreadsheetSheetService,
14    },
15};
16
17#[derive(Serialize, Deserialize, Debug, Default)]
18pub struct OperateSheetsRequest {
19    #[serde(skip)]
20    api_request: ApiRequest,
21    #[serde(skip)]
22    spreadsheet_token: String,
23    /// 支持增加、复制、和删除工作表。一次请求可以同时进行多个操作。
24    requests: Vec<OperateSheetsRequestElem>,
25}
26
27#[derive(Serialize, Deserialize, Debug)]
28/// 表格操作请求元素类型
29///
30/// 定义对表格进行操作的不同请求类型
31pub enum OperateSheetsRequestElem {
32    /// 增加工作表。
33    #[serde(rename = "addSheet")]
34    AddSheet {
35        /// 工作表属性
36        properties: AddSheetProperty,
37    },
38    /// 复制工作表。复制的新工作表位于源工作表索引位置之后。
39    #[serde(rename = "copySheet")]
40    CopySheet {
41        /// 需要复制的工作表资源
42        source: CopySheetSource,
43        /// 新工作表的属性
44        destination: CopySheetDestination,
45    },
46    /// 更新工作表
47    #[serde(rename = "updateSheet")]
48    UpdateSheet {
49        /// 工作表属性
50        properties: UpdateSheetProperty,
51    },
52    /// 删除工作表。
53    #[serde(rename = "deleteSheet")]
54    DeleteSheet {
55        /// 要删除的工作表的 ID。调用获取工作表获取 ID
56        #[serde(rename = "sheetId")]
57        sheet_id: String,
58    },
59}
60
61/// 工作表属性
62#[derive(Serialize, Deserialize, Debug, Default)]
63pub struct AddSheetProperty {
64    /// 新增工作表的标题
65    pub title: String,
66    /// 新增工作表的位置。不填默认在工作表的第 0 索引位置增加工作表。
67    pub index: Option<i32>,
68}
69
70/// 需要复制的工作表资源
71#[derive(Serialize, Deserialize, Debug, Default)]
72pub struct CopySheetSource {
73    /// 源工作表的 ID。调用获取工作表获取 ID
74    #[serde(rename = "sheetId")]
75    sheet_id: String,
76}
77
78/// 新工作表的属性
79#[derive(Serialize, Deserialize, Debug, Default)]
80pub struct CopySheetDestination {
81    /// 新工作表名称。不填默认为“源工作表名称”+“(副本_源工作表的 index 值)”,如 “Sheet1(副本_0)”。
82    title: Option<String>,
83}
84
85impl OperateSheetsRequest {
86    pub fn builder() -> OperateSheetsRequestBuilder {
87        OperateSheetsRequestBuilder::default()
88    }
89}
90
91#[derive(Default)]
92pub struct OperateSheetsRequestBuilder {
93    request: OperateSheetsRequest,
94}
95
96impl OperateSheetsRequestBuilder {
97    pub fn spreadsheet_token(mut self, spreadsheet_token: impl ToString) -> Self {
98        self.request.spreadsheet_token = spreadsheet_token.to_string();
99        self
100    }
101
102    /// 增加工作表。
103    pub fn add_sheet(mut self, title: impl ToString, index: Option<i32>) -> Self {
104        self.request
105            .requests
106            .push(OperateSheetsRequestElem::AddSheet {
107                properties: AddSheetProperty {
108                    title: title.to_string(),
109                    index,
110                },
111            });
112        self
113    }
114
115    /// 复制工作表。复制的新工作表位于源工作表索引位置之后。
116    pub fn copy_sheet(mut self, source: impl ToString, destination: Option<String>) -> Self {
117        self.request
118            .requests
119            .push(OperateSheetsRequestElem::CopySheet {
120                source: CopySheetSource {
121                    sheet_id: source.to_string(),
122                },
123                destination: CopySheetDestination { title: destination },
124            });
125        self
126    }
127
128    /// 删除工作表。
129    pub fn delete_sheet(mut self, sheet_id: impl ToString) -> Self {
130        self.request
131            .requests
132            .push(OperateSheetsRequestElem::DeleteSheet {
133                sheet_id: sheet_id.to_string(),
134            });
135        self
136    }
137
138    pub fn build(mut self) -> OperateSheetsRequest {
139        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
140        self.request
141    }
142}
143
144impl_executable_builder_owned!(
145    OperateSheetsRequestBuilder,
146    SpreadsheetSheetService,
147    OperateSheetsRequest,
148    BaseResponse<OperateSheetResponse>,
149    operate
150);
151
152impl SpreadsheetSheetService {
153    /// 操作工作表
154    /// 新增、复制、删除工作表。
155    pub async fn operate(
156        &self,
157        request: OperateSheetsRequest,
158        option: Option<req_option::RequestOption>,
159    ) -> SDKResult<BaseResponse<OperateSheetResponse>> {
160        let mut api_req = request.api_request;
161        api_req.api_path =
162            SHEETS_V2_SPREADSHEET_SHEETS_BATCH_UPDATE.replace("{}", &request.spreadsheet_token);
163        api_req.http_method = reqwest::Method::POST;
164        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
165
166        let api_resp =
167            crate::core::http::Transport::request(api_req, &self.config_arc, option).await?;
168
169        Ok(api_resp)
170    }
171}
172
173#[derive(Deserialize, Debug)]
174pub struct OperateSheetResponse {
175    pub replies: Vec<OperateSheetReply>,
176}
177
178impl ApiResponseTrait for OperateSheetResponse {
179    fn data_format() -> ResponseFormat {
180        ResponseFormat::Data
181    }
182}
183
184#[derive(Deserialize, Debug)]
185/// 表格操作响应类型
186///
187/// 定义表格操作请求的响应结果类型
188pub enum OperateSheetReply {
189    /// 新增工作表的属性
190    #[serde(rename = "addSheet")]
191    AddSheet { properties: SheetResponse },
192    /// 复制工作表的结果
193    #[serde(rename = "copySheet")]
194    CopySheet { properties: SheetResponse },
195    /// 更新工作表的结果
196    #[serde(rename = "updateSheet")]
197    UpdateSheet { properties: UpdateSheetProperty },
198    /// 删除工作表的结果
199    #[serde(rename = "deleteSheet")]
200    DeleteSheet {
201        /// 删除工作表是否成功
202        result: bool,
203        /// 被删除的工作表的 ID
204        #[serde(rename = "sheetId")]
205        sheet_id: String,
206    },
207}
208
209#[derive(Deserialize, Debug)]
210/// 工作表的属性
211pub struct SheetResponse {
212    /// 工作表的 sheetId
213    #[serde(rename = "sheetId")]
214    pub sheet_id: String,
215    /// 工作表的标题
216    pub title: String,
217    /// 工作表的位置
218    pub index: Option<i32>,
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224    use crate::core::config::Config;
225    use rstest::rstest;
226
227    fn create_test_config() -> Config {
228        Config::builder()
229            .app_id("test_app_id")
230            .app_secret("test_app_secret")
231            .build()
232    }
233
234    fn create_test_service() -> SpreadsheetSheetService {
235        SpreadsheetSheetService::new(create_test_config())
236    }
237
238    #[test]
239    fn test_operate_sheets_request_builder_creation() {
240        let builder = OperateSheetsRequest::builder();
241        let request = builder.build();
242
243        assert_eq!(request.spreadsheet_token, "");
244        assert!(request.requests.is_empty());
245    }
246
247    #[test]
248    fn test_operate_sheets_request_builder_with_spreadsheet_token() {
249        let request = OperateSheetsRequest::builder()
250            .spreadsheet_token("test_spreadsheet_123")
251            .build();
252
253        assert_eq!(request.spreadsheet_token, "test_spreadsheet_123");
254    }
255
256    #[test]
257    fn test_operate_sheets_request_builder_with_add_sheet() {
258        let request = OperateSheetsRequest::builder()
259            .add_sheet("New Sheet", Some(2))
260            .build();
261
262        assert_eq!(request.requests.len(), 1);
263        match &request.requests[0] {
264            OperateSheetsRequestElem::AddSheet { properties } => {
265                assert_eq!(properties.title, "New Sheet");
266                assert_eq!(properties.index, Some(2));
267            }
268            _ => panic!("Expected AddSheet variant"),
269        }
270    }
271
272    #[test]
273    fn test_operate_sheets_request_builder_with_add_sheet_no_index() {
274        let request = OperateSheetsRequest::builder()
275            .add_sheet("Default Position Sheet", None)
276            .build();
277
278        assert_eq!(request.requests.len(), 1);
279        match &request.requests[0] {
280            OperateSheetsRequestElem::AddSheet { properties } => {
281                assert_eq!(properties.title, "Default Position Sheet");
282                assert!(properties.index.is_none());
283            }
284            _ => panic!("Expected AddSheet variant"),
285        }
286    }
287
288    #[test]
289    fn test_operate_sheets_request_builder_with_copy_sheet() {
290        let request = OperateSheetsRequest::builder()
291            .copy_sheet("source_sheet_123", Some("Copied Sheet".to_string()))
292            .build();
293
294        assert_eq!(request.requests.len(), 1);
295        match &request.requests[0] {
296            OperateSheetsRequestElem::CopySheet {
297                source,
298                destination,
299            } => {
300                assert_eq!(source.sheet_id, "source_sheet_123");
301                assert_eq!(destination.title, Some("Copied Sheet".to_string()));
302            }
303            _ => panic!("Expected CopySheet variant"),
304        }
305    }
306
307    #[test]
308    fn test_operate_sheets_request_builder_with_copy_sheet_no_title() {
309        let request = OperateSheetsRequest::builder()
310            .copy_sheet("source_sheet_456", None)
311            .build();
312
313        assert_eq!(request.requests.len(), 1);
314        match &request.requests[0] {
315            OperateSheetsRequestElem::CopySheet {
316                source,
317                destination,
318            } => {
319                assert_eq!(source.sheet_id, "source_sheet_456");
320                assert!(destination.title.is_none());
321            }
322            _ => panic!("Expected CopySheet variant"),
323        }
324    }
325
326    #[test]
327    fn test_operate_sheets_request_builder_with_delete_sheet() {
328        let request = OperateSheetsRequest::builder()
329            .delete_sheet("sheet_to_delete_789")
330            .build();
331
332        assert_eq!(request.requests.len(), 1);
333        match &request.requests[0] {
334            OperateSheetsRequestElem::DeleteSheet { sheet_id } => {
335                assert_eq!(sheet_id, "sheet_to_delete_789");
336            }
337            _ => panic!("Expected DeleteSheet variant"),
338        }
339    }
340
341    #[test]
342    fn test_operate_sheets_request_builder_chaining_multiple_operations() {
343        let request = OperateSheetsRequest::builder()
344            .spreadsheet_token("my_spreadsheet")
345            .add_sheet("Sheet 1", Some(0))
346            .add_sheet("Sheet 2", Some(1))
347            .copy_sheet("existing_sheet", Some("Copy of Existing".to_string()))
348            .delete_sheet("old_sheet")
349            .build();
350
351        assert_eq!(request.spreadsheet_token, "my_spreadsheet");
352        assert_eq!(request.requests.len(), 4);
353
354        // Verify each operation
355        match &request.requests[0] {
356            OperateSheetsRequestElem::AddSheet { properties } => {
357                assert_eq!(properties.title, "Sheet 1");
358                assert_eq!(properties.index, Some(0));
359            }
360            _ => panic!("Expected AddSheet variant"),
361        }
362
363        match &request.requests[1] {
364            OperateSheetsRequestElem::AddSheet { properties } => {
365                assert_eq!(properties.title, "Sheet 2");
366                assert_eq!(properties.index, Some(1));
367            }
368            _ => panic!("Expected AddSheet variant"),
369        }
370
371        match &request.requests[2] {
372            OperateSheetsRequestElem::CopySheet {
373                source,
374                destination,
375            } => {
376                assert_eq!(source.sheet_id, "existing_sheet");
377                assert_eq!(destination.title, Some("Copy of Existing".to_string()));
378            }
379            _ => panic!("Expected CopySheet variant"),
380        }
381
382        match &request.requests[3] {
383            OperateSheetsRequestElem::DeleteSheet { sheet_id } => {
384                assert_eq!(sheet_id, "old_sheet");
385            }
386            _ => panic!("Expected DeleteSheet variant"),
387        }
388    }
389
390    #[test]
391    fn test_operate_sheets_request_default() {
392        let request = OperateSheetsRequest::default();
393
394        assert_eq!(request.spreadsheet_token, "");
395        assert!(request.requests.is_empty());
396    }
397
398    #[test]
399    fn test_operate_sheets_request_builder_default() {
400        let builder = OperateSheetsRequestBuilder::default();
401        let request = builder.build();
402
403        assert_eq!(request.spreadsheet_token, "");
404        assert!(request.requests.is_empty());
405    }
406
407    #[test]
408    fn test_add_sheet_property_default() {
409        let props = AddSheetProperty::default();
410
411        assert_eq!(props.title, "");
412        assert!(props.index.is_none());
413    }
414
415    #[test]
416    fn test_copy_sheet_source_default() {
417        let source = CopySheetSource::default();
418
419        assert_eq!(source.sheet_id, "");
420    }
421
422    #[test]
423    fn test_copy_sheet_destination_default() {
424        let destination = CopySheetDestination::default();
425
426        assert!(destination.title.is_none());
427    }
428
429    #[test]
430    fn test_operate_sheets_request_serialization() {
431        let request = OperateSheetsRequest::builder()
432            .spreadsheet_token("token123")
433            .add_sheet("Test Sheet", Some(1))
434            .copy_sheet("source123", Some("Copy Title".to_string()))
435            .delete_sheet("delete456")
436            .build();
437
438        // Test that the request can be serialized (this validates field names)
439        let serialized = serde_json::to_string(&request);
440        assert!(serialized.is_ok());
441
442        let json_str = serialized.unwrap();
443        assert!(json_str.contains("requests"));
444        assert!(json_str.contains("addSheet"));
445        assert!(json_str.contains("copySheet"));
446        assert!(json_str.contains("deleteSheet"));
447        assert!(json_str.contains("Test Sheet"));
448        assert!(json_str.contains("source123"));
449        assert!(json_str.contains("Copy Title"));
450        assert!(json_str.contains("delete456"));
451    }
452
453    #[test]
454    fn test_operate_sheets_request_serialization_empty() {
455        let request = OperateSheetsRequest::builder()
456            .spreadsheet_token("token123")
457            .build();
458
459        let serialized = serde_json::to_string(&request);
460        assert!(serialized.is_ok());
461
462        let json_str = serialized.unwrap();
463        assert!(json_str.contains("requests"));
464        assert!(json_str.contains("[]")); // Empty array
465    }
466
467    #[test]
468    fn test_operate_sheets_request_debug() {
469        let request = OperateSheetsRequest::builder()
470            .spreadsheet_token("debug_token")
471            .add_sheet("Debug Sheet", Some(0))
472            .build();
473
474        let debug_str = format!("{:?}", request);
475        assert!(debug_str.contains("OperateSheetsRequest"));
476        assert!(debug_str.contains("debug_token"));
477        assert!(debug_str.contains("Debug Sheet"));
478    }
479
480    #[test]
481    fn test_operate_sheets_request_with_empty_strings() {
482        let request = OperateSheetsRequest::builder()
483            .spreadsheet_token("")
484            .add_sheet("", None)
485            .copy_sheet("", None)
486            .delete_sheet("")
487            .build();
488
489        assert_eq!(request.spreadsheet_token, "");
490        assert_eq!(request.requests.len(), 3);
491    }
492
493    #[test]
494    fn test_operate_sheets_request_with_special_characters() {
495        let request = OperateSheetsRequest::builder()
496            .spreadsheet_token("token_with_特殊字符_🎯")
497            .add_sheet("工作表_📋_测试", Some(1))
498            .copy_sheet("源工作表_🔗", Some("副本_🎨".to_string()))
499            .delete_sheet("删除工作表_🗑️")
500            .build();
501
502        assert_eq!(request.spreadsheet_token, "token_with_特殊字符_🎯");
503
504        match &request.requests[0] {
505            OperateSheetsRequestElem::AddSheet { properties } => {
506                assert_eq!(properties.title, "工作表_📋_测试");
507            }
508            _ => panic!("Expected AddSheet variant"),
509        }
510
511        match &request.requests[1] {
512            OperateSheetsRequestElem::CopySheet {
513                source,
514                destination,
515            } => {
516                assert_eq!(source.sheet_id, "源工作表_🔗");
517                assert_eq!(destination.title, Some("副本_🎨".to_string()));
518            }
519            _ => panic!("Expected CopySheet variant"),
520        }
521
522        match &request.requests[2] {
523            OperateSheetsRequestElem::DeleteSheet { sheet_id } => {
524                assert_eq!(sheet_id, "删除工作表_🗑️");
525            }
526            _ => panic!("Expected DeleteSheet variant"),
527        }
528    }
529
530    #[rstest]
531    #[case(-1)] // Negative index
532    #[case(0)] // First position
533    #[case(1)] // Second position
534    #[case(10)] // High index
535    #[case(1000)] // Very high index
536    fn test_operate_sheets_request_with_various_indices(#[case] index: i32) {
537        let request = OperateSheetsRequest::builder()
538            .add_sheet("Test Sheet", Some(index))
539            .build();
540
541        match &request.requests[0] {
542            OperateSheetsRequestElem::AddSheet { properties } => {
543                assert_eq!(properties.index, Some(index));
544            }
545            _ => panic!("Expected AddSheet variant"),
546        }
547    }
548
549    #[test]
550    fn test_operate_sheets_request_with_maximum_values() {
551        let request = OperateSheetsRequest::builder()
552            .add_sheet("Max Index Sheet", Some(i32::MAX))
553            .build();
554
555        match &request.requests[0] {
556            OperateSheetsRequestElem::AddSheet { properties } => {
557                assert_eq!(properties.index, Some(i32::MAX));
558            }
559            _ => panic!("Expected AddSheet variant"),
560        }
561    }
562
563    #[test]
564    fn test_operate_sheets_request_with_minimum_values() {
565        let request = OperateSheetsRequest::builder()
566            .add_sheet("Min Index Sheet", Some(i32::MIN))
567            .build();
568
569        match &request.requests[0] {
570            OperateSheetsRequestElem::AddSheet { properties } => {
571                assert_eq!(properties.index, Some(i32::MIN));
572            }
573            _ => panic!("Expected AddSheet variant"),
574        }
575    }
576
577    #[test]
578    fn test_operate_sheets_request_api_request_body_serialization() {
579        let request = OperateSheetsRequest::builder()
580            .spreadsheet_token("body_test_token")
581            .add_sheet("Body Test Sheet", Some(0))
582            .delete_sheet("delete_me")
583            .build();
584
585        // Verify that api_request.body is set correctly
586        assert!(!request.api_request.body.is_empty());
587
588        // Verify that the body contains valid JSON
589        let body_str = String::from_utf8(request.api_request.body).unwrap();
590        let parsed: serde_json::Value = serde_json::from_str(&body_str).unwrap();
591
592        assert!(parsed.get("requests").is_some());
593        let requests = parsed.get("requests").unwrap().as_array().unwrap();
594        assert_eq!(requests.len(), 2);
595
596        // Check first request (AddSheet)
597        assert!(requests[0].get("addSheet").is_some());
598        let add_sheet = requests[0].get("addSheet").unwrap();
599        assert!(add_sheet.get("properties").is_some());
600
601        // Check second request (DeleteSheet)
602        assert!(requests[1].get("deleteSheet").is_some());
603        let delete_sheet = requests[1].get("deleteSheet").unwrap();
604        assert_eq!(delete_sheet.get("sheetId").unwrap(), "delete_me");
605    }
606
607    #[test]
608    fn test_operate_sheets_request_builder_multiple_calls() {
609        let mut builder = OperateSheetsRequest::builder();
610
611        // Test that multiple calls to spreadsheet_token override previous values
612        builder = builder.spreadsheet_token("first_token");
613        builder = builder.spreadsheet_token("second_token");
614
615        // Add multiple operations
616        builder = builder.add_sheet("Sheet 1", Some(0));
617        builder = builder.add_sheet("Sheet 2", Some(1));
618
619        let request = builder.build();
620
621        assert_eq!(request.spreadsheet_token, "second_token");
622        assert_eq!(request.requests.len(), 2);
623    }
624
625    #[test]
626    fn test_spreadsheet_sheet_service_creation() {
627        let service = create_test_service();
628
629        // Verify the service can be created without panic
630        assert_eq!(service.config.app_id, "test_app_id");
631    }
632
633    #[test]
634    fn test_operate_sheet_response_data_format() {
635        assert_eq!(OperateSheetResponse::data_format(), ResponseFormat::Data);
636    }
637
638    #[test]
639    fn test_operate_sheet_reply_deserialization_add_sheet() {
640        let json_response = r#"{
641            "addSheet": {
642                "properties": {
643                    "sheetId": "sheet123",
644                    "title": "New Sheet",
645                    "index": 1
646                }
647            }
648        }"#;
649
650        let reply: OperateSheetReply = serde_json::from_str(json_response).unwrap();
651
652        match reply {
653            OperateSheetReply::AddSheet { properties } => {
654                assert_eq!(properties.sheet_id, "sheet123");
655                assert_eq!(properties.title, "New Sheet");
656                assert_eq!(properties.index, Some(1));
657            }
658            _ => panic!("Expected AddSheet variant"),
659        }
660    }
661
662    #[test]
663    fn test_operate_sheet_reply_deserialization_copy_sheet() {
664        let json_response = r#"{
665            "copySheet": {
666                "properties": {
667                    "sheetId": "copy456",
668                    "title": "Copied Sheet",
669                    "index": 2
670                }
671            }
672        }"#;
673
674        let reply: OperateSheetReply = serde_json::from_str(json_response).unwrap();
675
676        match reply {
677            OperateSheetReply::CopySheet { properties } => {
678                assert_eq!(properties.sheet_id, "copy456");
679                assert_eq!(properties.title, "Copied Sheet");
680                assert_eq!(properties.index, Some(2));
681            }
682            _ => panic!("Expected CopySheet variant"),
683        }
684    }
685
686    #[test]
687    fn test_operate_sheet_reply_deserialization_delete_sheet() {
688        let json_response = r#"{
689            "deleteSheet": {
690                "result": true,
691                "sheetId": "deleted789"
692            }
693        }"#;
694
695        let reply: OperateSheetReply = serde_json::from_str(json_response).unwrap();
696
697        match reply {
698            OperateSheetReply::DeleteSheet { result, sheet_id } => {
699                assert!(result);
700                assert_eq!(sheet_id, "deleted789");
701            }
702            _ => panic!("Expected DeleteSheet variant"),
703        }
704    }
705
706    #[test]
707    fn test_operate_sheet_response_deserialization() {
708        let json_response = r#"{
709            "replies": [
710                {
711                    "addSheet": {
712                        "properties": {
713                            "sheetId": "new123",
714                            "title": "Added Sheet",
715                            "index": 0
716                        }
717                    }
718                },
719                {
720                    "deleteSheet": {
721                        "result": true,
722                        "sheetId": "old456"
723                    }
724                }
725            ]
726        }"#;
727
728        let response: OperateSheetResponse = serde_json::from_str(json_response).unwrap();
729
730        assert_eq!(response.replies.len(), 2);
731
732        match &response.replies[0] {
733            OperateSheetReply::AddSheet { properties } => {
734                assert_eq!(properties.sheet_id, "new123");
735                assert_eq!(properties.title, "Added Sheet");
736                assert_eq!(properties.index, Some(0));
737            }
738            _ => panic!("Expected AddSheet variant"),
739        }
740
741        match &response.replies[1] {
742            OperateSheetReply::DeleteSheet { result, sheet_id } => {
743                assert!(result);
744                assert_eq!(sheet_id, "old456");
745            }
746            _ => panic!("Expected DeleteSheet variant"),
747        }
748    }
749
750    #[test]
751    fn test_operate_sheets_request_edge_cases() {
752        // Test with very long token
753        let long_token = "a".repeat(10000);
754        let request = OperateSheetsRequest::builder()
755            .spreadsheet_token(&long_token)
756            .build();
757        assert_eq!(request.spreadsheet_token, long_token);
758
759        // Test with very long sheet titles
760        let long_title = "Sheet_".repeat(1000);
761        let request = OperateSheetsRequest::builder()
762            .add_sheet(&long_title, Some(0))
763            .build();
764
765        match &request.requests[0] {
766            OperateSheetsRequestElem::AddSheet { properties } => {
767                assert_eq!(properties.title, long_title);
768            }
769            _ => panic!("Expected AddSheet variant"),
770        }
771
772        // Test with very long sheet IDs
773        let long_sheet_id = "sheet_id_".repeat(500);
774        let request = OperateSheetsRequest::builder()
775            .copy_sheet(&long_sheet_id, None)
776            .delete_sheet(&long_sheet_id)
777            .build();
778
779        match &request.requests[0] {
780            OperateSheetsRequestElem::CopySheet { source, .. } => {
781                assert_eq!(source.sheet_id, long_sheet_id);
782            }
783            _ => panic!("Expected CopySheet variant"),
784        }
785    }
786
787    #[test]
788    fn test_operate_sheets_request_memory_efficiency() {
789        // Test creating many requests doesn't consume excessive memory
790        let mut builder = OperateSheetsRequest::builder().spreadsheet_token("memory_test");
791
792        // Add many operations
793        for i in 0..100 {
794            builder = builder.add_sheet(format!("Sheet_{}", i), Some(i));
795            builder = builder.copy_sheet(format!("source_{}", i), Some(format!("copy_{}", i)));
796            builder = builder.delete_sheet(format!("delete_{}", i));
797        }
798
799        let request = builder.build();
800
801        assert_eq!(request.requests.len(), 300); // 100 * 3 operations
802
803        // Verify a few random operations
804        match &request.requests[0] {
805            OperateSheetsRequestElem::AddSheet { properties } => {
806                assert_eq!(properties.title, "Sheet_0");
807                assert_eq!(properties.index, Some(0));
808            }
809            _ => panic!("Expected AddSheet variant"),
810        }
811
812        match &request.requests[299] {
813            OperateSheetsRequestElem::DeleteSheet { sheet_id } => {
814                assert_eq!(sheet_id, "delete_99");
815            }
816            _ => panic!("Expected DeleteSheet variant"),
817        }
818    }
819
820    #[test]
821    fn test_sheet_response_debug() {
822        let response = SheetResponse {
823            sheet_id: "debug_sheet_123".to_string(),
824            title: "Debug Sheet".to_string(),
825            index: Some(5),
826        };
827
828        let debug_str = format!("{:?}", response);
829        assert!(debug_str.contains("SheetResponse"));
830        assert!(debug_str.contains("debug_sheet_123"));
831        assert!(debug_str.contains("Debug Sheet"));
832        assert!(debug_str.contains("Some(5)"));
833    }
834
835    #[test]
836    fn test_operate_sheets_request_unicode_handling() {
837        let request = OperateSheetsRequest::builder()
838            .spreadsheet_token("令牌_🔑_test")
839            .add_sheet("工作表_📋_name", Some(0))
840            .copy_sheet("源_🎯", Some("目标_🎪".to_string()))
841            .delete_sheet("删除_🗑️")
842            .build();
843
844        assert_eq!(request.spreadsheet_token, "令牌_🔑_test");
845
846        // Test serialization works with Unicode
847        let serialized = serde_json::to_string(&request);
848        assert!(serialized.is_ok());
849
850        let json_str = serialized.unwrap();
851        assert!(json_str.contains("工作表_📋_name"));
852        assert!(json_str.contains("源_🎯"));
853        assert!(json_str.contains("目标_🎪"));
854        assert!(json_str.contains("删除_🗑️"));
855    }
856
857    #[test]
858    fn test_operate_sheets_request_builder_partial_configuration() {
859        // Test building with only some operations
860        let request1 = OperateSheetsRequest::builder()
861            .add_sheet("Only Add", Some(1))
862            .build();
863
864        assert_eq!(request1.requests.len(), 1);
865
866        let request2 = OperateSheetsRequest::builder()
867            .copy_sheet("source", None)
868            .delete_sheet("target")
869            .build();
870
871        assert_eq!(request2.requests.len(), 2);
872
873        let request3 = OperateSheetsRequest::builder()
874            .spreadsheet_token("test")
875            .build();
876
877        assert!(request3.requests.is_empty());
878    }
879}