1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8 config::Config,
9 constants::AccessTokenType,
10 endpoints::cloud_docs::*,
11 http::Transport,
12 req_option::RequestOption,
13 SDKResult,
14 },
15 impl_executable_builder_owned,
16};
17
18pub struct FolderService {
20 config: Config,
21}
22
23impl FolderService {
24 pub fn new(config: Config) -> Self {
25 Self { config }
26 }
27
28 pub async fn get_root_folder_meta(
34 &self,
35 option: Option<RequestOption>,
36 ) -> SDKResult<BaseResponse<GetRootFolderMetaRespData>> {
37 let api_req = ApiRequest {
38 http_method: Method::GET,
39 api_path: DRIVE_V1_FOLDERS_ROOT_FOLDER_META.to_string(),
40 supported_access_token_types: vec![AccessTokenType::User],
41 ..Default::default()
42 };
43
44 let api_resp = Transport::request(api_req, &self.config, option).await?;
45 Ok(api_resp)
46 }
47
48 pub async fn list_files(
54 &self,
55 request: ListFilesRequest,
56 option: Option<RequestOption>,
57 ) -> SDKResult<BaseResponse<ListFilesRespData>> {
58 let mut api_req = ApiRequest {
59 http_method: Method::GET,
60 api_path: DRIVE_V1_FOLDER_CHILDREN.replace("{}", &request.folder_token),
61 ..Default::default()
62 };
63 api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
64
65 if let Some(page_token) = request.page_token {
67 api_req.query_params.insert("page_token", page_token);
68 }
69 if let Some(page_size) = request.page_size {
70 api_req
71 .query_params
72 .insert("page_size", page_size.to_string());
73 }
74 if let Some(order_by) = request.order_by {
75 api_req.query_params.insert("order_by", order_by);
76 }
77 if let Some(direction) = request.direction {
78 api_req.query_params.insert("direction", direction);
79 }
80
81 let api_resp = Transport::request(api_req, &self.config, option).await?;
82 Ok(api_resp)
83 }
84
85 pub async fn get_folder_meta(
91 &self,
92 request: GetFolderMetaRequest,
93 option: Option<RequestOption>,
94 ) -> SDKResult<BaseResponse<GetFolderMetaRespData>> {
95 let api_req = ApiRequest {
96 http_method: Method::GET,
97 api_path: DRIVE_V1_FOLDER_GET.replace("{}", &request.folder_token),
98 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
99 ..Default::default()
100 };
101
102 let api_resp = Transport::request(api_req, &self.config, option).await?;
103 Ok(api_resp)
104 }
105
106 pub async fn create_folder(
112 &self,
113 request: CreateFolderRequest,
114 option: Option<RequestOption>,
115 ) -> SDKResult<BaseResponse<CreateFolderRespData>> {
116 let api_req = ApiRequest {
117 http_method: Method::POST,
118 api_path: DRIVE_V1_FOLDERS.to_string(),
119 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
120 body: serde_json::to_vec(&request)?,
121 ..Default::default()
122 };
123
124 let api_resp = Transport::request(api_req, &self.config, option).await?;
125 Ok(api_resp)
126 }
127
128 pub async fn move_or_delete_folder(
134 &self,
135 request: MoveOrDeleteFolderRequest,
136 option: Option<RequestOption>,
137 ) -> SDKResult<BaseResponse<MoveOrDeleteFolderRespData>> {
138 let mut api_req = ApiRequest {
139 http_method: Method::POST,
140 api_path: DRIVE_V1_FOLDER_MOVE.replace("{}", &request.folder_token),
141 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
142 ..Default::default()
143 };
144
145 let body = serde_json::json!({
147 "type": request.operation_type,
148 "parent_token": request.parent_token
149 });
150 api_req.body = serde_json::to_vec(&body)?;
151
152 let api_resp = Transport::request(api_req, &self.config, option).await?;
153 Ok(api_resp)
154 }
155
156 pub async fn check_async_task(
162 &self,
163 request: CheckAsyncTaskRequest,
164 option: Option<RequestOption>,
165 ) -> SDKResult<BaseResponse<CheckAsyncTaskRespData>> {
166 let api_req = ApiRequest {
167 http_method: Method::GET,
168 api_path: DRIVE_V1_TASK_GET.replace("{}", &request.task_id),
169 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
170 ..Default::default()
171 };
172
173 let api_resp = Transport::request(api_req, &self.config, option).await?;
174 Ok(api_resp)
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct GetRootFolderMetaRespData {
181 pub token: String,
183 pub user_id: String,
185}
186
187impl ApiResponseTrait for GetRootFolderMetaRespData {
188 fn data_format() -> ResponseFormat {
189 ResponseFormat::Data
190 }
191}
192
193#[derive(Debug, Clone, Default, Serialize, Deserialize)]
195pub struct ListFilesRequest {
196 pub folder_token: String,
198 pub page_token: Option<String>,
200 pub page_size: Option<i32>,
202 pub order_by: Option<String>,
204 pub direction: Option<String>,
206}
207
208impl ListFilesRequest {
209 pub fn new(folder_token: impl Into<String>) -> Self {
210 Self {
211 folder_token: folder_token.into(),
212 ..Default::default()
213 }
214 }
215
216 pub fn builder() -> ListFilesRequestBuilder {
217 ListFilesRequestBuilder::default()
218 }
219}
220
221#[derive(Debug, Clone, Default)]
223pub struct ListFilesRequestBuilder {
224 request: ListFilesRequest,
225}
226
227impl ListFilesRequestBuilder {
228 pub fn folder_token(mut self, folder_token: impl Into<String>) -> Self {
229 self.request.folder_token = folder_token.into();
230 self
231 }
232
233 pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
234 self.request.page_token = Some(page_token.into());
235 self
236 }
237
238 pub fn page_size(mut self, page_size: i32) -> Self {
239 self.request.page_size = Some(page_size);
240 self
241 }
242
243 pub fn order_by(mut self, order_by: impl Into<String>) -> Self {
244 self.request.order_by = Some(order_by.into());
245 self
246 }
247
248 pub fn direction(mut self, direction: impl Into<String>) -> Self {
249 self.request.direction = Some(direction.into());
250 self
251 }
252
253 pub fn build(self) -> ListFilesRequest {
254 self.request
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct ListFilesRespData {
261 pub has_more: bool,
263 pub page_token: Option<String>,
265 pub files: Vec<DriveFile>,
267}
268
269impl ApiResponseTrait for ListFilesRespData {
270 fn data_format() -> ResponseFormat {
271 ResponseFormat::Data
272 }
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct DriveFile {
278 pub token: String,
280 pub name: String,
282 #[serde(rename = "type")]
284 pub file_type: String,
285 pub parent_token: Option<String>,
287 pub url: Option<String>,
289 pub short_url: Option<String>,
291 pub size: Option<i64>,
293 pub mime_type: Option<String>,
295 pub created_time: Option<String>,
297 pub modified_time: Option<String>,
299 pub owner_id: Option<String>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct GetFolderMetaRequest {
306 pub folder_token: String,
308}
309
310impl GetFolderMetaRequest {
311 pub fn new(folder_token: impl Into<String>) -> Self {
312 Self {
313 folder_token: folder_token.into(),
314 }
315 }
316}
317
318#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct GetFolderMetaRespData {
321 pub token: String,
323 pub id: String,
325 pub name: String,
327 pub parent_token: Option<String>,
329 pub owner_id: String,
331 pub creator_id: Option<String>,
333 pub create_time: String,
335 pub edit_time: String,
337 pub description: Option<String>,
339 pub url: String,
341}
342
343impl ApiResponseTrait for GetFolderMetaRespData {
344 fn data_format() -> ResponseFormat {
345 ResponseFormat::Data
346 }
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct CreateFolderRequest {
352 pub name: String,
354 pub parent_token: String,
356}
357
358impl CreateFolderRequest {
359 pub fn new(name: impl Into<String>, parent_token: impl Into<String>) -> Self {
360 Self {
361 name: name.into(),
362 parent_token: parent_token.into(),
363 }
364 }
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct CreateFolderRespData {
370 pub token: String,
372 pub url: String,
374}
375
376impl ApiResponseTrait for CreateFolderRespData {
377 fn data_format() -> ResponseFormat {
378 ResponseFormat::Data
379 }
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize)]
384pub struct MoveOrDeleteFolderRequest {
385 pub folder_token: String,
387 #[serde(rename = "type")]
389 pub operation_type: String,
390 pub parent_token: Option<String>,
392}
393
394impl MoveOrDeleteFolderRequest {
395 pub fn move_folder(folder_token: impl Into<String>, parent_token: impl Into<String>) -> Self {
397 Self {
398 folder_token: folder_token.into(),
399 operation_type: "move".to_string(),
400 parent_token: Some(parent_token.into()),
401 }
402 }
403
404 pub fn delete_folder(folder_token: impl Into<String>) -> Self {
406 Self {
407 folder_token: folder_token.into(),
408 operation_type: "delete".to_string(),
409 parent_token: None,
410 }
411 }
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct MoveOrDeleteFolderRespData {
417 pub task_id: Option<String>,
419}
420
421impl ApiResponseTrait for MoveOrDeleteFolderRespData {
422 fn data_format() -> ResponseFormat {
423 ResponseFormat::Data
424 }
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct CheckAsyncTaskRequest {
430 pub task_id: String,
432}
433
434impl CheckAsyncTaskRequest {
435 pub fn new(task_id: impl Into<String>) -> Self {
436 Self {
437 task_id: task_id.into(),
438 }
439 }
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct CheckAsyncTaskRespData {
445 pub status: String,
447 pub error_msg: Option<String>,
449}
450
451impl ApiResponseTrait for CheckAsyncTaskRespData {
452 fn data_format() -> ResponseFormat {
453 ResponseFormat::Data
454 }
455}
456
457impl_executable_builder_owned!(
460 ListFilesRequestBuilder,
461 FolderService,
462 ListFilesRequest,
463 BaseResponse<ListFilesRespData>,
464 list_files
465);
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470 use rstest::*;
471
472 fn mock_config() -> Config {
473 Config::builder()
474 .app_id("test_app_id")
475 .app_secret("test_app_secret")
476 .build()
477 }
478
479 #[test]
482 fn test_folder_service_new() {
483 let config = mock_config();
484 let service = FolderService::new(config.clone());
485 assert_eq!(service.config.app_id, config.app_id);
486 }
487
488 #[test]
491 fn test_list_files_request_new() {
492 let request = ListFilesRequest::new("test_folder_token");
493 assert_eq!(request.folder_token, "test_folder_token");
494 assert!(request.page_token.is_none());
495 assert!(request.page_size.is_none());
496 assert!(request.order_by.is_none());
497 assert!(request.direction.is_none());
498 }
499
500 #[test]
501 fn test_list_files_request_builder() {
502 let request = ListFilesRequest::builder()
503 .folder_token("folder123")
504 .page_token("page123")
505 .page_size(100)
506 .order_by("created_time")
507 .direction("ASC")
508 .build();
509
510 assert_eq!(request.folder_token, "folder123");
511 assert_eq!(request.page_token, Some("page123".to_string()));
512 assert_eq!(request.page_size, Some(100));
513 assert_eq!(request.order_by, Some("created_time".to_string()));
514 assert_eq!(request.direction, Some("ASC".to_string()));
515 }
516
517 #[test]
518 fn test_list_files_request_builder_fluent() {
519 let request = ListFilesRequest::builder()
520 .folder_token("test")
521 .page_size(50)
522 .order_by("modified_time")
523 .build();
524
525 assert_eq!(request.folder_token, "test");
526 assert_eq!(request.page_size, Some(50));
527 assert_eq!(request.order_by, Some("modified_time".to_string()));
528 assert!(request.page_token.is_none());
529 assert!(request.direction.is_none());
530 }
531
532 #[test]
533 fn test_get_folder_meta_request_new() {
534 let request = GetFolderMetaRequest::new("folder_token_123");
535 assert_eq!(request.folder_token, "folder_token_123");
536 }
537
538 #[test]
539 fn test_create_folder_request_new() {
540 let request = CreateFolderRequest::new("My Folder", "parent_token_456");
541 assert_eq!(request.name, "My Folder");
542 assert_eq!(request.parent_token, "parent_token_456");
543 }
544
545 #[test]
546 fn test_move_or_delete_folder_request_move() {
547 let request = MoveOrDeleteFolderRequest::move_folder("folder123", "new_parent456");
548 assert_eq!(request.folder_token, "folder123");
549 assert_eq!(request.operation_type, "move");
550 assert_eq!(request.parent_token, Some("new_parent456".to_string()));
551 }
552
553 #[test]
554 fn test_move_or_delete_folder_request_delete() {
555 let request = MoveOrDeleteFolderRequest::delete_folder("folder789");
556 assert_eq!(request.folder_token, "folder789");
557 assert_eq!(request.operation_type, "delete");
558 assert!(request.parent_token.is_none());
559 }
560
561 #[test]
562 fn test_check_async_task_request_new() {
563 let request = CheckAsyncTaskRequest::new("task_id_abc");
564 assert_eq!(request.task_id, "task_id_abc");
565 }
566
567 #[test]
570 fn test_get_root_folder_meta_resp_data() {
571 let data = GetRootFolderMetaRespData {
572 token: "root_token".to_string(),
573 user_id: "user123".to_string(),
574 };
575 assert_eq!(data.token, "root_token");
576 assert_eq!(data.user_id, "user123");
577 }
578
579 #[test]
580 fn test_list_files_resp_data() {
581 let file = DriveFile {
582 token: "file_token".to_string(),
583 name: "document.pdf".to_string(),
584 file_type: "pdf".to_string(),
585 parent_token: Some("parent123".to_string()),
586 url: Some("https://example.com/file".to_string()),
587 short_url: Some("https://short.ly/abc".to_string()),
588 size: Some(1024000),
589 mime_type: Some("application/pdf".to_string()),
590 created_time: Some("2023-01-01T00:00:00Z".to_string()),
591 modified_time: Some("2023-01-02T00:00:00Z".to_string()),
592 owner_id: Some("owner123".to_string()),
593 };
594
595 let data = ListFilesRespData {
596 has_more: true,
597 page_token: Some("next_page".to_string()),
598 files: vec![file.clone()],
599 };
600
601 assert!(data.has_more);
602 assert_eq!(data.page_token, Some("next_page".to_string()));
603 assert_eq!(data.files.len(), 1);
604 assert_eq!(data.files[0].token, "file_token");
605 assert_eq!(data.files[0].name, "document.pdf");
606 assert_eq!(data.files[0].size, Some(1024000));
607 }
608
609 #[test]
610 fn test_drive_file_optional_fields() {
611 let file = DriveFile {
612 token: "minimal_file".to_string(),
613 name: "simple.txt".to_string(),
614 file_type: "txt".to_string(),
615 parent_token: None,
616 url: None,
617 short_url: None,
618 size: None,
619 mime_type: None,
620 created_time: None,
621 modified_time: None,
622 owner_id: None,
623 };
624
625 assert_eq!(file.token, "minimal_file");
626 assert_eq!(file.name, "simple.txt");
627 assert!(file.parent_token.is_none());
628 assert!(file.url.is_none());
629 assert!(file.size.is_none());
630 }
631
632 #[test]
633 fn test_get_folder_meta_resp_data() {
634 let data = GetFolderMetaRespData {
635 token: "folder_token".to_string(),
636 id: "folder_id".to_string(),
637 name: "My Documents".to_string(),
638 parent_token: Some("parent_folder".to_string()),
639 owner_id: "owner123".to_string(),
640 creator_id: Some("creator456".to_string()),
641 create_time: "2023-01-01T00:00:00Z".to_string(),
642 edit_time: "2023-01-02T00:00:00Z".to_string(),
643 description: Some("Folder description".to_string()),
644 url: "https://example.com/folder".to_string(),
645 };
646
647 assert_eq!(data.token, "folder_token");
648 assert_eq!(data.name, "My Documents");
649 assert_eq!(data.parent_token, Some("parent_folder".to_string()));
650 assert_eq!(data.description, Some("Folder description".to_string()));
651 }
652
653 #[test]
654 fn test_create_folder_resp_data() {
655 let data = CreateFolderRespData {
656 token: "new_folder_token".to_string(),
657 url: "https://example.com/new-folder".to_string(),
658 };
659 assert_eq!(data.token, "new_folder_token");
660 assert_eq!(data.url, "https://example.com/new-folder");
661 }
662
663 #[test]
664 fn test_move_or_delete_folder_resp_data() {
665 let data = MoveOrDeleteFolderRespData {
666 task_id: Some("async_task_123".to_string()),
667 };
668 assert_eq!(data.task_id, Some("async_task_123".to_string()));
669
670 let data_no_task = MoveOrDeleteFolderRespData { task_id: None };
671 assert!(data_no_task.task_id.is_none());
672 }
673
674 #[test]
675 fn test_check_async_task_resp_data() {
676 let success_data = CheckAsyncTaskRespData {
677 status: "SUCCESS".to_string(),
678 error_msg: None,
679 };
680 assert_eq!(success_data.status, "SUCCESS");
681 assert!(success_data.error_msg.is_none());
682
683 let failure_data = CheckAsyncTaskRespData {
684 status: "FAILURE".to_string(),
685 error_msg: Some("Task failed due to insufficient permissions".to_string()),
686 };
687 assert_eq!(failure_data.status, "FAILURE");
688 assert_eq!(
689 failure_data.error_msg,
690 Some("Task failed due to insufficient permissions".to_string())
691 );
692 }
693
694 #[rstest]
697 #[case("list_files_request")]
698 #[case("get_folder_meta_request")]
699 #[case("create_folder_request")]
700 #[case("move_or_delete_folder_request")]
701 #[case("check_async_task_request")]
702 fn test_request_serialization_roundtrip(#[case] request_type: &str) {
703 match request_type {
704 "list_files_request" => {
705 let original = ListFilesRequest::builder()
706 .folder_token("test123")
707 .page_size(50)
708 .order_by("created_time")
709 .direction("DESC")
710 .build();
711 let json = serde_json::to_string(&original).unwrap();
712 let deserialized: ListFilesRequest = serde_json::from_str(&json).unwrap();
713 assert_eq!(original.folder_token, deserialized.folder_token);
714 assert_eq!(original.page_size, deserialized.page_size);
715 assert_eq!(original.order_by, deserialized.order_by);
716 }
717 "get_folder_meta_request" => {
718 let original = GetFolderMetaRequest::new("folder123");
719 let json = serde_json::to_string(&original).unwrap();
720 let deserialized: GetFolderMetaRequest = serde_json::from_str(&json).unwrap();
721 assert_eq!(original.folder_token, deserialized.folder_token);
722 }
723 "create_folder_request" => {
724 let original = CreateFolderRequest::new("Test Folder", "parent123");
725 let json = serde_json::to_string(&original).unwrap();
726 let deserialized: CreateFolderRequest = serde_json::from_str(&json).unwrap();
727 assert_eq!(original.name, deserialized.name);
728 assert_eq!(original.parent_token, deserialized.parent_token);
729 }
730 "move_or_delete_folder_request" => {
731 let original = MoveOrDeleteFolderRequest::move_folder("folder123", "new_parent");
732 let json = serde_json::to_string(&original).unwrap();
733 let deserialized: MoveOrDeleteFolderRequest = serde_json::from_str(&json).unwrap();
734 assert_eq!(original.folder_token, deserialized.folder_token);
735 assert_eq!(original.operation_type, deserialized.operation_type);
736 assert_eq!(original.parent_token, deserialized.parent_token);
737 }
738 "check_async_task_request" => {
739 let original = CheckAsyncTaskRequest::new("task123");
740 let json = serde_json::to_string(&original).unwrap();
741 let deserialized: CheckAsyncTaskRequest = serde_json::from_str(&json).unwrap();
742 assert_eq!(original.task_id, deserialized.task_id);
743 }
744 _ => panic!("Unknown request type: {}", request_type),
745 }
746 }
747
748 #[rstest]
749 #[case("get_root_folder_meta_resp")]
750 #[case("list_files_resp")]
751 #[case("get_folder_meta_resp")]
752 #[case("create_folder_resp")]
753 #[case("move_or_delete_folder_resp")]
754 #[case("check_async_task_resp")]
755 fn test_response_serialization_roundtrip(#[case] response_type: &str) {
756 match response_type {
757 "get_root_folder_meta_resp" => {
758 let original = GetRootFolderMetaRespData {
759 token: "root123".to_string(),
760 user_id: "user456".to_string(),
761 };
762 let json = serde_json::to_string(&original).unwrap();
763 let deserialized: GetRootFolderMetaRespData = serde_json::from_str(&json).unwrap();
764 assert_eq!(original.token, deserialized.token);
765 assert_eq!(original.user_id, deserialized.user_id);
766 }
767 "list_files_resp" => {
768 let original = ListFilesRespData {
769 has_more: false,
770 page_token: None,
771 files: vec![],
772 };
773 let json = serde_json::to_string(&original).unwrap();
774 let deserialized: ListFilesRespData = serde_json::from_str(&json).unwrap();
775 assert_eq!(original.has_more, deserialized.has_more);
776 assert_eq!(original.page_token, deserialized.page_token);
777 assert_eq!(original.files.len(), deserialized.files.len());
778 }
779 "get_folder_meta_resp" => {
780 let original = GetFolderMetaRespData {
781 token: "folder123".to_string(),
782 id: "id123".to_string(),
783 name: "Test".to_string(),
784 parent_token: None,
785 owner_id: "owner123".to_string(),
786 creator_id: None,
787 create_time: "2023-01-01T00:00:00Z".to_string(),
788 edit_time: "2023-01-01T00:00:00Z".to_string(),
789 description: None,
790 url: "https://example.com".to_string(),
791 };
792 let json = serde_json::to_string(&original).unwrap();
793 let deserialized: GetFolderMetaRespData = serde_json::from_str(&json).unwrap();
794 assert_eq!(original.token, deserialized.token);
795 assert_eq!(original.name, deserialized.name);
796 }
797 "create_folder_resp" => {
798 let original = CreateFolderRespData {
799 token: "new123".to_string(),
800 url: "https://example.com/new".to_string(),
801 };
802 let json = serde_json::to_string(&original).unwrap();
803 let deserialized: CreateFolderRespData = serde_json::from_str(&json).unwrap();
804 assert_eq!(original.token, deserialized.token);
805 assert_eq!(original.url, deserialized.url);
806 }
807 "move_or_delete_folder_resp" => {
808 let original = MoveOrDeleteFolderRespData {
809 task_id: Some("task123".to_string()),
810 };
811 let json = serde_json::to_string(&original).unwrap();
812 let deserialized: MoveOrDeleteFolderRespData = serde_json::from_str(&json).unwrap();
813 assert_eq!(original.task_id, deserialized.task_id);
814 }
815 "check_async_task_resp" => {
816 let original = CheckAsyncTaskRespData {
817 status: "PENDING".to_string(),
818 error_msg: None,
819 };
820 let json = serde_json::to_string(&original).unwrap();
821 let deserialized: CheckAsyncTaskRespData = serde_json::from_str(&json).unwrap();
822 assert_eq!(original.status, deserialized.status);
823 assert_eq!(original.error_msg, deserialized.error_msg);
824 }
825 _ => panic!("Unknown response type: {}", response_type),
826 }
827 }
828
829 #[rstest]
832 #[case("GetRootFolderMetaRespData")]
833 #[case("ListFilesRespData")]
834 #[case("GetFolderMetaRespData")]
835 #[case("CreateFolderRespData")]
836 #[case("MoveOrDeleteFolderRespData")]
837 #[case("CheckAsyncTaskRespData")]
838 fn test_api_response_trait(#[case] response_type: &str) {
839 let format = match response_type {
840 "GetRootFolderMetaRespData" => GetRootFolderMetaRespData::data_format(),
841 "ListFilesRespData" => ListFilesRespData::data_format(),
842 "GetFolderMetaRespData" => GetFolderMetaRespData::data_format(),
843 "CreateFolderRespData" => CreateFolderRespData::data_format(),
844 "MoveOrDeleteFolderRespData" => MoveOrDeleteFolderRespData::data_format(),
845 "CheckAsyncTaskRespData" => CheckAsyncTaskRespData::data_format(),
846 _ => panic!("Unknown response type: {}", response_type),
847 };
848 assert_eq!(format, ResponseFormat::Data);
849 }
850
851 #[test]
854 fn test_empty_folder_token() {
855 let request = ListFilesRequest::new("");
856 assert_eq!(request.folder_token, "");
857 }
858
859 #[test]
860 fn test_very_long_folder_token() {
861 let long_token = "a".repeat(1000);
862 let request = ListFilesRequest::new(&long_token);
863 assert_eq!(request.folder_token, long_token);
864 }
865
866 #[test]
867 fn test_unicode_folder_names() {
868 let unicode_name = "文件夹测试🗂️";
869 let request = CreateFolderRequest::new(unicode_name, "parent123");
870 assert_eq!(request.name, unicode_name);
871 }
872
873 #[test]
874 fn test_special_characters_in_names() {
875 let special_name = "Folder with spaces & symbols @#$%";
876 let request = CreateFolderRequest::new(special_name, "parent");
877 assert_eq!(request.name, special_name);
878 }
879
880 #[test]
881 fn test_large_page_size() {
882 let request = ListFilesRequest::builder()
883 .folder_token("test")
884 .page_size(999999)
885 .build();
886 assert_eq!(request.page_size, Some(999999));
887 }
888
889 #[test]
890 fn test_negative_page_size() {
891 let request = ListFilesRequest::builder()
892 .folder_token("test")
893 .page_size(-1)
894 .build();
895 assert_eq!(request.page_size, Some(-1));
896 }
897
898 #[test]
899 fn test_drive_file_with_large_size() {
900 let file = DriveFile {
901 token: "large_file".to_string(),
902 name: "huge_video.mp4".to_string(),
903 file_type: "mp4".to_string(),
904 parent_token: Some("parent".to_string()),
905 url: None,
906 short_url: None,
907 size: Some(i64::MAX),
908 mime_type: Some("video/mp4".to_string()),
909 created_time: None,
910 modified_time: None,
911 owner_id: None,
912 };
913 assert_eq!(file.size, Some(i64::MAX));
914 }
915
916 #[test]
917 fn test_drive_file_zero_size() {
918 let file = DriveFile {
919 token: "empty_file".to_string(),
920 name: "empty.txt".to_string(),
921 file_type: "txt".to_string(),
922 parent_token: None,
923 url: None,
924 short_url: None,
925 size: Some(0),
926 mime_type: Some("text/plain".to_string()),
927 created_time: None,
928 modified_time: None,
929 owner_id: None,
930 };
931 assert_eq!(file.size, Some(0));
932 }
933
934 #[test]
935 fn test_task_status_variations() {
936 let statuses = ["PENDING", "SUCCESS", "FAILURE", "RUNNING", "CANCELLED"];
937 for status in statuses {
938 let resp = CheckAsyncTaskRespData {
939 status: status.to_string(),
940 error_msg: None,
941 };
942 assert_eq!(resp.status, status);
943 }
944 }
945
946 #[test]
947 fn test_long_error_message() {
948 let long_error = "Error: ".repeat(100);
949 let resp = CheckAsyncTaskRespData {
950 status: "FAILURE".to_string(),
951 error_msg: Some(long_error.clone()),
952 };
953 assert_eq!(resp.error_msg, Some(long_error));
954 }
955
956 #[test]
959 fn test_list_files_builder_chaining() {
960 let builder = ListFilesRequest::builder();
961 let request = builder
962 .folder_token("test")
963 .page_size(10)
964 .page_size(20) .order_by("name")
966 .build();
967 assert_eq!(request.page_size, Some(20));
968 assert_eq!(request.order_by, Some("name".to_string()));
969 }
970
971 #[test]
972 fn test_empty_builder() {
973 let request = ListFilesRequest::builder().build();
974 assert_eq!(request.folder_token, "");
975 assert!(request.page_token.is_none());
976 assert!(request.page_size.is_none());
977 }
978
979 #[rstest]
982 #[case("created_time", "ASC")]
983 #[case("edited_time", "DESC")]
984 #[case("file_type", "ASC")]
985 #[case("size", "DESC")]
986 #[case("name", "ASC")]
987 fn test_valid_sort_combinations(#[case] order_by: &str, #[case] direction: &str) {
988 let request = ListFilesRequest::builder()
989 .folder_token("test")
990 .order_by(order_by)
991 .direction(direction)
992 .build();
993 assert_eq!(request.order_by, Some(order_by.to_string()));
994 assert_eq!(request.direction, Some(direction.to_string()));
995 }
996
997 #[test]
998 fn test_invalid_sort_parameters() {
999 let request = ListFilesRequest::builder()
1000 .folder_token("test")
1001 .order_by("invalid_field")
1002 .direction("INVALID_DIRECTION")
1003 .build();
1004 assert_eq!(request.order_by, Some("invalid_field".to_string()));
1005 assert_eq!(request.direction, Some("INVALID_DIRECTION".to_string()));
1006 }
1007
1008 #[test]
1011 fn test_request_clone() {
1012 let original = ListFilesRequest::builder()
1013 .folder_token("test")
1014 .page_size(50)
1015 .build();
1016 let cloned = original.clone();
1017 assert_eq!(original.folder_token, cloned.folder_token);
1018 assert_eq!(original.page_size, cloned.page_size);
1019 }
1020
1021 #[test]
1022 fn test_response_debug() {
1023 let data = CreateFolderRespData {
1024 token: "debug_test".to_string(),
1025 url: "https://test.com".to_string(),
1026 };
1027 let debug_str = format!("{:?}", data);
1028 assert!(debug_str.contains("debug_test"));
1029 assert!(debug_str.contains("https://test.com"));
1030 }
1031}