mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add "Full Name" field to user creation (#13659)
Adds the ability to specify "Full Name" (a.k.a. Name) when creating users either via CLI or UI.
This commit is contained in:
@@ -58,6 +58,21 @@ func promptFirstUsername(inv *serpent.Invocation) (string, error) {
|
||||
return username, nil
|
||||
}
|
||||
|
||||
func promptFirstName(inv *serpent.Invocation) (string, error) {
|
||||
name, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "(Optional) What " + pretty.Sprint(cliui.DefaultStyles.Field, "name") + " would you like?",
|
||||
Default: "",
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, cliui.Canceled) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func promptFirstPassword(inv *serpent.Invocation) (string, error) {
|
||||
retry:
|
||||
password, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
@@ -130,6 +145,7 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
var (
|
||||
email string
|
||||
username string
|
||||
name string
|
||||
password string
|
||||
trial bool
|
||||
useTokenForSession bool
|
||||
@@ -191,6 +207,7 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
|
||||
_, _ = fmt.Fprintf(inv.Stdout, "Attempting to authenticate with %s URL: '%s'\n", urlSource, serverURL)
|
||||
|
||||
// nolint: nestif
|
||||
if !hasFirstUser {
|
||||
_, _ = fmt.Fprintf(inv.Stdout, Caret+"Your Coder deployment hasn't been set up!\n")
|
||||
|
||||
@@ -212,6 +229,10 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name, err = promptFirstName(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
@@ -249,6 +270,7 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
|
||||
Email: email,
|
||||
Username: username,
|
||||
Name: name,
|
||||
Password: password,
|
||||
Trial: trial,
|
||||
})
|
||||
@@ -360,6 +382,12 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
Description: "Specifies a username to use if creating the first user for the deployment.",
|
||||
Value: serpent.StringOf(&username),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-full-name",
|
||||
Env: "CODER_FIRST_USER_FULL_NAME",
|
||||
Description: "Specifies a human-readable name for the first user of the deployment.",
|
||||
Value: serpent.StringOf(&name),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-password",
|
||||
Env: "CODER_FIRST_USER_PASSWORD",
|
||||
|
||||
+133
-16
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
@@ -91,10 +92,11 @@ func TestLogin(t *testing.T) {
|
||||
|
||||
matches := []string{
|
||||
"first user?", "yes",
|
||||
"username", "testuser",
|
||||
"email", "user@coder.com",
|
||||
"password", "SomeSecurePassword!",
|
||||
"password", "SomeSecurePassword!", // Confirm.
|
||||
"username", coderdtest.FirstUserParams.Username,
|
||||
"name", coderdtest.FirstUserParams.Name,
|
||||
"email", coderdtest.FirstUserParams.Email,
|
||||
"password", coderdtest.FirstUserParams.Password,
|
||||
"password", coderdtest.FirstUserParams.Password, // confirm
|
||||
"trial", "yes",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
@@ -105,6 +107,64 @@ func TestLogin(t *testing.T) {
|
||||
}
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
<-doneChan
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Name, me.Name)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTTYNameOptional", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
// The --force-tty flag is required on Windows, because the `isatty` library does not
|
||||
// accurately detect Windows ptys when they are not attached to a process:
|
||||
// https://github.com/mattn/go-isatty/issues/59
|
||||
doneChan := make(chan struct{})
|
||||
root, _ := clitest.New(t, "login", "--force-tty", client.URL.String())
|
||||
pty := ptytest.New(t).Attach(root)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
err := root.Run()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
matches := []string{
|
||||
"first user?", "yes",
|
||||
"username", coderdtest.FirstUserParams.Username,
|
||||
"name", "",
|
||||
"email", coderdtest.FirstUserParams.Email,
|
||||
"password", coderdtest.FirstUserParams.Password,
|
||||
"password", coderdtest.FirstUserParams.Password, // confirm
|
||||
"trial", "yes",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
value := matches[i+1]
|
||||
pty.ExpectMatch(match)
|
||||
pty.WriteLine(value)
|
||||
}
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
<-doneChan
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
assert.Empty(t, me.Name)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTTYFlag", func(t *testing.T) {
|
||||
@@ -121,10 +181,11 @@ func TestLogin(t *testing.T) {
|
||||
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with flag URL: '%s'", client.URL.String()))
|
||||
matches := []string{
|
||||
"first user?", "yes",
|
||||
"username", "testuser",
|
||||
"email", "user@coder.com",
|
||||
"password", "SomeSecurePassword!",
|
||||
"password", "SomeSecurePassword!", // Confirm.
|
||||
"username", coderdtest.FirstUserParams.Username,
|
||||
"name", coderdtest.FirstUserParams.Name,
|
||||
"email", coderdtest.FirstUserParams.Email,
|
||||
"password", coderdtest.FirstUserParams.Password,
|
||||
"password", coderdtest.FirstUserParams.Password, // confirm
|
||||
"trial", "yes",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
@@ -134,6 +195,18 @@ func TestLogin(t *testing.T) {
|
||||
pty.WriteLine(value)
|
||||
}
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Name, me.Name)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
})
|
||||
|
||||
t.Run("InitialUserFlags", func(t *testing.T) {
|
||||
@@ -141,13 +214,56 @@ func TestLogin(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", "testuser", "--first-user-email", "user@coder.com",
|
||||
"--first-user-password", "SomeSecurePassword!", "--first-user-trial",
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
"--first-user-trial",
|
||||
)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
w := clitest.StartWithWaiter(t, inv)
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
w.RequireSuccess()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Name, me.Name)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
})
|
||||
|
||||
t.Run("InitialUserFlagsNameOptional", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
"--first-user-trial",
|
||||
)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
w := clitest.StartWithWaiter(t, inv)
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
w.RequireSuccess()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
assert.Empty(t, me.Name)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTTYConfirmPasswordFailAndReprompt", func(t *testing.T) {
|
||||
@@ -169,10 +285,11 @@ func TestLogin(t *testing.T) {
|
||||
|
||||
matches := []string{
|
||||
"first user?", "yes",
|
||||
"username", "testuser",
|
||||
"email", "user@coder.com",
|
||||
"password", "MyFirstSecurePassword!",
|
||||
"password", "MyNonMatchingSecurePassword!", // Confirm.
|
||||
"username", coderdtest.FirstUserParams.Username,
|
||||
"name", coderdtest.FirstUserParams.Name,
|
||||
"email", coderdtest.FirstUserParams.Email,
|
||||
"password", coderdtest.FirstUserParams.Password,
|
||||
"password", "something completely different",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
@@ -185,9 +302,9 @@ func TestLogin(t *testing.T) {
|
||||
pty.ExpectMatch("Passwords do not match")
|
||||
pty.ExpectMatch("Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password"))
|
||||
|
||||
pty.WriteLine("SomeSecurePassword!")
|
||||
pty.WriteLine(coderdtest.FirstUserParams.Password)
|
||||
pty.ExpectMatch("Confirm")
|
||||
pty.WriteLine("SomeSecurePassword!")
|
||||
pty.WriteLine(coderdtest.FirstUserParams.Password)
|
||||
pty.ExpectMatch("trial")
|
||||
pty.WriteLine("yes")
|
||||
pty.ExpectMatch("Welcome to Coder")
|
||||
|
||||
@@ -85,6 +85,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
|
||||
// Use the validator tags so we match the API's validation.
|
||||
req := codersdk.CreateUserRequest{
|
||||
Username: "username",
|
||||
Name: "Admin User",
|
||||
Email: "email@coder.com",
|
||||
Password: "ValidPa$$word123!",
|
||||
OrganizationID: uuid.New(),
|
||||
@@ -116,6 +117,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if newUserEmail == "" {
|
||||
newUserEmail, err = cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Email",
|
||||
@@ -189,6 +191,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
|
||||
ID: uuid.New(),
|
||||
Email: newUserEmail,
|
||||
Username: newUserUsername,
|
||||
Name: "Admin User",
|
||||
HashedPassword: []byte(hashedPassword),
|
||||
CreatedAt: dbtime.Now(),
|
||||
UpdatedAt: dbtime.Now(),
|
||||
|
||||
+3
@@ -10,6 +10,9 @@ OPTIONS:
|
||||
Specifies an email address to use if creating the first user for the
|
||||
deployment.
|
||||
|
||||
--first-user-full-name string, $CODER_FIRST_USER_FULL_NAME
|
||||
Specifies a human-readable name for the first user of the deployment.
|
||||
|
||||
--first-user-password string, $CODER_FIRST_USER_PASSWORD
|
||||
Specifies a password to use if creating the first user for the
|
||||
deployment.
|
||||
|
||||
@@ -7,6 +7,9 @@ OPTIONS:
|
||||
-e, --email string
|
||||
Specifies an email address for the new user.
|
||||
|
||||
-n, --full-name string
|
||||
Specifies an optional human-readable name for the new user.
|
||||
|
||||
--login-type string
|
||||
Optionally specify the login type for the user. Valid values are:
|
||||
password, none, github, oidc. Using 'none' prevents the user from
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"id": "[first user ID]",
|
||||
"username": "testuser",
|
||||
"avatar_url": "",
|
||||
"name": "",
|
||||
"name": "Test User",
|
||||
"email": "testuser@coder.com",
|
||||
"created_at": "[timestamp]",
|
||||
"last_seen_at": "[timestamp]",
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/coder/pretty"
|
||||
|
||||
"github.com/coder/coder/v2/cli/cliui"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/cryptorand"
|
||||
"github.com/coder/serpent"
|
||||
@@ -19,6 +20,7 @@ func (r *RootCmd) userCreate() *serpent.Command {
|
||||
var (
|
||||
email string
|
||||
username string
|
||||
name string
|
||||
password string
|
||||
disableLogin bool
|
||||
loginType string
|
||||
@@ -35,6 +37,9 @@ func (r *RootCmd) userCreate() *serpent.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// We only prompt for the full name if both username and email have not
|
||||
// been set. This is to avoid breaking existing non-interactive usage.
|
||||
shouldPromptName := username == "" && email == ""
|
||||
if username == "" {
|
||||
username, err = cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Username:",
|
||||
@@ -58,6 +63,18 @@ func (r *RootCmd) userCreate() *serpent.Command {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if name == "" && shouldPromptName {
|
||||
rawName, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Full name (optional):",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name = httpapi.NormalizeRealUsername(rawName)
|
||||
if !strings.EqualFold(rawName, name) {
|
||||
cliui.Warnf(inv.Stderr, "Normalized name to %q", name)
|
||||
}
|
||||
}
|
||||
userLoginType := codersdk.LoginTypePassword
|
||||
if disableLogin && loginType != "" {
|
||||
return xerrors.New("You cannot specify both --disable-login and --login-type")
|
||||
@@ -79,6 +96,7 @@ func (r *RootCmd) userCreate() *serpent.Command {
|
||||
_, err = client.CreateUser(inv.Context(), codersdk.CreateUserRequest{
|
||||
Email: email,
|
||||
Username: username,
|
||||
Name: name,
|
||||
Password: password,
|
||||
OrganizationID: organization.ID,
|
||||
UserLoginType: userLoginType,
|
||||
@@ -127,6 +145,12 @@ Create a workspace `+pretty.Sprint(cliui.DefaultStyles.Code, "coder create")+`!
|
||||
Description: "Specifies a username for the new user.",
|
||||
Value: serpent.StringOf(&username),
|
||||
},
|
||||
{
|
||||
Flag: "full-name",
|
||||
FlagShorthand: "n",
|
||||
Description: "Specifies an optional human-readable name for the new user.",
|
||||
Value: serpent.StringOf(&name),
|
||||
},
|
||||
{
|
||||
Flag: "password",
|
||||
FlagShorthand: "p",
|
||||
|
||||
+88
-1
@@ -4,16 +4,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestUserCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Prompts", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
inv, root := clitest.New(t, "users", "create")
|
||||
@@ -28,6 +31,7 @@ func TestUserCreate(t *testing.T) {
|
||||
matches := []string{
|
||||
"Username", "dean",
|
||||
"Email", "dean@coder.com",
|
||||
"Full name (optional):", "Mr. Dean Deanington",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
@@ -35,6 +39,89 @@ func TestUserCreate(t *testing.T) {
|
||||
pty.ExpectMatch(match)
|
||||
pty.WriteLine(value)
|
||||
}
|
||||
<-doneChan
|
||||
_ = testutil.RequireRecvCtx(ctx, t, doneChan)
|
||||
created, err := client.User(ctx, matches[1])
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, matches[1], created.Username)
|
||||
assert.Equal(t, matches[3], created.Email)
|
||||
assert.Equal(t, matches[5], created.Name)
|
||||
})
|
||||
|
||||
t.Run("PromptsNoName", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
inv, root := clitest.New(t, "users", "create")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
doneChan := make(chan struct{})
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
err := inv.Run()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
matches := []string{
|
||||
"Username", "noname",
|
||||
"Email", "noname@coder.com",
|
||||
"Full name (optional):", "",
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
value := matches[i+1]
|
||||
pty.ExpectMatch(match)
|
||||
pty.WriteLine(value)
|
||||
}
|
||||
_ = testutil.RequireRecvCtx(ctx, t, doneChan)
|
||||
created, err := client.User(ctx, matches[1])
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, matches[1], created.Username)
|
||||
assert.Equal(t, matches[3], created.Email)
|
||||
assert.Empty(t, created.Name)
|
||||
})
|
||||
|
||||
t.Run("Args", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
args := []string{
|
||||
"users", "create",
|
||||
"-e", "dean@coder.com",
|
||||
"-u", "dean",
|
||||
"-n", "Mr. Dean Deanington",
|
||||
"-p", "1n5ecureP4ssw0rd!",
|
||||
}
|
||||
inv, root := clitest.New(t, args...)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
err := inv.Run()
|
||||
require.NoError(t, err)
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
created, err := client.User(ctx, "dean")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, args[3], created.Email)
|
||||
assert.Equal(t, args[5], created.Username)
|
||||
assert.Equal(t, args[7], created.Name)
|
||||
})
|
||||
|
||||
t.Run("ArgsNoName", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
args := []string{
|
||||
"users", "create",
|
||||
"-e", "dean@coder.com",
|
||||
"-u", "dean",
|
||||
"-p", "1n5ecureP4ssw0rd!",
|
||||
}
|
||||
inv, root := clitest.New(t, args...)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
err := inv.Run()
|
||||
require.NoError(t, err)
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
created, err := client.User(ctx, args[5])
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, args[3], created.Email)
|
||||
assert.Equal(t, args[5], created.Username)
|
||||
assert.Empty(t, created.Name)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ func (*userShowFormat) Format(_ context.Context, out interface{}) (string, error
|
||||
// Add rows for each of the user's fields.
|
||||
addRow("ID", user.ID.String())
|
||||
addRow("Username", user.Username)
|
||||
addRow("Full name", user.Name)
|
||||
addRow("Email", user.Email)
|
||||
addRow("Status", user.Status)
|
||||
addRow("Created At", user.CreatedAt.Format(time.Stamp))
|
||||
|
||||
@@ -57,7 +57,14 @@ func TestUserList(t *testing.T) {
|
||||
err := json.Unmarshal(buf.Bytes(), &users)
|
||||
require.NoError(t, err, "unmarshal JSON output")
|
||||
require.Len(t, users, 2)
|
||||
require.Contains(t, users[0].Email, "coder.com")
|
||||
for _, u := range users {
|
||||
assert.NotEmpty(t, u.ID)
|
||||
assert.NotEmpty(t, u.Email)
|
||||
assert.NotEmpty(t, u.Username)
|
||||
assert.NotEmpty(t, u.Name)
|
||||
assert.NotEmpty(t, u.CreatedAt)
|
||||
assert.NotEmpty(t, u.Status)
|
||||
}
|
||||
})
|
||||
t.Run("NoURLFileErrorHasHelperText", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -133,5 +140,6 @@ func TestUserShow(t *testing.T) {
|
||||
require.Equal(t, otherUser.ID, newUser.ID)
|
||||
require.Equal(t, otherUser.Username, newUser.Username)
|
||||
require.Equal(t, otherUser.Email, newUser.Email)
|
||||
require.Equal(t, otherUser.Name, newUser.Name)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user