diff --git a/internal/cmd/initcmd/init.go b/internal/cmd/initcmd/init.go index 04fba2e..0d7143c 100644 --- a/internal/cmd/initcmd/init.go +++ b/internal/cmd/initcmd/init.go @@ -72,6 +72,7 @@ func run(command *cobra.Command, dependencies Dependencies, flags Setup, nonInte if err != nil { return exitcode.New(exitcode.Config, err) } + configuredBackend := strings.TrimSpace(configValue.Keyring.Backend) setup := Setup{ ClientID: configValue.ClientID, RedirectURI: configValue.RedirectURI, CredentialRef: configValue.CredentialRef, Backend: configValue.Keyring.Backend, @@ -89,11 +90,12 @@ func run(command *cobra.Command, dependencies Dependencies, flags Setup, nonInte backendSet := backendFlag != nil && backendFlag.Changed runtimeBackend := pointerValue(dependencies.Backend) runtimeBackendSet := backendSet + interactive := dependencies.Interactive && !nonInteractive if backendSet { setup.Backend = runtimeBackend } - if dependencies.Interactive && !nonInteractive { + if interactive { prompt := dependencies.Prompt if prompt == nil { prompt = func(value *Setup) error { return RunPrompt(command.InOrStdin(), command.ErrOrStderr(), value) } @@ -127,7 +129,7 @@ func run(command *cobra.Command, dependencies Dependencies, flags Setup, nonInte result, err := dependencies.Initializer.Initialize(command.Context(), InitializeOptions{ Config: configValue, Profile: profile, Backend: runtimeBackend, BackendSet: runtimeBackendSet, - Overwrite: overwrite, Verify: !noVerify, + Overwrite: overwrite || (interactive && strings.TrimSpace(setup.Backend) != configuredBackend), Verify: !noVerify, Authorization: auth.Request{ ClientID: configValue.ClientID, RedirectURI: configValue.RedirectURI, NoBrowser: noBrowser || authCodeStdin, AuthCodeStdin: authCodeStdin, diff --git a/internal/cmd/initcmd/init_test.go b/internal/cmd/initcmd/init_test.go index 7146608..9df7984 100644 --- a/internal/cmd/initcmd/init_test.go +++ b/internal/cmd/initcmd/init_test.go @@ -120,6 +120,43 @@ func TestInteractiveInitUsesPromptedBackendForStoreAndConfig(t *testing.T) { } } +func TestInteractiveInitBackendChangeReplacesDestinationCredential(t *testing.T) { + harness := newInitHarness(t) + cfg := config.Default() + cfg.Keyring.Backend = "file" + if err := config.Save(harness.scope, cfg); err != nil { + t.Fatal(err) + } + key := "default/" + credentials.OAuthTokenKey + harness.store.values[key] = "stale-destination-credential" + harness.interactive = true + harness.prompt = func(setup *Setup) error { + setup.ClientID = "client-id" + setup.Backend = "keychain" + return nil + } + if err := harness.execute("--no-verify"); err != nil { + t.Fatal(err) + } + if harness.store.values[key] == "stale-destination-credential" || harness.store.setCalls != 1 { + t.Fatalf("credential = %q, set calls = %d", harness.store.values[key], harness.store.setCalls) + } +} + +func TestNonInteractiveInitBackendChangeStillRequiresOverwrite(t *testing.T) { + harness := newInitHarness(t) + cfg := config.Default() + cfg.Keyring.Backend = "file" + if err := config.Save(harness.scope, cfg); err != nil { + t.Fatal(err) + } + harness.store.values["default/"+credentials.OAuthTokenKey] = "old-secret" + err := harness.execute("--backend", "keychain", "--non-interactive", "--client-id", "client-id", "--no-verify") + if !errors.Is(err, credstore.ErrExists) { + t.Fatalf("error = %v", err) + } +} + func TestNonInteractiveInitNamesMissingClientID(t *testing.T) { harness := newInitHarness(t) called := false