1pub(crate) mod account;
4pub(crate) mod auth;
5pub(crate) mod create;
6pub(crate) mod dedupe;
7pub(crate) mod docs;
8pub(crate) mod edit;
9pub(crate) mod format;
10pub(crate) mod helpers;
11pub(crate) mod lease;
12pub(crate) mod move_file;
15pub(crate) mod permissions;
16pub(crate) mod read;
17pub(crate) mod rename;
18pub(crate) mod search;
19pub(crate) mod sheets;
20pub(crate) mod upload;
21
22use anyhow::Result;
23use clap::{Parser, Subcommand};
24
25use crate::drive::account::DRIVE_ACCOUNT_ENV;
26use crate::drive::client::DriveClient;
27
28#[derive(Parser)]
30pub struct DriveCommand {
31 #[arg(long, global = true, value_name = "NAME")]
43 pub account: Option<String>,
44 #[command(subcommand)]
46 pub command: DriveSubcommands,
47}
48
49#[derive(Subcommand)]
51pub enum DriveSubcommands {
52 Auth(auth::AuthCommand),
54 Account(account::AccountCommand),
56 Search(search::SearchCommand),
58 Read(read::ReadCommand),
60 Dedupe(dedupe::DedupeCommand),
62 Create(create::CreateCommand),
66 Upload(upload::UploadCommand),
70 Edit(edit::EditCommand),
76 Lease(lease::LeaseCommand),
80 Rename(rename::RenameCommand),
83 Move(move_file::MoveCommand),
86 Permissions(permissions::PermissionsCommand),
91 Docs(docs::DocsCommand),
94 Sheets(sheets::SheetsCommand),
97}
98
99impl DriveCommand {
100 pub async fn execute(self) -> Result<()> {
107 let _account_guard = self
116 .account
117 .as_ref()
118 .map(|account| crate::utils::env::ScopedEnvVar::set(DRIVE_ACCOUNT_ENV, account));
119
120 match self.command {
121 DriveSubcommands::Auth(cmd) => cmd.execute().await,
122 DriveSubcommands::Account(cmd) => cmd.execute(),
123 DriveSubcommands::Permissions(cmd) => cmd.execute().await,
128 DriveSubcommands::Lease(cmd) => cmd.execute(helpers::create_client).await,
139 command => {
140 let client = helpers::create_client()?;
141 command.dispatch(&client).await
142 }
143 }
144 }
145}
146
147impl DriveSubcommands {
148 async fn dispatch(self, client: &DriveClient) -> Result<()> {
152 match self {
153 Self::Auth(_) => unreachable!("Auth is dispatched before client resolution"),
154 Self::Account(_) => unreachable!("Account is dispatched before client resolution"),
155 Self::Permissions(_) => {
156 unreachable!("Permissions is dispatched before client resolution")
157 }
158 Self::Lease(_) => unreachable!("Lease is dispatched before client resolution"),
159 Self::Search(cmd) => cmd.execute(client).await,
160 Self::Read(cmd) => cmd.execute(client).await,
161 Self::Dedupe(cmd) => cmd.execute(client).await,
162 Self::Create(cmd) => cmd.execute(client).await,
163 Self::Upload(cmd) => cmd.execute(client).await,
164 Self::Edit(cmd) => cmd.execute(client).await,
165 Self::Rename(cmd) => cmd.execute(client).await,
166 Self::Move(cmd) => cmd.execute(client).await,
167 Self::Docs(cmd) => cmd.execute(client).await,
168 Self::Sheets(cmd) => cmd.execute(client).await,
169 }
170 }
171}
172
173#[cfg(test)]
174#[allow(clippy::unwrap_used)]
175mod tests {
176 use super::*;
177 use crate::cli::drive::format::OutputFormat;
178 use crate::drive::auth::{DriveCredentials, DriveGrantedScopes};
179 use crate::utils::secret::Secret;
180
181 fn dead_credentials() -> DriveCredentials {
182 DriveCredentials {
183 client_id: "client".to_string(),
184 client_secret: Secret::new("secret"),
185 refresh_token: Secret::new("refresh"),
186 scope: DriveGrantedScopes::READONLY,
187 }
188 }
189
190 fn dead_client() -> DriveClient {
191 DriveClient::new("http://127.0.0.1:1", &dead_credentials()).unwrap()
192 }
193
194 fn no_write_args() -> helpers::StructureWriteArgs {
197 helpers::StructureWriteArgs {
198 dry_run: false,
199 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
200 output: OutputFormat::Table,
201 }
202 }
203
204 #[tokio::test]
205 async fn execute_routes_auth_subcommand_and_surfaces_missing_credentials() {
206 let guard = crate::drive::test_support::EnvGuard::take();
207 let _dir = guard.clear_credentials();
208
209 let cmd = DriveCommand {
210 account: None,
211 command: DriveSubcommands::Auth(auth::AuthCommand {
212 command: auth::AuthSubcommands::Status(auth::StatusCommand { all: false }),
213 }),
214 };
215 let err = cmd.execute().await.unwrap_err();
216 assert!(err.to_string().contains("not configured"));
217 }
218
219 #[tokio::test]
220 async fn execute_non_auth_subcommand_errors_when_credentials_missing() {
221 let guard = crate::drive::test_support::EnvGuard::take();
222 let _dir = guard.clear_credentials();
223
224 let cmd = DriveCommand {
225 account: None,
226 command: DriveSubcommands::Search(search::SearchCommand {
227 query: "name contains 'report'".to_string(),
228 limit: 10,
229 output: OutputFormat::Table,
230 }),
231 };
232 let err = cmd.execute().await.unwrap_err();
233 assert!(err.to_string().contains("not configured"));
234 }
235
236 #[tokio::test]
248 async fn execute_lease_release_needs_no_credentials() {
249 let guard = crate::drive::test_support::EnvGuard::take();
250 let dir = guard.clear_credentials();
251 let audit = crate::test_support::AuditLogGuard::redirect(dir.path());
252
253 let cmd = lease::LeaseCommand::release_for_test("no-such-token".to_string());
254 let code = cmd
255 .run(|| panic!("release must never resolve a client"))
256 .await
257 .unwrap();
258 assert_eq!(code, 1, "NoSuchToken must be a non-zero exit");
259
260 assert_eq!(audit.verdicts(), vec!["release-no-such-token".to_string()]);
261 }
262
263 #[tokio::test]
267 async fn execute_lease_prune_dry_run_needs_no_credentials() {
268 let guard = crate::drive::test_support::EnvGuard::take();
269 let _dir = guard.clear_credentials();
270
271 let cmd = DriveCommand {
272 account: None,
273 command: DriveSubcommands::Lease(lease::LeaseCommand::prune_for_test("1h", true)),
274 };
275 cmd.execute().await.unwrap();
276 }
277
278 #[tokio::test]
281 async fn execute_lease_acquire_still_errors_when_credentials_missing() {
282 let guard = crate::drive::test_support::EnvGuard::take();
283 let _dir = guard.clear_credentials();
284
285 let cmd = DriveCommand {
286 account: None,
287 command: DriveSubcommands::Lease(lease::LeaseCommand::acquire_for_test(
288 "file-1".to_string(),
289 )),
290 };
291 let err = cmd.execute().await.unwrap_err();
292 assert!(err.to_string().contains("not configured"), "{err}");
293 }
294
295 #[tokio::test]
296 async fn execute_restores_account_env_var_after_return() {
297 let guard = crate::drive::test_support::EnvGuard::take();
298 let _dir = guard.clear_credentials();
299
300 let cmd = DriveCommand {
301 account: Some("work".to_string()),
302 command: DriveSubcommands::Account(account::AccountCommand {
303 command: account::AccountSubcommands::List(account::list::ListCommand {
304 output: OutputFormat::Table,
305 }),
306 }),
307 };
308 cmd.execute().await.unwrap();
309 assert_eq!(std::env::var(DRIVE_ACCOUNT_ENV).ok(), None);
310 }
311
312 #[tokio::test]
313 async fn execute_restores_previous_account_env_var_after_return() {
314 let guard = crate::drive::test_support::EnvGuard::take();
315 let _dir = guard.clear_credentials();
316 std::env::set_var(DRIVE_ACCOUNT_ENV, "personal");
317
318 let cmd = DriveCommand {
319 account: Some("work".to_string()),
320 command: DriveSubcommands::Account(account::AccountCommand {
321 command: account::AccountSubcommands::List(account::list::ListCommand {
322 output: OutputFormat::Table,
323 }),
324 }),
325 };
326 cmd.execute().await.unwrap();
327 assert_eq!(
328 std::env::var(DRIVE_ACCOUNT_ENV).ok().as_deref(),
329 Some("personal")
330 );
331 }
332
333 #[tokio::test]
334 async fn execute_does_not_leak_account_across_sequential_calls() {
335 let guard = crate::drive::test_support::EnvGuard::take();
336 let _dir = guard.clear_credentials();
337
338 let account_list_cmd = || DriveCommand {
339 account: None,
340 command: DriveSubcommands::Account(account::AccountCommand {
341 command: account::AccountSubcommands::List(account::list::ListCommand {
342 output: OutputFormat::Table,
343 }),
344 }),
345 };
346
347 let first = DriveCommand {
348 account: Some("alpha".to_string()),
349 ..account_list_cmd()
350 };
351 first.execute().await.unwrap();
352 assert_eq!(std::env::var(DRIVE_ACCOUNT_ENV).ok(), None);
353
354 account_list_cmd().execute().await.unwrap();
357 assert_eq!(std::env::var(DRIVE_ACCOUNT_ENV).ok(), None);
358 }
359
360 #[tokio::test]
361 async fn execute_absent_account_leaves_ambient_env_var_untouched() {
362 let guard = crate::drive::test_support::EnvGuard::take();
363 let _dir = guard.clear_credentials();
364 std::env::set_var(DRIVE_ACCOUNT_ENV, "personal");
365
366 let cmd = DriveCommand {
367 account: None,
368 command: DriveSubcommands::Account(account::AccountCommand {
369 command: account::AccountSubcommands::List(account::list::ListCommand {
370 output: OutputFormat::Table,
371 }),
372 }),
373 };
374 cmd.execute().await.unwrap();
375 assert_eq!(
376 std::env::var(DRIVE_ACCOUNT_ENV).ok().as_deref(),
377 Some("personal")
378 );
379 }
380
381 #[tokio::test]
382 async fn execute_routes_account_list_without_client_resolution() {
383 let guard = crate::drive::test_support::EnvGuard::take();
384 let _dir = guard.clear_credentials();
385
386 let cmd = DriveCommand {
387 account: None,
388 command: DriveSubcommands::Account(account::AccountCommand {
389 command: account::AccountSubcommands::List(account::list::ListCommand {
390 output: OutputFormat::Table,
391 }),
392 }),
393 };
394 cmd.execute().await.unwrap();
395 }
396
397 #[tokio::test]
398 async fn execute_routes_permissions_show_without_client_resolution() {
399 let guard = crate::drive::test_support::EnvGuard::take();
400 let _dir = guard.clear_credentials();
401
402 let cmd = DriveCommand {
403 account: None,
404 command: DriveSubcommands::Permissions(permissions::PermissionsCommand {
405 command: permissions::PermissionsSubcommands::Show(
406 permissions::show::ShowCommand {
407 output: OutputFormat::Table,
408 },
409 ),
410 }),
411 };
412 cmd.execute().await.unwrap();
413 }
414
415 #[tokio::test]
416 async fn execute_routes_permissions_check_and_surfaces_missing_credentials() {
417 let guard = crate::drive::test_support::EnvGuard::take();
418 let _dir = guard.clear_credentials();
419
420 let cmd = DriveCommand {
421 account: None,
422 command: DriveSubcommands::Permissions(permissions::PermissionsCommand {
423 command: permissions::PermissionsSubcommands::Check(
424 permissions::check::CheckCommand {
425 id: "f1".to_string(),
426 operation: permissions::check::OperationArg::Read,
427 output: OutputFormat::Table,
428 },
429 ),
430 }),
431 };
432 let err = cmd.execute().await.unwrap_err();
433 assert!(err.to_string().contains("not configured"));
434 }
435
436 #[tokio::test]
437 async fn dispatch_routes_docs_info() {
438 let guard = crate::drive::test_support::EnvGuard::take();
443 guard.redirect_api_hosts_to_a_dead_port();
444
445 let cmd = DriveSubcommands::Docs(docs::DocsCommand {
446 command: docs::DocsSubcommands::Info(docs::info::InfoCommand {
447 document_id: "d1".to_string(),
448 output: OutputFormat::Table,
449 }),
450 });
451 assert!(cmd.dispatch(&dead_client()).await.is_err());
452 }
453
454 #[tokio::test]
455 async fn dispatch_routes_docs_read() {
456 let guard = crate::drive::test_support::EnvGuard::take();
457 guard.redirect_api_hosts_to_a_dead_port();
458
459 let cmd = DriveSubcommands::Docs(docs::DocsCommand {
460 command: docs::DocsSubcommands::Read(docs::read::ReadCommand {
461 document_id: "d1".to_string(),
462 tab: None,
463 suggestions_view: docs::read::SuggestionsViewArg::Default,
464 output: OutputFormat::Table,
465 }),
466 });
467 assert!(cmd.dispatch(&dead_client()).await.is_err());
468 }
469
470 #[tokio::test]
482 async fn dispatch_routes_docs_replace() {
483 let guard = crate::drive::test_support::EnvGuard::take();
484 guard.redirect_api_hosts_to_a_dead_port();
485 let _dir = guard.clear_credentials();
486
487 let cmd = DriveSubcommands::Docs(docs::DocsCommand {
488 command: docs::DocsSubcommands::Replace(docs::write::ReplaceCommand {
489 document_id: "d1".to_string(),
490 search: "a".to_string(),
491 replace: "b".to_string(),
492 ignore_case: false,
493 dry_run: false,
494 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
495 output: OutputFormat::Table,
496 }),
497 });
498 assert!(cmd.dispatch(&dead_client()).await.is_ok());
499 }
500
501 #[tokio::test]
502 async fn dispatch_routes_docs_append() {
503 let guard = crate::drive::test_support::EnvGuard::take();
504 guard.redirect_api_hosts_to_a_dead_port();
505
506 let _dir = guard.clear_credentials();
507
508 let cmd = DriveSubcommands::Docs(docs::DocsCommand {
509 command: docs::DocsSubcommands::Append(docs::write::AppendCommand {
510 document_id: "d1".to_string(),
511 text: Some("x".to_string()),
512 text_file: None,
513 dry_run: false,
514 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
515 output: OutputFormat::Table,
516 }),
517 });
518 assert!(cmd.dispatch(&dead_client()).await.is_ok());
520 }
521
522 #[tokio::test]
523 async fn dispatch_routes_docs_create() {
524 let guard = crate::drive::test_support::EnvGuard::take();
525 guard.redirect_api_hosts_to_a_dead_port();
526
527 let _dir = guard.clear_credentials();
528
529 let cmd = DriveSubcommands::Docs(docs::DocsCommand {
530 command: docs::DocsSubcommands::Create(docs::create::CreateCommand {
531 name: "n".to_string(),
532 parent: "folder-1".to_string(),
533 text: None,
534 text_file: None,
535 dry_run: false,
536 output: OutputFormat::Table,
537 }),
538 });
539 assert!(cmd.dispatch(&dead_client()).await.is_ok());
541 }
542
543 #[tokio::test]
544 async fn dispatch_routes_sheets_info() {
545 let guard = crate::drive::test_support::EnvGuard::take();
550 guard.redirect_api_hosts_to_a_dead_port();
551
552 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
553 command: sheets::SheetsSubcommands::Info(sheets::info::InfoCommand {
554 spreadsheet_id: "s1".to_string(),
555 output: OutputFormat::Table,
556 }),
557 });
558 assert!(cmd.dispatch(&dead_client()).await.is_err());
559 }
560
561 #[tokio::test]
562 async fn dispatch_routes_sheets_read() {
563 let guard = crate::drive::test_support::EnvGuard::take();
564 guard.redirect_api_hosts_to_a_dead_port();
565
566 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
567 command: sheets::SheetsSubcommands::Read(sheets::read::ReadCommand {
568 spreadsheet_id: "s1".to_string(),
569 range: None,
570 sheet: None,
571 render: sheets::read::RenderArg::Formatted,
572 output: OutputFormat::Table,
573 }),
574 });
575 assert!(cmd.dispatch(&dead_client()).await.is_err());
576 }
577
578 #[tokio::test]
579 async fn dispatch_routes_sheets_write() {
580 let guard = crate::drive::test_support::EnvGuard::take();
581 guard.redirect_api_hosts_to_a_dead_port();
582 let _dir = guard.clear_credentials();
583
584 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
585 command: sheets::SheetsSubcommands::Write(sheets::write::WriteCommand {
586 spreadsheet_id: "s1".to_string(),
587 range: Some("A1".to_string()),
588 sheet: None,
589 values: "/definitely/not/here.csv".to_string(),
590 values_format: sheets::values::ValuesFormat::Auto,
591 input: sheets::write::InputArg::UserEntered,
592 dry_run: false,
593 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
594 output: OutputFormat::Table,
595 }),
596 });
597 assert!(cmd.dispatch(&dead_client()).await.is_err());
598 }
599
600 #[tokio::test]
601 async fn dispatch_routes_sheets_append() {
602 let guard = crate::drive::test_support::EnvGuard::take();
603 guard.redirect_api_hosts_to_a_dead_port();
604 let _dir = guard.clear_credentials();
605
606 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
607 command: sheets::SheetsSubcommands::Append(sheets::write::AppendCommand {
608 spreadsheet_id: "s1".to_string(),
609 range: Some("A1".to_string()),
610 sheet: None,
611 values: "/definitely/not/here.csv".to_string(),
612 values_format: sheets::values::ValuesFormat::Auto,
613 input: sheets::write::InputArg::UserEntered,
614 dry_run: false,
615 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
616 output: OutputFormat::Table,
617 }),
618 });
619 assert!(cmd.dispatch(&dead_client()).await.is_err());
620 }
621
622 #[tokio::test]
623 async fn dispatch_routes_sheets_clear() {
624 let guard = crate::drive::test_support::EnvGuard::take();
625 guard.redirect_api_hosts_to_a_dead_port();
626 let _dir = guard.clear_credentials();
627
628 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
629 command: sheets::SheetsSubcommands::Clear(sheets::write::ClearCommand {
630 spreadsheet_id: "s1".to_string(),
631 range: Some("A1".to_string()),
632 sheet: None,
633 dry_run: false,
634 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
635 output: OutputFormat::Table,
636 }),
637 });
638 assert!(cmd.dispatch(&dead_client()).await.is_ok());
645 }
646
647 #[tokio::test]
648 async fn dispatch_routes_sheets_create() {
649 let guard = crate::drive::test_support::EnvGuard::take();
650 guard.redirect_api_hosts_to_a_dead_port();
651 let _dir = guard.clear_credentials();
652
653 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
654 command: sheets::SheetsSubcommands::Create(sheets::create::CreateCommand {
655 name: "Budget".to_string(),
656 parent: "parent-1".to_string(),
657 values: None,
658 values_format: sheets::values::ValuesFormat::Auto,
659 input: sheets::write::InputArg::UserEntered,
660 dry_run: false,
661 output: OutputFormat::Table,
662 }),
663 });
664 assert!(cmd.dispatch(&dead_client()).await.is_ok());
668 }
669
670 #[tokio::test]
671 async fn dispatch_routes_sheets_add_sheet() {
672 let guard = crate::drive::test_support::EnvGuard::take();
673 guard.redirect_api_hosts_to_a_dead_port();
674 let _dir = guard.clear_credentials();
675
676 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
677 command: sheets::SheetsSubcommands::AddSheet(sheets::structure::AddSheetCommand {
678 target: sheets::structure::SpreadsheetIdArg {
679 spreadsheet_id: "sheet-1".to_string(),
680 },
681 title: "Q3".to_string(),
682 index: None,
683 rows: None,
684 columns: None,
685 write: no_write_args(),
686 }),
687 });
688 assert!(cmd.dispatch(&dead_client()).await.is_ok());
691 }
692
693 #[tokio::test]
694 async fn dispatch_routes_sheets_rename_sheet() {
695 let guard = crate::drive::test_support::EnvGuard::take();
696 guard.redirect_api_hosts_to_a_dead_port();
697 let _dir = guard.clear_credentials();
698
699 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
700 command: sheets::SheetsSubcommands::RenameSheet(
701 sheets::structure::RenameSheetCommand {
702 target: sheets::structure::SpreadsheetIdArg {
703 spreadsheet_id: "sheet-1".to_string(),
704 },
705 sheet: "Q2".to_string(),
706 title: "Q3".to_string(),
707 write: no_write_args(),
708 },
709 ),
710 });
711 assert!(cmd.dispatch(&dead_client()).await.is_ok());
712 }
713
714 #[tokio::test]
715 async fn dispatch_routes_sheets_insert_rows() {
716 let guard = crate::drive::test_support::EnvGuard::take();
717 guard.redirect_api_hosts_to_a_dead_port();
718 let _dir = guard.clear_credentials();
719
720 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
721 command: sheets::SheetsSubcommands::InsertRows(sheets::structure::InsertRowsCommand {
722 target: sheets::structure::SpreadsheetIdArg {
723 spreadsheet_id: "sheet-1".to_string(),
724 },
725 sheet: "Q2".to_string(),
726 at: 5,
727 count: 3,
728 write: no_write_args(),
729 }),
730 });
731 assert!(cmd.dispatch(&dead_client()).await.is_ok());
732 }
733
734 #[tokio::test]
735 async fn dispatch_routes_sheets_insert_columns() {
736 let guard = crate::drive::test_support::EnvGuard::take();
737 guard.redirect_api_hosts_to_a_dead_port();
738 let _dir = guard.clear_credentials();
739
740 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
741 command: sheets::SheetsSubcommands::InsertColumns(
742 sheets::structure::InsertColumnsCommand {
743 target: sheets::structure::SpreadsheetIdArg {
744 spreadsheet_id: "sheet-1".to_string(),
745 },
746 sheet: "Q2".to_string(),
747 at: 2,
748 count: 1,
749 write: no_write_args(),
750 },
751 ),
752 });
753 assert!(cmd.dispatch(&dead_client()).await.is_ok());
754 }
755
756 #[tokio::test]
757 async fn dispatch_routes_sheets_move_rows() {
758 let guard = crate::drive::test_support::EnvGuard::take();
759 guard.redirect_api_hosts_to_a_dead_port();
760 let _dir = guard.clear_credentials();
761
762 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
763 command: sheets::SheetsSubcommands::MoveRows(sheets::structure::MoveRowsCommand {
764 target: sheets::structure::SpreadsheetIdArg {
765 spreadsheet_id: "sheet-1".to_string(),
766 },
767 sheet: "Q2".to_string(),
768 at: 5,
769 count: 3,
770 before: 10,
771 write: no_write_args(),
772 }),
773 });
774 assert!(cmd.dispatch(&dead_client()).await.is_ok());
775 }
776
777 #[tokio::test]
778 async fn dispatch_routes_sheets_move_columns() {
779 let guard = crate::drive::test_support::EnvGuard::take();
780 guard.redirect_api_hosts_to_a_dead_port();
781 let _dir = guard.clear_credentials();
782
783 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
784 command: sheets::SheetsSubcommands::MoveColumns(
785 sheets::structure::MoveColumnsCommand {
786 target: sheets::structure::SpreadsheetIdArg {
787 spreadsheet_id: "sheet-1".to_string(),
788 },
789 sheet: "Q2".to_string(),
790 at: 5,
791 count: 3,
792 before: 10,
793 write: no_write_args(),
794 },
795 ),
796 });
797 assert!(cmd.dispatch(&dead_client()).await.is_ok());
798 }
799
800 #[tokio::test]
801 async fn dispatch_routes_sheets_move_chart() {
802 let guard = crate::drive::test_support::EnvGuard::take();
803 guard.redirect_api_hosts_to_a_dead_port();
804 let _dir = guard.clear_credentials();
805
806 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
807 command: sheets::SheetsSubcommands::MoveChart(
808 sheets::embedded_object::MoveChartCommand {
809 spreadsheet_id: "sheet-1".to_string(),
810 chart_id: 1,
811 sheet: None,
812 anchor: Some("F2".to_string()),
813 offset_x: None,
814 offset_y: None,
815 width: None,
816 height: None,
817 new_sheet: false,
818 write: helpers::StructureWriteArgs {
819 dry_run: false,
820 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
821 output: OutputFormat::Table,
822 },
823 },
824 ),
825 });
826 assert!(cmd.dispatch(&dead_client()).await.is_ok());
827 }
828
829 #[tokio::test]
830 async fn dispatch_routes_sheets_move_slicer() {
831 let guard = crate::drive::test_support::EnvGuard::take();
832 guard.redirect_api_hosts_to_a_dead_port();
833 let _dir = guard.clear_credentials();
834
835 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
836 command: sheets::SheetsSubcommands::MoveSlicer(
837 sheets::embedded_object::MoveSlicerCommand {
838 spreadsheet_id: "sheet-1".to_string(),
839 slicer_id: 1,
840 sheet: None,
841 anchor: Some("F2".to_string()),
842 offset_x: None,
843 offset_y: None,
844 width: None,
845 height: None,
846 write: helpers::StructureWriteArgs {
847 dry_run: false,
848 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
849 output: OutputFormat::Table,
850 },
851 },
852 ),
853 });
854 assert!(cmd.dispatch(&dead_client()).await.is_ok());
855 }
856
857 #[tokio::test]
858 async fn dispatch_routes_sheets_update_chart_border() {
859 let guard = crate::drive::test_support::EnvGuard::take();
860 guard.redirect_api_hosts_to_a_dead_port();
861 let _dir = guard.clear_credentials();
862
863 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
864 command: sheets::SheetsSubcommands::UpdateChartBorder(
865 sheets::embedded_object::UpdateChartBorderCommand {
866 spreadsheet_id: "sheet-1".to_string(),
867 chart_id: 1,
868 color: Some("#4A86E8".to_string()),
869 clear: false,
870 write: helpers::StructureWriteArgs {
871 dry_run: false,
872 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
873 output: OutputFormat::Table,
874 },
875 },
876 ),
877 });
878 assert!(cmd.dispatch(&dead_client()).await.is_ok());
879 }
880
881 #[tokio::test]
882 async fn dispatch_routes_sheets_delete_sheet() {
883 let guard = crate::drive::test_support::EnvGuard::take();
884 guard.redirect_api_hosts_to_a_dead_port();
885 let _dir = guard.clear_credentials();
886
887 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
888 command: sheets::SheetsSubcommands::DeleteSheet(
889 sheets::structure::DeleteSheetCommand {
890 target: sheets::structure::SpreadsheetIdArg {
891 spreadsheet_id: "sheet-1".to_string(),
892 },
893 sheet: "Q2".to_string(),
894 write: no_write_args(),
895 },
896 ),
897 });
898 assert!(cmd.dispatch(&dead_client()).await.is_ok());
901 }
902
903 #[tokio::test]
904 async fn dispatch_routes_sheets_delete_rows() {
905 let guard = crate::drive::test_support::EnvGuard::take();
906 guard.redirect_api_hosts_to_a_dead_port();
907 let _dir = guard.clear_credentials();
908
909 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
910 command: sheets::SheetsSubcommands::DeleteRows(sheets::structure::DeleteRowsCommand {
911 target: sheets::structure::SpreadsheetIdArg {
912 spreadsheet_id: "sheet-1".to_string(),
913 },
914 sheet: "Q2".to_string(),
915 at: 5,
916 count: 3,
917 write: no_write_args(),
918 }),
919 });
920 assert!(cmd.dispatch(&dead_client()).await.is_ok());
921 }
922
923 #[tokio::test]
924 async fn dispatch_routes_sheets_delete_columns() {
925 let guard = crate::drive::test_support::EnvGuard::take();
926 guard.redirect_api_hosts_to_a_dead_port();
927 let _dir = guard.clear_credentials();
928
929 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
930 command: sheets::SheetsSubcommands::DeleteColumns(
931 sheets::structure::DeleteColumnsCommand {
932 target: sheets::structure::SpreadsheetIdArg {
933 spreadsheet_id: "sheet-1".to_string(),
934 },
935 sheet: "Q2".to_string(),
936 at: 2,
937 count: 1,
938 write: no_write_args(),
939 },
940 ),
941 });
942 assert!(cmd.dispatch(&dead_client()).await.is_ok());
943 }
944
945 #[tokio::test]
946 async fn dispatch_routes_sheets_delete_range() {
947 let guard = crate::drive::test_support::EnvGuard::take();
948 guard.redirect_api_hosts_to_a_dead_port();
949 let _dir = guard.clear_credentials();
950
951 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
952 command: sheets::SheetsSubcommands::DeleteRange(
953 sheets::structure::DeleteRangeCommand {
954 target: sheets::structure::SpreadsheetIdArg {
955 spreadsheet_id: "sheet-1".to_string(),
956 },
957 sheet: "Q2".to_string(),
958 range: sheets::structure::GridRangeArgs {
959 start_row: 2,
960 end_row: 4,
961 start_column: 2,
962 end_column: 3,
963 shift: sheets::structure::ShiftArg::Rows,
964 },
965 write: no_write_args(),
966 },
967 ),
968 });
969 assert!(cmd.dispatch(&dead_client()).await.is_ok());
970 }
971
972 #[tokio::test]
973 async fn dispatch_routes_sheets_duplicate_sheet() {
974 let guard = crate::drive::test_support::EnvGuard::take();
975 let _dir = guard.clear_credentials();
976
977 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
978 command: sheets::SheetsSubcommands::DuplicateSheet(
979 sheets::structure::DuplicateSheetCommand {
980 target: sheets::structure::SpreadsheetIdArg {
981 spreadsheet_id: "sheet-1".to_string(),
982 },
983 sheet: "Q2".to_string(),
984 title: Some("Q2 copy".to_string()),
985 index: None,
986 write: no_write_args(),
987 },
988 ),
989 });
990 assert!(cmd.dispatch(&dead_client()).await.is_ok());
991 }
992
993 #[tokio::test]
994 async fn dispatch_routes_sheets_reorder_sheet() {
995 let guard = crate::drive::test_support::EnvGuard::take();
996 let _dir = guard.clear_credentials();
997
998 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
999 command: sheets::SheetsSubcommands::ReorderSheet(
1000 sheets::structure::ReorderSheetCommand {
1001 target: sheets::structure::SpreadsheetIdArg {
1002 spreadsheet_id: "sheet-1".to_string(),
1003 },
1004 sheet: "Q2".to_string(),
1005 index: 0,
1006 write: no_write_args(),
1007 },
1008 ),
1009 });
1010 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1011 }
1012
1013 #[tokio::test]
1014 async fn dispatch_routes_sheets_hide_sheet() {
1015 let guard = crate::drive::test_support::EnvGuard::take();
1016 let _dir = guard.clear_credentials();
1017
1018 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1019 command: sheets::SheetsSubcommands::HideSheet(sheets::structure::HideSheetCommand {
1020 target: sheets::structure::SpreadsheetIdArg {
1021 spreadsheet_id: "sheet-1".to_string(),
1022 },
1023 sheet: "Q2".to_string(),
1024 write: no_write_args(),
1025 }),
1026 });
1027 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1028 }
1029
1030 #[tokio::test]
1031 async fn dispatch_routes_sheets_show_sheet() {
1032 let guard = crate::drive::test_support::EnvGuard::take();
1033 let _dir = guard.clear_credentials();
1034
1035 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1036 command: sheets::SheetsSubcommands::ShowSheet(sheets::structure::ShowSheetCommand {
1037 target: sheets::structure::SpreadsheetIdArg {
1038 spreadsheet_id: "sheet-1".to_string(),
1039 },
1040 sheet: "Q2".to_string(),
1041 write: no_write_args(),
1042 }),
1043 });
1044 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1045 }
1046
1047 #[tokio::test]
1048 async fn dispatch_routes_sheets_update_workbook_properties() {
1049 let guard = crate::drive::test_support::EnvGuard::take();
1050 guard.redirect_api_hosts_to_a_dead_port();
1051 let _dir = guard.clear_credentials();
1052
1053 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1054 command: sheets::SheetsSubcommands::UpdateWorkbookProperties(
1055 sheets::structure::UpdateWorkbookPropertiesCommand {
1056 target: sheets::structure::SpreadsheetIdArg {
1057 spreadsheet_id: "sheet-1".to_string(),
1058 },
1059 locale: Some("en_US".to_string()),
1060 time_zone: None,
1061 auto_recalc: None,
1062 iterative_calculation: None,
1063 iterative_calculation_max_iterations: None,
1064 iterative_calculation_convergence_threshold: None,
1065 write: no_write_args(),
1066 },
1067 ),
1068 });
1069 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1072 }
1073
1074 #[tokio::test]
1075 async fn dispatch_routes_sheets_format_cells() {
1076 let guard = crate::drive::test_support::EnvGuard::take();
1077 let _dir = guard.clear_credentials();
1078
1079 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1080 command: sheets::SheetsSubcommands::FormatCells(sheets::format::FormatCellsCommand {
1081 spreadsheet_id: "sheet-1".to_string(),
1082 range: Some("A1:B2".to_string()),
1083 sheet: None,
1084 bold: Some(true),
1085 italic: None,
1086 strikethrough: None,
1087 underline: None,
1088 font_size: None,
1089 text_color: None,
1090 background: None,
1091 horizontal_align: Some(sheets::format::HorizontalAlign::Center),
1092 vertical_align: Some(sheets::format::VerticalAlign::Middle),
1093 number_format: Some("#,##0.00".to_string()),
1094 number_format_type: Some(sheets::format::NumberFormatType::Currency),
1095 wrap: Some(sheets::format::WrapStrategy::Wrap),
1096 font_family: None,
1097 text_rotation_angle: None,
1098 text_rotation_vertical: false,
1099 hyperlink_display_type: None,
1100 padding_top: None,
1101 padding_right: None,
1102 padding_bottom: None,
1103 padding_left: None,
1104 text_direction: None,
1105 write: helpers::StructureWriteArgs {
1106 dry_run: false,
1107 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1108 output: OutputFormat::Table,
1109 },
1110 }),
1111 });
1112 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1113 }
1114
1115 fn base_format_cells_command() -> sheets::format::FormatCellsCommand {
1116 sheets::format::FormatCellsCommand {
1117 spreadsheet_id: "sheet-1".to_string(),
1118 range: Some("A1:B2".to_string()),
1119 sheet: None,
1120 bold: None,
1121 italic: None,
1122 strikethrough: None,
1123 underline: None,
1124 font_size: None,
1125 text_color: None,
1126 background: None,
1127 horizontal_align: None,
1128 vertical_align: None,
1129 number_format: None,
1130 number_format_type: None,
1131 wrap: None,
1132 font_family: None,
1133 text_rotation_angle: None,
1134 text_rotation_vertical: false,
1135 hyperlink_display_type: None,
1136 padding_top: None,
1137 padding_right: None,
1138 padding_bottom: None,
1139 padding_left: None,
1140 text_direction: None,
1141 write: helpers::StructureWriteArgs {
1142 dry_run: false,
1143 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1144 output: OutputFormat::Table,
1145 },
1146 }
1147 }
1148
1149 #[tokio::test]
1150 async fn dispatch_routes_sheets_format_cells_text_rotation_angle() {
1151 let guard = crate::drive::test_support::EnvGuard::take();
1152 let _dir = guard.clear_credentials();
1153
1154 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1155 command: sheets::SheetsSubcommands::FormatCells(sheets::format::FormatCellsCommand {
1156 text_rotation_angle: Some(45),
1157 ..base_format_cells_command()
1158 }),
1159 });
1160 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1161 }
1162
1163 #[tokio::test]
1164 async fn dispatch_routes_sheets_format_cells_text_rotation_vertical() {
1165 let guard = crate::drive::test_support::EnvGuard::take();
1166 let _dir = guard.clear_credentials();
1167
1168 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1169 command: sheets::SheetsSubcommands::FormatCells(sheets::format::FormatCellsCommand {
1170 text_rotation_vertical: true,
1171 ..base_format_cells_command()
1172 }),
1173 });
1174 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1175 }
1176
1177 #[tokio::test]
1178 async fn dispatch_routes_sheets_update_borders() {
1179 let guard = crate::drive::test_support::EnvGuard::take();
1180 let _dir = guard.clear_credentials();
1181
1182 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1183 command: sheets::SheetsSubcommands::UpdateBorders(
1184 sheets::format::UpdateBordersCommand {
1185 spreadsheet_id: "sheet-1".to_string(),
1186 range: Some("A1:B2".to_string()),
1187 sheet: None,
1188 top: true,
1189 bottom: false,
1190 left: false,
1191 right: false,
1192 all: false,
1193 inner_horizontal: false,
1194 inner_vertical: false,
1195 style: sheets::format::BorderStyle::Dashed,
1196 color: Some("#FF0000".to_string()),
1197 write: helpers::StructureWriteArgs {
1198 dry_run: false,
1199 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1200 output: OutputFormat::Table,
1201 },
1202 },
1203 ),
1204 });
1205 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1206 }
1207
1208 #[tokio::test]
1209 async fn dispatch_routes_sheets_merge_cells() {
1210 let guard = crate::drive::test_support::EnvGuard::take();
1211 let _dir = guard.clear_credentials();
1212
1213 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1214 command: sheets::SheetsSubcommands::MergeCells(sheets::format::MergeCellsCommand {
1215 spreadsheet_id: "sheet-1".to_string(),
1216 range: Some("A1:B2".to_string()),
1217 sheet: None,
1218 r#type: sheets::format::MergeType::Rows,
1219 dry_run: false,
1220 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1221 output: OutputFormat::Table,
1222 }),
1223 });
1224 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1225 }
1226
1227 #[tokio::test]
1228 async fn dispatch_routes_sheets_unmerge_cells() {
1229 let guard = crate::drive::test_support::EnvGuard::take();
1230 let _dir = guard.clear_credentials();
1231
1232 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1233 command: sheets::SheetsSubcommands::UnmergeCells(sheets::format::UnmergeCellsCommand {
1234 spreadsheet_id: "sheet-1".to_string(),
1235 range: Some("A1:B2".to_string()),
1236 sheet: None,
1237 write: helpers::StructureWriteArgs {
1238 dry_run: false,
1239 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1240 output: OutputFormat::Table,
1241 },
1242 }),
1243 });
1244 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1245 }
1246
1247 #[tokio::test]
1248 async fn dispatch_routes_sheets_auto_resize_dimension() {
1249 let guard = crate::drive::test_support::EnvGuard::take();
1250 let _dir = guard.clear_credentials();
1251
1252 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1253 command: sheets::SheetsSubcommands::AutoResizeDimension(
1254 sheets::format::AutoResizeDimensionCommand {
1255 spreadsheet_id: "sheet-1".to_string(),
1256 sheet: "Q2".to_string(),
1257 dimension: sheets::format::DimensionArg::Columns,
1258 start: 1,
1259 end: 3,
1260 write: helpers::StructureWriteArgs {
1261 dry_run: false,
1262 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1263 output: OutputFormat::Table,
1264 },
1265 },
1266 ),
1267 });
1268 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1269 }
1270
1271 #[tokio::test]
1272 async fn dispatch_routes_sheets_update_dimension_properties() {
1273 let guard = crate::drive::test_support::EnvGuard::take();
1274 let _dir = guard.clear_credentials();
1275
1276 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1277 command: sheets::SheetsSubcommands::UpdateDimensionProperties(
1278 sheets::format::UpdateDimensionPropertiesCommand {
1279 spreadsheet_id: "sheet-1".to_string(),
1280 sheet: "Q2".to_string(),
1281 dimension: sheets::format::DimensionArg::Rows,
1282 start: 1,
1283 end: 3,
1284 pixel_size: 42,
1285 write: helpers::StructureWriteArgs {
1286 dry_run: false,
1287 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1288 output: OutputFormat::Table,
1289 },
1290 },
1291 ),
1292 });
1293 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1294 }
1295
1296 #[tokio::test]
1297 async fn dispatch_routes_sheets_set_data_validation() {
1298 let guard = crate::drive::test_support::EnvGuard::take();
1299 let _dir = guard.clear_credentials();
1300
1301 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1302 command: sheets::SheetsSubcommands::SetDataValidation(Box::new(
1303 sheets::validation::SetDataValidationCommand {
1304 spreadsheet_id: "sheet-1".to_string(),
1305 range: Some("A1:A10".to_string()),
1306 sheet: None,
1307 one_of_list: Some(vec!["yes".to_string(), "no".to_string()]),
1308 one_of_range: None,
1309 number_between: None,
1310 number_not_between: None,
1311 number_greater: None,
1312 number_greater_eq: None,
1313 number_less: None,
1314 number_less_eq: None,
1315 number_eq: None,
1316 number_not_eq: None,
1317 text_contains: None,
1318 text_not_contains: None,
1319 text_starts_with: None,
1320 text_ends_with: None,
1321 text_eq: None,
1322 date_after: None,
1323 date_before: None,
1324 date_on: None,
1325 date_between: None,
1326 blank: false,
1327 not_blank: false,
1328 checkbox: false,
1329 custom_formula: None,
1330 input_message: Some("Pick one".to_string()),
1331 show_warning: false,
1332 write: helpers::StructureWriteArgs {
1333 dry_run: false,
1334 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1335 output: OutputFormat::Table,
1336 },
1337 },
1338 )),
1339 });
1340 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1341 }
1342
1343 #[tokio::test]
1344 async fn dispatch_routes_sheets_clear_data_validation() {
1345 let guard = crate::drive::test_support::EnvGuard::take();
1346 let _dir = guard.clear_credentials();
1347
1348 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1349 command: sheets::SheetsSubcommands::ClearDataValidation(
1350 sheets::validation::ClearDataValidationCommand {
1351 spreadsheet_id: "sheet-1".to_string(),
1352 range: Some("A1:A10".to_string()),
1353 sheet: None,
1354 write: helpers::StructureWriteArgs {
1355 dry_run: false,
1356 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1357 output: OutputFormat::Table,
1358 },
1359 },
1360 ),
1361 });
1362 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1363 }
1364
1365 #[tokio::test]
1366 async fn dispatch_routes_sheets_set_developer_metadata() {
1367 let guard = crate::drive::test_support::EnvGuard::take();
1368 let _dir = guard.clear_credentials();
1369
1370 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1371 command: sheets::SheetsSubcommands::SetDeveloperMetadata(
1372 sheets::developer_metadata::SetDeveloperMetadataCommand {
1373 spreadsheet_id: "sheet-1".to_string(),
1374 key: "owner".to_string(),
1375 value: "team-a".to_string(),
1376 sheet: None,
1377 dimension: None,
1378 start: None,
1379 end: None,
1380 write: helpers::StructureWriteArgs {
1381 dry_run: false,
1382 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1383 output: OutputFormat::Table,
1384 },
1385 },
1386 ),
1387 });
1388 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1392 }
1393
1394 #[tokio::test]
1395 async fn dispatch_routes_sheets_delete_developer_metadata() {
1396 let guard = crate::drive::test_support::EnvGuard::take();
1397 let _dir = guard.clear_credentials();
1398
1399 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1400 command: sheets::SheetsSubcommands::DeleteDeveloperMetadata(
1401 sheets::developer_metadata::DeleteDeveloperMetadataCommand {
1402 spreadsheet_id: "sheet-1".to_string(),
1403 key: "owner".to_string(),
1404 sheet: None,
1405 dimension: None,
1406 start: None,
1407 end: None,
1408 dry_run: false,
1409 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1410 output: OutputFormat::Table,
1411 },
1412 ),
1413 });
1414 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1415 }
1416
1417 #[tokio::test]
1418 async fn dispatch_routes_sheets_search_developer_metadata() {
1419 let guard = crate::drive::test_support::EnvGuard::take();
1423 guard.redirect_api_hosts_to_a_dead_port();
1424
1425 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1426 command: sheets::SheetsSubcommands::SearchDeveloperMetadata(
1427 sheets::developer_metadata::SearchDeveloperMetadataCommand {
1428 spreadsheet_id: "sheet-1".to_string(),
1429 key: None,
1430 sheet: None,
1431 dimension: None,
1432 start: None,
1433 end: None,
1434 output: OutputFormat::Table,
1435 },
1436 ),
1437 });
1438 assert!(cmd.dispatch(&dead_client()).await.is_err());
1439 }
1440
1441 #[tokio::test]
1442 async fn dispatch_routes_sheets_protect_range() {
1443 let guard = crate::drive::test_support::EnvGuard::take();
1444 let _dir = guard.clear_credentials();
1445
1446 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1447 command: sheets::SheetsSubcommands::ProtectRange(
1448 sheets::protection::ProtectRangeCommand {
1449 spreadsheet_id: "sheet-1".to_string(),
1450 range: Some("A1:B2".to_string()),
1451 sheet: None,
1452 whole_sheet: false,
1453 description: Some("locked".to_string()),
1454 warning_only: false,
1455 editors: vec!["alice@example.com".to_string()],
1456 write: helpers::StructureWriteArgs {
1457 dry_run: false,
1458 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1459 output: OutputFormat::Table,
1460 },
1461 },
1462 ),
1463 });
1464 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1465 }
1466
1467 #[tokio::test]
1468 async fn dispatch_routes_sheets_update_protection() {
1469 let guard = crate::drive::test_support::EnvGuard::take();
1470 let _dir = guard.clear_credentials();
1471
1472 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1473 command: sheets::SheetsSubcommands::UpdateProtection(
1474 sheets::protection::UpdateProtectionCommand {
1475 spreadsheet_id: "sheet-1".to_string(),
1476 range: Some("A1:B2".to_string()),
1477 sheet: None,
1478 whole_sheet: false,
1479 description: Some("still locked".to_string()),
1480 warning_only: Some(true),
1481 add_editors: vec!["bob@example.com".to_string()],
1482 remove_editors: vec!["alice@example.com".to_string()],
1483 write: helpers::StructureWriteArgs {
1484 dry_run: false,
1485 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1486 output: OutputFormat::Table,
1487 },
1488 },
1489 ),
1490 });
1491 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1492 }
1493
1494 #[tokio::test]
1495 async fn dispatch_routes_sheets_unprotect_range() {
1496 let guard = crate::drive::test_support::EnvGuard::take();
1497 let _dir = guard.clear_credentials();
1498
1499 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1500 command: sheets::SheetsSubcommands::UnprotectRange(
1501 sheets::protection::UnprotectRangeCommand {
1502 spreadsheet_id: "sheet-1".to_string(),
1503 range: Some("A1:B2".to_string()),
1504 sheet: None,
1505 whole_sheet: false,
1506 write: helpers::StructureWriteArgs {
1507 dry_run: false,
1508 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1509 output: OutputFormat::Table,
1510 },
1511 },
1512 ),
1513 });
1514 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1515 }
1516
1517 #[tokio::test]
1518 async fn dispatch_routes_sheets_list_protections() {
1519 let guard = crate::drive::test_support::EnvGuard::take();
1523 guard.redirect_api_hosts_to_a_dead_port();
1524
1525 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1526 command: sheets::SheetsSubcommands::ListProtections(
1527 sheets::protection::ListProtectionsCommand {
1528 spreadsheet_id: "sheet-1".to_string(),
1529 output: OutputFormat::Table,
1530 },
1531 ),
1532 });
1533 assert!(cmd.dispatch(&dead_client()).await.is_err());
1534 }
1535
1536 fn cell_empty_only_rule_args() -> sheets::conditional_format::ConditionalFormatRuleArgs {
1543 sheets::conditional_format::ConditionalFormatRuleArgs {
1544 number_between: None,
1545 number_not_between: None,
1546 number_greater: None,
1547 number_greater_eq: None,
1548 number_less: None,
1549 number_less_eq: None,
1550 number_eq: None,
1551 number_not_eq: None,
1552 text_contains: None,
1553 text_not_contains: None,
1554 text_starts_with: None,
1555 text_ends_with: None,
1556 text_eq: None,
1557 date_after: None,
1558 date_before: None,
1559 date_on: None,
1560 date_between: None,
1561 cell_empty: true,
1562 cell_not_empty: false,
1563 custom_formula: None,
1564 background: None,
1565 text_color: None,
1566 bold: None,
1567 gradient_min_color: None,
1568 gradient_max_color: None,
1569 gradient_mid_color: None,
1570 gradient_mid_type: None,
1571 gradient_mid_value: None,
1572 }
1573 }
1574
1575 #[tokio::test]
1576 async fn dispatch_routes_sheets_add_conditional_format() {
1577 let guard = crate::drive::test_support::EnvGuard::take();
1578 let _dir = guard.clear_credentials();
1579
1580 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1581 command: sheets::SheetsSubcommands::AddConditionalFormat(Box::new(
1582 sheets::conditional_format::AddConditionalFormatCommand {
1583 spreadsheet_id: "sheet-1".to_string(),
1584 sheet: "Q1".to_string(),
1585 ranges: vec!["A1:A10".to_string()],
1586 index: None,
1587 rule: cell_empty_only_rule_args(),
1588 write: helpers::StructureWriteArgs {
1589 dry_run: false,
1590 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1591 output: OutputFormat::Table,
1592 },
1593 },
1594 )),
1595 });
1596 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1597 }
1598
1599 #[tokio::test]
1600 async fn dispatch_routes_sheets_update_conditional_format() {
1601 let guard = crate::drive::test_support::EnvGuard::take();
1602 let _dir = guard.clear_credentials();
1603
1604 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1605 command: sheets::SheetsSubcommands::UpdateConditionalFormat(Box::new(
1606 sheets::conditional_format::UpdateConditionalFormatCommand {
1607 spreadsheet_id: "sheet-1".to_string(),
1608 sheet: "Q1".to_string(),
1609 index: 0,
1610 ranges: vec!["A1:A10".to_string()],
1611 rule: cell_empty_only_rule_args(),
1612 dry_run: false,
1613 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1614 output: OutputFormat::Table,
1615 },
1616 )),
1617 });
1618 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1619 }
1620
1621 #[tokio::test]
1622 async fn dispatch_routes_sheets_delete_conditional_format() {
1623 let guard = crate::drive::test_support::EnvGuard::take();
1624 let _dir = guard.clear_credentials();
1625
1626 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1627 command: sheets::SheetsSubcommands::DeleteConditionalFormat(
1628 sheets::conditional_format::DeleteConditionalFormatCommand {
1629 spreadsheet_id: "sheet-1".to_string(),
1630 sheet: "Q1".to_string(),
1631 index: 0,
1632 dry_run: false,
1633 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1634 output: OutputFormat::Table,
1635 },
1636 ),
1637 });
1638 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1639 }
1640
1641 #[tokio::test]
1642 async fn dispatch_routes_sheets_list_conditional_formats() {
1643 let guard = crate::drive::test_support::EnvGuard::take();
1647 guard.redirect_api_hosts_to_a_dead_port();
1648
1649 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1650 command: sheets::SheetsSubcommands::ListConditionalFormats(
1651 sheets::conditional_format::ListConditionalFormatsCommand {
1652 spreadsheet_id: "sheet-1".to_string(),
1653 output: OutputFormat::Table,
1654 },
1655 ),
1656 });
1657 assert!(cmd.dispatch(&dead_client()).await.is_err());
1658 }
1659
1660 #[tokio::test]
1661 async fn dispatch_routes_sheets_set_basic_filter() {
1662 let guard = crate::drive::test_support::EnvGuard::take();
1663 let _dir = guard.clear_credentials();
1664
1665 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1666 command: sheets::SheetsSubcommands::SetBasicFilter(
1667 sheets::filter::SetBasicFilterCommand {
1668 spreadsheet_id: "sheet-1".to_string(),
1669 sheet: "Q1".to_string(),
1670 range: "A1:D10".to_string(),
1671 sort_by: vec!["0:asc".to_string()],
1672 hide_values: vec!["1:Closed".to_string()],
1673 write: helpers::StructureWriteArgs {
1674 dry_run: false,
1675 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1676 output: OutputFormat::Table,
1677 },
1678 },
1679 ),
1680 });
1681 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1682 }
1683
1684 #[tokio::test]
1685 async fn dispatch_routes_sheets_clear_basic_filter() {
1686 let guard = crate::drive::test_support::EnvGuard::take();
1687 let _dir = guard.clear_credentials();
1688
1689 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1690 command: sheets::SheetsSubcommands::ClearBasicFilter(
1691 sheets::filter::ClearBasicFilterCommand {
1692 spreadsheet_id: "sheet-1".to_string(),
1693 sheet: "Q1".to_string(),
1694 write: helpers::StructureWriteArgs {
1695 dry_run: false,
1696 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1697 output: OutputFormat::Table,
1698 },
1699 },
1700 ),
1701 });
1702 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1703 }
1704
1705 #[tokio::test]
1706 async fn dispatch_routes_sheets_add_filter_view() {
1707 let guard = crate::drive::test_support::EnvGuard::take();
1708 let _dir = guard.clear_credentials();
1709
1710 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1711 command: sheets::SheetsSubcommands::AddFilterView(
1712 sheets::filter::AddFilterViewCommand {
1713 spreadsheet_id: "sheet-1".to_string(),
1714 sheet: "Q1".to_string(),
1715 range: "A1:D10".to_string(),
1716 title: Some("Open only".to_string()),
1717 sort_by: Vec::new(),
1718 hide_values: Vec::new(),
1719 write: helpers::StructureWriteArgs {
1720 dry_run: false,
1721 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1722 output: OutputFormat::Table,
1723 },
1724 },
1725 ),
1726 });
1727 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1728 }
1729
1730 #[tokio::test]
1731 async fn dispatch_routes_sheets_update_filter_view() {
1732 let guard = crate::drive::test_support::EnvGuard::take();
1733 let _dir = guard.clear_credentials();
1734
1735 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1736 command: sheets::SheetsSubcommands::UpdateFilterView(
1737 sheets::filter::UpdateFilterViewCommand {
1738 spreadsheet_id: "sheet-1".to_string(),
1739 filter_view_id: 7,
1740 sheet: None,
1741 range: None,
1742 title: Some("Renamed".to_string()),
1743 sort_by: Vec::new(),
1744 hide_values: Vec::new(),
1745 clear_sort: false,
1746 clear_criteria: false,
1747 write: helpers::StructureWriteArgs {
1748 dry_run: false,
1749 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1750 output: OutputFormat::Table,
1751 },
1752 },
1753 ),
1754 });
1755 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1756 }
1757
1758 #[tokio::test]
1759 async fn dispatch_routes_sheets_delete_filter_view() {
1760 let guard = crate::drive::test_support::EnvGuard::take();
1761 let _dir = guard.clear_credentials();
1762
1763 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1764 command: sheets::SheetsSubcommands::DeleteFilterView(
1765 sheets::filter::DeleteFilterViewCommand {
1766 spreadsheet_id: "sheet-1".to_string(),
1767 filter_view_id: 7,
1768 write: helpers::StructureWriteArgs {
1769 dry_run: false,
1770 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1771 output: OutputFormat::Table,
1772 },
1773 },
1774 ),
1775 });
1776 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1777 }
1778
1779 #[tokio::test]
1780 async fn dispatch_routes_sheets_list_filter_views() {
1781 let guard = crate::drive::test_support::EnvGuard::take();
1785 guard.redirect_api_hosts_to_a_dead_port();
1786
1787 let cmd = DriveSubcommands::Sheets(sheets::SheetsCommand {
1788 command: sheets::SheetsSubcommands::ListFilterViews(
1789 sheets::filter::ListFilterViewsCommand {
1790 spreadsheet_id: "sheet-1".to_string(),
1791 output: OutputFormat::Table,
1792 },
1793 ),
1794 });
1795 assert!(cmd.dispatch(&dead_client()).await.is_err());
1796 }
1797
1798 #[tokio::test]
1799 async fn dispatch_routes_search() {
1800 let cmd = DriveSubcommands::Search(search::SearchCommand {
1801 query: "name contains 'x'".to_string(),
1802 limit: 10,
1803 output: OutputFormat::Table,
1804 });
1805 assert!(cmd.dispatch(&dead_client()).await.is_err());
1806 }
1807
1808 #[tokio::test]
1809 async fn dispatch_routes_read() {
1810 let cmd = DriveSubcommands::Read(read::ReadCommand {
1811 file_id: "f1".to_string(),
1812 content: false,
1813 export_mime_type: None,
1814 out_file: None,
1815 verify: false,
1816 output: OutputFormat::Table,
1817 });
1818 assert!(cmd.dispatch(&dead_client()).await.is_err());
1819 }
1820
1821 #[tokio::test]
1822 async fn dispatch_routes_dedupe() {
1823 let cmd = DriveSubcommands::Dedupe(dedupe::DedupeCommand {
1824 query: "name contains 'x'".to_string(),
1825 limit: 10,
1826 output: OutputFormat::Table,
1827 });
1828 assert!(cmd.dispatch(&dead_client()).await.is_err());
1829 }
1830
1831 #[tokio::test]
1832 async fn dispatch_routes_create() {
1833 let guard = crate::drive::test_support::EnvGuard::take();
1844 let _dir = guard.clear_credentials();
1845
1846 let cmd = DriveSubcommands::Create(create::CreateCommand {
1847 name: "New File".to_string(),
1848 parent: "parent-1".to_string(),
1849 folder: false,
1850 mime_type: None,
1851 dry_run: false,
1852 output: OutputFormat::Table,
1853 });
1854 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1855 }
1856
1857 #[tokio::test]
1858 async fn dispatch_routes_upload() {
1859 let guard = crate::drive::test_support::EnvGuard::take();
1862 let _dir = guard.clear_credentials();
1863 let content_dir = tempfile::tempdir().unwrap();
1864 let local_path = content_dir.path().join("upload-me.txt");
1865 std::fs::write(&local_path, b"content").unwrap();
1866
1867 let cmd = DriveSubcommands::Upload(upload::UploadCommand {
1868 local_path,
1869 parent: "parent-1".to_string(),
1870 name: None,
1871 mime_type: None,
1872 dry_run: false,
1873 output: OutputFormat::Table,
1874 });
1875 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1876 }
1877
1878 #[tokio::test]
1879 async fn dispatch_routes_edit() {
1880 let guard = crate::drive::test_support::EnvGuard::take();
1883 let _dir = guard.clear_credentials();
1884 let content_dir = tempfile::tempdir().unwrap();
1885 let content_path = content_dir.path().join("new-content.txt");
1886 std::fs::write(&content_path, b"content").unwrap();
1887
1888 let cmd = DriveSubcommands::Edit(edit::EditCommand {
1889 file_id: "f1".to_string(),
1890 content: content_path.to_str().unwrap().to_string(),
1891 mime_type: None,
1892 dry_run: false,
1893 lease: crate::cli::drive::helpers::LeaseTokenArg { lease: None },
1894 output: OutputFormat::Table,
1895 });
1896 assert!(cmd.dispatch(&dead_client()).await.is_ok());
1897 }
1898
1899 #[tokio::test]
1900 async fn dispatch_routes_rename() {
1901 let cmd = DriveSubcommands::Rename(rename::RenameCommand {
1902 file_id: "f1".to_string(),
1903 new_name: "New Name".to_string(),
1904 dry_run: false,
1905 output: OutputFormat::Table,
1906 });
1907 assert!(cmd.dispatch(&dead_client()).await.is_err());
1908 }
1909
1910 #[tokio::test]
1911 async fn dispatch_routes_move() {
1912 let cmd = DriveSubcommands::Move(move_file::MoveCommand {
1913 file_ids: vec!["f1".to_string()],
1914 to: "dest1".to_string(),
1915 allow_visibility_increase: false,
1916 allow_visibility_decrease: false,
1917 allow_drive_boundary_crossing: false,
1918 dry_run: false,
1919 output: OutputFormat::Table,
1920 });
1921 assert!(cmd.dispatch(&dead_client()).await.is_err());
1922 }
1923}