@@ -473,6 +473,180 @@ func TestAuthAddCmd_SheetsDriveScopeFile(t *testing.T) {
473473 }
474474}
475475
476+ func TestAuthAddCmd_RemoteStep1_PrintsAuthURL (t * testing.T ) {
477+ origManualURL := manualAuthURL
478+ origAuth := authorizeGoogle
479+ origKeychain := ensureKeychainAccess
480+ t .Cleanup (func () {
481+ manualAuthURL = origManualURL
482+ authorizeGoogle = origAuth
483+ ensureKeychainAccess = origKeychain
484+ })
485+
486+ manualCalled := false
487+ manualAuthURL = func (context.Context , googleauth.AuthorizeOptions ) (googleauth.ManualAuthURLResult , error ) {
488+ manualCalled = true
489+ return googleauth.ManualAuthURLResult {
490+ URL : "https://example.com/auth" ,
491+ StateReused : true ,
492+ }, nil
493+ }
494+ authorizeGoogle = func (context.Context , googleauth.AuthorizeOptions ) (string , error ) {
495+ t .Fatal ("authorizeGoogle should not be called in remote step 1" )
496+ return "" , nil
497+ }
498+ ensureKeychainAccess = func () error {
499+ t .Fatal ("keychain access should not be checked in remote step 1" )
500+ return nil
501+ }
502+
503+ out := captureStdout (t , func () {
504+ _ = captureStderr (t , func () {
505+ if err := Execute ([]string {
506+ "auth" ,
507+ "add" ,
508+ "user@example.com" ,
509+ "--services" ,
510+ "gmail" ,
511+ "--remote" ,
512+ "--step" ,
513+ "1" ,
514+ }); err != nil {
515+ t .Fatalf ("Execute: %v" , err )
516+ }
517+ })
518+ })
519+
520+ if ! manualCalled {
521+ t .Fatalf ("expected manualAuthURL to be called" )
522+ }
523+ if ! strings .Contains (out , "auth_url\t https://example.com/auth" ) {
524+ t .Fatalf ("unexpected output: %q" , out )
525+ }
526+ if ! strings .Contains (out , "state_reused\t true" ) {
527+ t .Fatalf ("expected state_reused output, got: %q" , out )
528+ }
529+ }
530+
531+ func TestAuthAddCmd_RemoteStep2_RejectsAuthCode (t * testing.T ) {
532+ err := Execute ([]string {
533+ "auth" ,
534+ "add" ,
535+ "user@example.com" ,
536+ "--services" ,
537+ "gmail" ,
538+ "--remote" ,
539+ "--step" ,
540+ "2" ,
541+ "--auth-code" ,
542+ "abc123" ,
543+ })
544+ if err == nil {
545+ t .Fatalf ("expected error" )
546+ }
547+ var ee * ExitError
548+ if ! errors .As (err , & ee ) || ee .Code != 2 {
549+ t .Fatalf ("expected exit code 2, got %T %#v" , err , err )
550+ }
551+ if ! strings .Contains (err .Error (), "--auth-code is not valid with --remote" ) {
552+ t .Fatalf ("unexpected error: %v" , err )
553+ }
554+ }
555+
556+ func TestAuthAddCmd_RemoteStep2_PassesAuthURL (t * testing.T ) {
557+ origAuth := authorizeGoogle
558+ origOpen := openSecretsStore
559+ origKeychain := ensureKeychainAccess
560+ origFetch := fetchAuthorizedEmail
561+ t .Cleanup (func () {
562+ authorizeGoogle = origAuth
563+ openSecretsStore = origOpen
564+ ensureKeychainAccess = origKeychain
565+ fetchAuthorizedEmail = origFetch
566+ })
567+
568+ ensureKeychainAccess = func () error { return nil }
569+ openSecretsStore = func () (secrets.Store , error ) { return newMemSecretsStore (), nil }
570+
571+ var gotOpts googleauth.AuthorizeOptions
572+ authorizeGoogle = func (ctx context.Context , opts googleauth.AuthorizeOptions ) (string , error ) {
573+ gotOpts = opts
574+ return "rt" , nil
575+ }
576+ fetchAuthorizedEmail = func (context.Context , string , string , []string , time.Duration ) (string , error ) {
577+ return "user@example.com" , nil
578+ }
579+
580+ if err := Execute ([]string {
581+ "auth" ,
582+ "add" ,
583+ "user@example.com" ,
584+ "--services" ,
585+ "gmail" ,
586+ "--remote" ,
587+ "--step" ,
588+ "2" ,
589+ "--auth-url" ,
590+ "http://localhost:1/?code=abc&state=state123" ,
591+ }); err != nil {
592+ t .Fatalf ("Execute: %v" , err )
593+ }
594+
595+ if ! gotOpts .Manual {
596+ t .Fatalf ("expected manual auth in remote step 2" )
597+ }
598+ if ! gotOpts .RequireState {
599+ t .Fatalf ("expected require state in remote step 2" )
600+ }
601+ if gotOpts .AuthURL == "" {
602+ t .Fatalf ("expected auth URL to be passed through" )
603+ }
604+ }
605+
606+ func TestAuthAddCmd_AuthCode_PassesThrough (t * testing.T ) {
607+ origAuth := authorizeGoogle
608+ origOpen := openSecretsStore
609+ origKeychain := ensureKeychainAccess
610+ origFetch := fetchAuthorizedEmail
611+ t .Cleanup (func () {
612+ authorizeGoogle = origAuth
613+ openSecretsStore = origOpen
614+ ensureKeychainAccess = origKeychain
615+ fetchAuthorizedEmail = origFetch
616+ })
617+
618+ ensureKeychainAccess = func () error { return nil }
619+ openSecretsStore = func () (secrets.Store , error ) { return newMemSecretsStore (), nil }
620+
621+ var gotOpts googleauth.AuthorizeOptions
622+ authorizeGoogle = func (ctx context.Context , opts googleauth.AuthorizeOptions ) (string , error ) {
623+ gotOpts = opts
624+ return "rt" , nil
625+ }
626+ fetchAuthorizedEmail = func (context.Context , string , string , []string , time.Duration ) (string , error ) {
627+ return "user@example.com" , nil
628+ }
629+
630+ if err := Execute ([]string {
631+ "auth" ,
632+ "add" ,
633+ "user@example.com" ,
634+ "--services" ,
635+ "gmail" ,
636+ "--auth-code" ,
637+ "abc123" ,
638+ }); err != nil {
639+ t .Fatalf ("Execute: %v" , err )
640+ }
641+
642+ if ! gotOpts .Manual {
643+ t .Fatalf ("expected manual auth when auth-code is provided" )
644+ }
645+ if gotOpts .AuthCode != "abc123" {
646+ t .Fatalf ("expected auth-code to be passed through, got %q" , gotOpts .AuthCode )
647+ }
648+ }
649+
476650func containsStringInSlice (items []string , want string ) bool {
477651 for _ , it := range items {
478652 if it == want {
0 commit comments