Skip to content

Commit c4057eb

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

File tree

9 files changed

+150
-46
lines changed

9 files changed

+150
-46
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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
client := coderdtest.New(t)
17+
root, _ := clitest.New(t, "login", client.URL.String())
18+
err := root.Execute()
19+
require.Error(t, err)
20+
})
21+
22+
t.Run("InitialUserTTY", func(t *testing.T) {
23+
console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t)))
24+
require.NoError(t, err)
25+
client := coderdtest.New(t)
26+
root, _ := clitest.New(t, "login", client.URL.String())
27+
root.SetIn(console.Tty())
28+
root.SetOut(console.Tty())
29+
go func() {
30+
err = root.Execute()
31+
require.NoError(t, err)
32+
}()
33+
34+
matches := []string{
35+
"first user?", "y",
36+
"username", "testuser",
37+
"organization", "testorg",
38+
"email", "user@coder.com",
39+
"password", "password",
40+
}
41+
for i := 0; i < len(matches); i += 2 {
42+
match := matches[i]
43+
value := matches[i+1]
44+
_, err = console.ExpectString(match)
45+
require.NoError(t, err)
46+
_, err = console.SendLine(value)
47+
require.NoError(t, err)
48+
}
49+
_, err = console.ExpectString("Welcome to Coder")
50+
require.NoError(t, err)
51+
})
52+
}

cli/projectcreate.go

Lines changed: 28 additions & 32 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
}
@@ -102,54 +102,50 @@ func projectCreate() *cobra.Command {
102102
if !ok {
103103
break
104104
}
105-
fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output)
105+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output)
106106
}
107107

108-
fmt.Printf("Projects %+v %+v\n", projects, organization)
108+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name)
109109
return nil
110110
},
111111
}
112112
}
113113

114-
func tarDir(directory string) ([]byte, error) {
114+
func tarDirectory(directory string) ([]byte, error) {
115115
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)
116+
tarWriter := tar.NewWriter(&buffer)
117+
err := filepath.Walk(directory, func(file string, fileInfo os.FileInfo, err error) error {
118+
if err != nil {
119+
return err
120+
}
121+
header, err := tar.FileInfoHeader(fileInfo, file)
121122
if err != nil {
122123
return err
123124
}
124-
125-
// must provide real name
126-
// (see https://golang.org/src/archive/tar/common.go?#L626)
127125
rel, err := filepath.Rel(directory, file)
128126
if err != nil {
129127
return err
130128
}
131129
header.Name = rel
132-
133-
// write header
134-
if err := tw.WriteHeader(header); err != nil {
130+
if err := tarWriter.WriteHeader(header); err != nil {
135131
return err
136132
}
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-
}
133+
if fileInfo.IsDir() {
134+
return nil
135+
}
136+
data, err := os.Open(file)
137+
if err != nil {
138+
return err
139+
}
140+
if _, err := io.Copy(tarWriter, data); err != nil {
141+
return err
146142
}
147-
return nil
143+
return data.Close()
148144
})
149145
if err != nil {
150146
return nil, err
151147
}
152-
err = tw.Flush()
148+
err = tarWriter.Flush()
153149
if err != nil {
154150
return nil, err
155151
}

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