Ctrl K
ComunidadGuías…

cli/exp_update_user_email_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cli_test

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/coder/coder/v2/cli/clitest"
	"github.com/coder/coder/v2/cli/cliui"
	"github.com/coder/coder/v2/coderd/coderdtest"
	"github.com/coder/coder/v2/codersdk"
	"github.com/coder/coder/v2/testutil"
	"github.com/coder/serpent"
)

func TestUpdateUserEmail(t *testing.T) {
	t.Parallel()

	t.Run("CommandReachable", func(t *testing.T) {
		t.Parallel()

		root := getRoot(t)
		var found *serpent.Command
		root.Walk(func(cmd *serpent.Command) {
			if cmd.Name() == "update-user-email" {
				found = cmd
			}
		})
		require.NotNil(t, found, "update-user-email command not found under exp")
		require.True(t, found.Hidden, "command should be hidden")
	})

	t.Run("MissingOldEmail", func(t *testing.T) {
		t.Parallel()

		inv, _ := clitest.New(t, "exp", "update-user-email", "--new-email", "new@example.com")
		err := inv.Run()
		require.Error(t, err)
		require.Contains(t, err.Error(), "old-email")
	})

	t.Run("MissingNewEmail", func(t *testing.T) {
		t.Parallel()

		inv, _ := clitest.New(t, "exp", "update-user-email", "--old-email", "old@example.com")
		err := inv.Run()
		require.Error(t, err)
		require.Contains(t, err.Error(), "new-email")
	})

	t.Run("DeclinePrompt", func(t *testing.T) {
		t.Parallel()

		// Use a channel closed by the handler to detect whether the API is called.
		apiCalled := make(chan struct{})
		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			close(apiCalled)
			w.WriteHeader(http.StatusNoContent)
		}))
		t.Cleanup(srv.Close)

		client := codersdk.New(must(url.Parse(srv.URL)))
		inv, root := clitest.New(t, "exp", "update-user-email",
			"--old-email", "old@example.com",
			"--new-email", "new@example.com",
		)
		clitest.SetupConfig(t, client, root)
		inv.Stdin = strings.NewReader("no\n")

		ctx := testutil.Context(t, testutil.WaitShort)
		done := make(chan struct{})
		var runErr error
		go func() {
			defer close(done)
			runErr = inv.Run()
		}()

		testutil.TryReceive(ctx, t, done)
		require.ErrorIs(t, runErr, cliui.ErrCanceled)

		// Verify the API was not called after the command returned.
		select {
		case <-apiCalled:
			t.Fatal("API should not be called when prompt is declined")
		default:
		}
	})

	// AcceptPrompt runs a full end-to-end test against a real Coder server: it
	// creates a second user, invokes the CLI as an admin, confirms the prompt,
	// and asserts both the command output and the actual stored email.
	t.Run("AcceptPrompt", func(t *testing.T) {
		t.Parallel()

		client := coderdtest.New(t, nil)
		owner := coderdtest.CreateFirstUser(t, client)
		_, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
		ctx := testutil.Context(t, testutil.WaitShort)

		oldEmail := member.Email
		newEmail := "updated-" + oldEmail

		inv, root := clitest.New(t, "exp", "update-user-email",
			"--old-email", oldEmail,
			"--new-email", newEmail,
		)
		//nolint:gocritic // This break-glass command is restricted to deployment owners.
		clitest.SetupConfig(t, client, root)
		inv.Stdin = strings.NewReader("yes\n")

		var outBuf bytes.Buffer
		inv.Stdout = &outBuf

		require.NoError(t, inv.Run())

		out := outBuf.String()
		require.Contains(t, out, oldEmail)
		require.Contains(t, out, newEmail)
		require.Contains(t, out, "sessions and API tokens")
		require.Contains(t, out, "external identity provider")
		require.Contains(t, out, "Updated user email from "+oldEmail+" to "+newEmail+".")

		// Verify the email was actually persisted.
		updated, err := client.User(ctx, member.ID.String())
		require.NoError(t, err)
		require.Equal(t, newEmail, updated.Email)
	})

	t.Run("YesSkipsPrompt", func(t *testing.T) {
		t.Parallel()

		var gotBody codersdk.UpdateUserEmailRequest
		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			err := json.NewDecoder(r.Body).Decode(&gotBody)
			assert.NoError(t, err)
			w.WriteHeader(http.StatusNoContent)
		}))
		t.Cleanup(srv.Close)

		client := codersdk.New(must(url.Parse(srv.URL)))
		inv, root := clitest.New(t, "exp", "update-user-email",
			"--old-email", "old@example.com",
			"--new-email", "new@example.com",
			"--yes",
		)
		clitest.SetupConfig(t, client, root)

		var outBuf bytes.Buffer
		inv.Stdout = &outBuf

		require.NoError(t, inv.Run())
		require.Contains(t, outBuf.String(), "Updated user email from old@example.com to new@example.com.")
		require.Equal(t, "old@example.com", gotBody.OldEmail)
		require.Equal(t, "new@example.com", gotBody.NewEmail)
	})

	t.Run("APIError", func(t *testing.T) {
		t.Parallel()

		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusInternalServerError)
			_, _ = w.Write([]byte(`{"message":"internal server error"}`))
		}))
		t.Cleanup(srv.Close)

		client := codersdk.New(must(url.Parse(srv.URL)))
		inv, root := clitest.New(t, "exp", "update-user-email",
			"--old-email", "old@example.com",
			"--new-email", "new@example.com",
			"--yes",
		)
		clitest.SetupConfig(t, client, root)

		err := inv.Run()
		require.Error(t, err)
		require.Contains(t, err.Error(), "update user email")

		var sdkErr *codersdk.Error
		require.ErrorAs(t, err, &sdkErr)
		require.Equal(t, http.StatusInternalServerError, sdkErr.StatusCode())
		require.Contains(t, sdkErr.Message, "internal server error")
	})
}