Ctrl K
ComunidadGuías…

cli/server_createadminuser_test.go

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

Archivo original ↗Preguntar a KiBot
package cli_test

import (
	"bytes"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"runtime"
	"testing"

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

	"github.com/coder/coder/v2/cli/clitest"
	"github.com/coder/coder/v2/coderd/database"
	"github.com/coder/coder/v2/coderd/database/dbtestutil"
	"github.com/coder/coder/v2/coderd/database/dbtime"
	"github.com/coder/coder/v2/coderd/rbac"
	"github.com/coder/coder/v2/coderd/userpassword"
	"github.com/coder/coder/v2/codersdk"
	"github.com/coder/coder/v2/testutil"
	"github.com/coder/coder/v2/testutil/expecter"
)

//nolint:paralleltest, tparallel
func TestServerCreateAdminUser(t *testing.T) {
	const (
		username = "dean"
		email    = "dean@example.com"
		password = "SecurePa$$word123"
	)

	verifyUser := func(t *testing.T, dbURL, username, email, password string) {
		t.Helper()

		ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
		defer cancel()

		sqlDB, err := sql.Open("postgres", dbURL)
		require.NoError(t, err)
		defer sqlDB.Close()
		db := database.New(sqlDB)

		pingCtx, pingCancel := context.WithTimeout(ctx, testutil.WaitShort)
		defer pingCancel()
		_, err = db.Ping(pingCtx)
		require.NoError(t, err, "ping db")

		user, err := db.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
			Email: email,
		})
		require.NoError(t, err)
		require.Equal(t, username, user.Username, "username does not match")
		require.Equal(t, email, user.Email, "email does not match")

		ok, err := userpassword.Compare(string(user.HashedPassword), password)
		require.NoError(t, err)
		require.True(t, ok, "password does not match")

		require.EqualValues(t, []string{codersdk.RoleOwner}, user.RBACRoles, "user does not have owner role")

		// Check that user is admin in every org.
		orgs, err := db.GetOrganizations(ctx, database.GetOrganizationsParams{})
		require.NoError(t, err)
		orgIDs := make(map[uuid.UUID]struct{}, len(orgs))
		for _, org := range orgs {
			orgIDs[org.ID] = struct{}{}
		}

		orgMemberships, err := db.OrganizationMembers(ctx, database.OrganizationMembersParams{UserID: user.ID})
		require.NoError(t, err)
		orgIDs2 := make(map[uuid.UUID]struct{}, len(orgMemberships))
		for _, membership := range orgMemberships {
			orgIDs2[membership.OrganizationMember.OrganizationID] = struct{}{}
			assert.Equal(t, []string{rbac.RoleOrgAdmin()}, membership.OrganizationMember.Roles, "user is not org admin")
		}

		require.Equal(t, orgIDs, orgIDs2, "user is not in all orgs")
	}

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

		if runtime.GOOS != "linux" || testing.Short() {
			// Skip on non-Linux because it spawns a PostgreSQL instance.
			t.SkipNow()
		}
		connectionURL, err := dbtestutil.Open(t)
		require.NoError(t, err)

		sqlDB, err := sql.Open("postgres", connectionURL)
		require.NoError(t, err)
		defer sqlDB.Close()
		db := database.New(sqlDB)

		ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
		defer cancel()

		pingCtx, pingCancel := context.WithTimeout(ctx, testutil.WaitShort)
		defer pingCancel()
		_, err = db.Ping(pingCtx)
		require.NoError(t, err, "ping db")

		// Insert a few orgs.
		org1Name, org1ID := "org1", uuid.New()
		org2Name, org2ID := "org2", uuid.New()
		_, err = db.InsertOrganization(ctx, database.InsertOrganizationParams{
			ID:                    org1ID,
			Name:                  org1Name,
			CreatedAt:             dbtime.Now(),
			UpdatedAt:             dbtime.Now(),
			DefaultOrgMemberRoles: rbac.DefaultOrgMemberRoles(),
		})
		require.NoError(t, err)
		_, err = db.InsertOrganization(ctx, database.InsertOrganizationParams{
			ID:                    org2ID,
			Name:                  org2Name,
			CreatedAt:             dbtime.Now(),
			UpdatedAt:             dbtime.Now(),
			DefaultOrgMemberRoles: rbac.DefaultOrgMemberRoles(),
		})
		require.NoError(t, err)

		inv, _ := clitest.New(t,
			"server", "create-admin-user",
			"--postgres-url", connectionURL,
			"--ssh-keygen-algorithm", "ed25519",
			"--username", username,
			"--email", email,
			"--password", password,
		)
		stdout := expecter.NewAttachedToInvocation(t, inv)
		clitest.Start(t, inv)

		stdout.ExpectMatch(ctx, "Creating user...")
		stdout.ExpectMatch(ctx, "Generating user SSH key...")
		stdout.ExpectMatch(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org1Name, org1ID.String()))
		stdout.ExpectMatch(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org2Name, org2ID.String()))
		stdout.ExpectMatch(ctx, "User created successfully.")
		stdout.ExpectMatch(ctx, username)
		stdout.ExpectMatch(ctx, email)
		stdout.ExpectMatch(ctx, "****")

		verifyUser(t, connectionURL, username, email, password)
	})

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

		if runtime.GOOS != "linux" || testing.Short() {
			// Skip on non-Linux because it spawns a PostgreSQL instance.
			t.SkipNow()
		}
		connectionURL, err := dbtestutil.Open(t)
		require.NoError(t, err)

		ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
		defer cancel()

		jsonUsername, jsonEmail, jsonPassword := "jsondean", "jsondean@example.com", "SecurePa$$word123"

		inv, _ := clitest.New(t,
			"server", "create-admin-user",
			"--postgres-url", connectionURL,
			"--ssh-keygen-algorithm", "ed25519",
			"--username", jsonUsername,
			"--email", jsonEmail,
			"--password", jsonPassword,
			"--output", "json",
		)
		stdout := new(bytes.Buffer)
		inv.Stdout = stdout
		inv.Stderr = io.Discard
		require.NoError(t, inv.WithContext(ctx).Run())

		var resp struct {
			ID       uuid.UUID `json:"id"`
			Username string    `json:"username"`
			Email    string    `json:"email"`
			Roles    []string  `json:"roles"`
		}
		require.NoError(t, json.Unmarshal(stdout.Bytes(), &resp))
		assert.NotEmpty(t, resp.ID)
		assert.Equal(t, jsonUsername, resp.Username)
		assert.Equal(t, jsonEmail, resp.Email)
		assert.Contains(t, resp.Roles, codersdk.RoleOwner)
		assert.NotContains(t, stdout.String(), jsonPassword)

		verifyUser(t, connectionURL, jsonUsername, jsonEmail, jsonPassword)
	})

	t.Run("Env", func(t *testing.T) {
		t.Parallel()
		if runtime.GOOS != "linux" || testing.Short() {
			// Skip on non-Linux because it spawns a PostgreSQL instance.
			t.SkipNow()
		}
		connectionURL, err := dbtestutil.Open(t)
		require.NoError(t, err)

		ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
		defer cancel()

		inv, _ := clitest.New(t, "server", "create-admin-user")
		inv.Environ.Set("CODER_PG_CONNECTION_URL", connectionURL)
		inv.Environ.Set("CODER_SSH_KEYGEN_ALGORITHM", "ed25519")
		inv.Environ.Set("CODER_USERNAME", username)
		inv.Environ.Set("CODER_EMAIL", email)
		inv.Environ.Set("CODER_PASSWORD", password)

		stdout := expecter.NewAttachedToInvocation(t, inv)
		clitest.Start(t, inv)

		stdout.ExpectMatch(ctx, "User created successfully.")
		stdout.ExpectMatch(ctx, username)
		stdout.ExpectMatch(ctx, email)
		stdout.ExpectMatch(ctx, "****")

		verifyUser(t, connectionURL, username, email, password)
	})

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

		if runtime.GOOS != "linux" || testing.Short() {
			// Skip on non-Linux because it spawns a PostgreSQL instance.
			t.SkipNow()
		}
		logger := testutil.Logger(t)
		connectionURL, err := dbtestutil.Open(t)
		require.NoError(t, err)

		ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
		defer cancel()

		inv, _ := clitest.New(t,
			"server", "create-admin-user",
			"--postgres-url", connectionURL,
			"--ssh-keygen-algorithm", "ed25519",
		)
		stdout := expecter.NewAttachedToInvocation(t, inv)
		stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)

		clitest.Start(t, inv)

		stdout.ExpectMatch(ctx, "Username")
		stdin.WriteLine(username)
		stdout.ExpectMatch(ctx, "Email")
		stdin.WriteLine(email)
		stdout.ExpectMatch(ctx, "Password")
		stdin.WriteLine(password)
		stdout.ExpectMatch(ctx, "Confirm password")
		stdin.WriteLine(password)

		stdout.ExpectMatch(ctx, "User created successfully.")
		stdout.ExpectMatch(ctx, username)
		stdout.ExpectMatch(ctx, email)
		stdout.ExpectMatch(ctx, "****")

		verifyUser(t, connectionURL, username, email, password)
	})

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

		if runtime.GOOS != "linux" || testing.Short() {
			// Skip on non-Linux because it spawns a PostgreSQL instance.
			t.SkipNow()
		}
		connectionURL, err := dbtestutil.Open(t)
		require.NoError(t, err)
		ctx := testutil.Context(t, testutil.WaitShort)

		root, _ := clitest.New(t,
			"server", "create-admin-user",
			"--postgres-url", connectionURL,
			"--ssh-keygen-algorithm", "rsa4096",
			"--username", "$",
			"--email", "not-an-email",
			"--password", "x",
		)
		root.Stdout, root.Stderr = io.Discard, io.Discard
		err = root.WithContext(ctx).Run()
		require.Error(t, err)
		require.ErrorContains(t, err, "'email' failed on the 'email' tag")
		require.ErrorContains(t, err, "'username' failed on the 'username' tag")
	})
}