Skip to content

Commit 24cc781

Browse files
committed
Add CLI test for login
1 parent d6a1eb8 commit 24cc781

File tree

9 files changed

+152
-48
lines changed

9 files changed

+152
-48
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@
3535
"goleak",
3636
"hashicorp",
3737
"httpmw",
38+
"isatty",
3839
"Jobf",
40+
"kirsle",
3941
"manifoldco",
42+
"mattn",
4043
"moby",
4144
"nhooyr",
4245
"nolint",

cli/clitest/clitest.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package clitest
2+
3+
import (
4+
"bufio"
5+
"io"
6+
"testing"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/coder/coder/cli"
11+
"github.com/coder/coder/cli/config"
12+
)
13+
14+
func New(t *testing.T, args ...string) (*cobra.Command, config.Root) {
15+
cmd := cli.Root()
16+
dir := t.TempDir()
17+
root := config.Root(dir)
18+
cmd.SetArgs(append([]string{"--global-config", dir}, args...))
19+
return cmd, root
20+
}
21+
22+
func StdoutLogs(t *testing.T) io.Writer {
23+
reader, writer := io.Pipe()
24+
scanner := bufio.NewScanner(reader)
25+
t.Cleanup(func() {
26+
_ = reader.Close()
27+
_ = writer.Close()
28+
})
29+
go func() {
30+
for scanner.Scan() {
31+
if scanner.Err() != nil {
32+
return
33+
}
34+
t.Log(scanner.Text())
35+
}
36+
}()
37+
return writer
38+
}

cli/config/file_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestFile(t *testing.T) {
2424
require.NoError(t, err)
2525
data, err := root.Session().Read()
2626
require.NoError(t, err)
27-
require.Equal(t, "test", string(data))
27+
require.Equal(t, "test", data)
2828
})
2929

3030
t.Run("Delete", func(t *testing.T) {

cli/login.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package cli
22

33
import (
4-
"errors"
54
"fmt"
65
"net/url"
6+
"os"
77
"strings"
88

9-
"github.com/coder/coder/coderd"
10-
"github.com/coder/coder/codersdk"
119
"github.com/fatih/color"
1210
"github.com/go-playground/validator/v10"
1311
"github.com/manifoldco/promptui"
1412
"github.com/spf13/cobra"
1513
"golang.org/x/xerrors"
14+
15+
"github.com/coder/coder/coderd"
16+
"github.com/coder/coder/codersdk"
1617
)
1718

1819
func login() *cobra.Command {
@@ -46,7 +47,7 @@ func login() *cobra.Command {
4647
if !isTTY(cmd.InOrStdin()) {
4748
return xerrors.New("the initial user cannot be created in non-interactive mode. use the API")
4849
}
49-
fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been setup!\n", color.HiBlackString(">"))
50+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been setup!\n", color.HiBlackString(">"))
5051

5152
_, err := runPrompt(cmd, &promptui.Prompt{
5253
Label: "Would you like to create the first user?",
@@ -59,7 +60,7 @@ func login() *cobra.Command {
5960

6061
username, err := runPrompt(cmd, &promptui.Prompt{
6162
Label: "What username would you like?",
62-
Default: "kyle",
63+
Default: os.Getenv("USER"),
6364
})
6465
if err != nil {
6566
return err
@@ -78,7 +79,7 @@ func login() *cobra.Command {
7879
Validate: func(s string) error {
7980
err := validator.New().Var(s, "email")
8081
if err != nil {
81-
return errors.New("That's not a valid email address!")
82+
return xerrors.New("That's not a valid email address!")
8283
}
8384
return err
8485
},
@@ -121,7 +122,7 @@ func login() *cobra.Command {
121122
return xerrors.Errorf("write server url: %w", err)
122123
}
123124

124-
fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're logged in.\n", color.HiBlackString(">"), color.HiCyanString(username))
125+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're logged in.\n", color.HiBlackString(">"), color.HiCyanString(username))
125126
return nil
126127
}
127128

cli/login_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package cli_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/coder/coder/cli/clitest"
7+
"github.com/coder/coder/coderd/coderdtest"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/Netflix/go-expect"
11+
)
12+
13+
func TestLogin(t *testing.T) {
14+
t.Parallel()
15+
t.Run("InitialUserNoTTY", func(t *testing.T) {
16+
t.Parallel()
17+
client := coderdtest.New(t)
18+
root, _ := clitest.New(t, "login", client.URL.String())
19+
err := root.Execute()
20+
require.Error(t, err)
21+
})
22+
23+
t.Run("InitialUserTTY", func(t *testing.T) {
24+
t.Parallel()
25+
console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t)))
26+
require.NoError(t, err)
27+
client := coderdtest.New(t)
28+
root, _ := clitest.New(t, "login", client.URL.String())
29+
root.SetIn(console.Tty())
30+
root.SetOut(console.Tty())
31+
go func() {
32+
err = root.Execute()
33+
require.NoError(t, err)
34+
}()
35+
36+
matches := []string{
37+
"first user?", "y",
38+
"username", "testuser",
39+
"organization", "testorg",
40+
"email", "user@coder.com",
41+
"password", "password",
42+
}
43+
for i := 0; i < len(matches); i += 2 {
44+
match := matches[i]
45+
value := matches[i+1]
46+
_, err = console.ExpectString(match)
47+
require.NoError(t, err)
48+
_, err = console.SendLine(value)
49+
require.NoError(t, err)
50+
}
51+
_, err = console.ExpectString("Welcome to Coder")
52+
require.NoError(t, err)
53+
})
54+
}

cli/projectcreate.go

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import (
1111
"time"
1212

1313
"github.com/briandowns/spinner"
14-
"github.com/coder/coder/coderd"
15-
"github.com/coder/coder/codersdk"
16-
"github.com/coder/coder/database"
1714
"github.com/fatih/color"
1815
"github.com/manifoldco/promptui"
1916
"github.com/spf13/cobra"
2017
"golang.org/x/xerrors"
18+
19+
"github.com/coder/coder/coderd"
20+
"github.com/coder/coder/codersdk"
21+
"github.com/coder/coder/database"
2122
)
2223

2324
func projectCreate() *cobra.Command {
@@ -48,9 +49,8 @@ func projectCreate() *cobra.Command {
4849
return err
4950
}
5051

51-
name := filepath.Base(workingDir)
52-
name, err = runPrompt(cmd, &promptui.Prompt{
53-
Default: name,
52+
name, err := runPrompt(cmd, &promptui.Prompt{
53+
Default: filepath.Base(workingDir),
5454
Label: "What's your project's name?",
5555
Validate: func(s string) error {
5656
_, err = client.Project(cmd.Context(), organization.Name, s)
@@ -69,7 +69,7 @@ func projectCreate() *cobra.Command {
6969
spin.Start()
7070
defer spin.Stop()
7171

72-
bytes, err := tarDir(workingDir)
72+
bytes, err := tarDirectory(workingDir)
7373
if err != nil {
7474
return err
7575
}
@@ -91,8 +91,6 @@ func projectCreate() *cobra.Command {
9191
}
9292
spin.Stop()
9393

94-
time.Sleep(time.Second)
95-
9694
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), organization.Name, job.ID, time.Time{})
9795
if err != nil {
9896
return err
@@ -102,54 +100,50 @@ func projectCreate() *cobra.Command {
102100
if !ok {
103101
break
104102
}
105-
fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output)
103+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output)
106104
}
107105

108-
fmt.Printf("Projects %+v %+v\n", projects, organization)
106+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name)
109107
return nil
110108
},
111109
}
112110
}
113111

114-
func tarDir(directory string) ([]byte, error) {
112+
func tarDirectory(directory string) ([]byte, error) {
115113
var buffer bytes.Buffer
116-
tw := tar.NewWriter(&buffer)
117-
// walk through every file in the folder
118-
err := filepath.Walk(directory, func(file string, fi os.FileInfo, err error) error {
119-
// generate tar header
120-
header, err := tar.FileInfoHeader(fi, file)
114+
tarWriter := tar.NewWriter(&buffer)
115+
err := filepath.Walk(directory, func(file string, fileInfo os.FileInfo, err error) error {
116+
if err != nil {
117+
return err
118+
}
119+
header, err := tar.FileInfoHeader(fileInfo, file)
121120
if err != nil {
122121
return err
123122
}
124-
125-
// must provide real name
126-
// (see https://golang.org/src/archive/tar/common.go?#L626)
127123
rel, err := filepath.Rel(directory, file)
128124
if err != nil {
129125
return err
130126
}
131127
header.Name = rel
132-
133-
// write header
134-
if err := tw.WriteHeader(header); err != nil {
128+
if err := tarWriter.WriteHeader(header); err != nil {
135129
return err
136130
}
137-
// if not a dir, write file content
138-
if !fi.IsDir() {
139-
data, err := os.Open(file)
140-
if err != nil {
141-
return err
142-
}
143-
if _, err := io.Copy(tw, data); err != nil {
144-
return err
145-
}
131+
if fileInfo.IsDir() {
132+
return nil
133+
}
134+
data, err := os.Open(file)
135+
if err != nil {
136+
return err
137+
}
138+
if _, err := io.Copy(tarWriter, data); err != nil {
139+
return err
146140
}
147-
return nil
141+
return data.Close()
148142
})
149143
if err != nil {
150144
return nil, err
151145
}
152-
err = tw.Flush()
146+
err = tarWriter.Flush()
153147
if err != nil {
154148
return nil, err
155149
}

cli/root.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import (
77
"os"
88
"strings"
99

10-
"github.com/coder/coder/cli/config"
11-
"github.com/coder/coder/coderd"
12-
"github.com/coder/coder/codersdk"
1310
"github.com/fatih/color"
1411
"github.com/kirsle/configdir"
1512
"github.com/manifoldco/promptui"
1613
"github.com/mattn/go-isatty"
1714
"github.com/spf13/cobra"
15+
"golang.org/x/xerrors"
16+
17+
"github.com/coder/coder/cli/config"
18+
"github.com/coder/coder/coderd"
19+
"github.com/coder/coder/codersdk"
1820
)
1921

2022
const (
@@ -115,8 +117,15 @@ func isTTY(reader io.Reader) bool {
115117
}
116118

117119
func runPrompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) {
118-
prompt.Stdin = cmd.InOrStdin().(io.ReadCloser)
119-
prompt.Stdout = cmd.OutOrStdout().(io.WriteCloser)
120+
var ok bool
121+
prompt.Stdin, ok = cmd.InOrStdin().(io.ReadCloser)
122+
if !ok {
123+
return "", xerrors.New("stdin must be a readcloser")
124+
}
125+
prompt.Stdout, ok = cmd.OutOrStdout().(io.WriteCloser)
126+
if !ok {
127+
return "", xerrors.New("stdout must be a readcloser")
128+
}
120129

121130
// The prompt library displays defaults in a jarring way for the user
122131
// by attempting to autocomplete it. This sets no default enabling us

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ replace github.com/hashicorp/terraform-config-inspect => github.com/kylecarbs/te
1010

1111
require (
1212
cdr.dev/slog v1.4.1
13+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
1314
github.com/briandowns/spinner v1.18.1
1415
github.com/coder/retry v1.3.0
1516
github.com/fatih/color v1.13.0
@@ -58,6 +59,7 @@ require (
5859
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
5960
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
6061
github.com/containerd/continuity v0.2.2 // indirect
62+
github.com/creack/pty v1.1.17 // indirect
6163
github.com/davecgh/go-spew v1.1.1 // indirect
6264
github.com/dhui/dktest v0.3.9 // indirect
6365
github.com/dlclark/regexp2 v1.4.0 // indirect

go.sum

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01
103103
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
104104
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
105105
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
106+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
107+
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
106108
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
107109
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
108110
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -350,8 +352,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
350352
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
351353
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
352354
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
353-
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
354355
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
356+
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
357+
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
355358
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
356359
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
357360
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy