Skip to content

Commit bfe475e

Browse files
committed
Merge main
2 parents 908b9cc + 154b9bc commit bfe475e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3208
-2214
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"nhooyr",
4545
"nolint",
4646
"nosec",
47+
"ntqry",
4748
"oneof",
4849
"parameterscopeid",
4950
"promptui",

cli/clitest/clitest.go

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
package clitest
22

33
import (
4+
"archive/tar"
45
"bufio"
6+
"bytes"
7+
"errors"
58
"io"
9+
"os"
10+
"path/filepath"
11+
"regexp"
612
"testing"
713

14+
"github.com/Netflix/go-expect"
815
"github.com/spf13/cobra"
16+
"github.com/stretchr/testify/require"
917

1018
"github.com/coder/coder/cli"
1119
"github.com/coder/coder/cli/config"
20+
"github.com/coder/coder/codersdk"
21+
"github.com/coder/coder/provisioner/echo"
1222
)
1323

24+
var (
25+
// Used to ensure terminal output doesn't have anything crazy!
26+
// See: https://stackoverflow.com/a/29497680
27+
stripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
28+
)
29+
30+
// New creates a CLI instance with a configuration pointed to a
31+
// temporary testing directory.
1432
func New(t *testing.T, args ...string) (*cobra.Command, config.Root) {
1533
cmd := cli.Root()
1634
dir := t.TempDir()
@@ -19,7 +37,27 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) {
1937
return cmd, root
2038
}
2139

22-
func StdoutLogs(t *testing.T) io.Writer {
40+
// SetupConfig applies the URL and SessionToken of the client to the config.
41+
func SetupConfig(t *testing.T, client *codersdk.Client, root config.Root) {
42+
err := root.Session().Write(client.SessionToken)
43+
require.NoError(t, err)
44+
err = root.URL().Write(client.URL.String())
45+
require.NoError(t, err)
46+
}
47+
48+
// CreateProjectVersionSource writes the echo provisioner responses into a
49+
// new temporary testing directory.
50+
func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string {
51+
directory := t.TempDir()
52+
data, err := echo.Tar(responses)
53+
require.NoError(t, err)
54+
extractTar(t, data, directory)
55+
return directory
56+
}
57+
58+
// NewConsole creates a new TTY bound to the command provided.
59+
// All ANSI escape codes are stripped to provide clean output.
60+
func NewConsole(t *testing.T, cmd *cobra.Command) *expect.Console {
2361
reader, writer := io.Pipe()
2462
scanner := bufio.NewScanner(reader)
2563
t.Cleanup(func() {
@@ -31,8 +69,46 @@ func StdoutLogs(t *testing.T) io.Writer {
3169
if scanner.Err() != nil {
3270
return
3371
}
34-
t.Log(scanner.Text())
72+
t.Log(stripAnsi.ReplaceAllString(scanner.Text(), ""))
3573
}
3674
}()
37-
return writer
75+
76+
console, err := expect.NewConsole(expect.WithStdout(writer))
77+
require.NoError(t, err)
78+
cmd.SetIn(console.Tty())
79+
cmd.SetOut(console.Tty())
80+
return console
81+
}
82+
83+
func extractTar(t *testing.T, data []byte, directory string) {
84+
reader := tar.NewReader(bytes.NewBuffer(data))
85+
for {
86+
header, err := reader.Next()
87+
if errors.Is(err, io.EOF) {
88+
break
89+
}
90+
require.NoError(t, err)
91+
// #nosec
92+
path := filepath.Join(directory, header.Name)
93+
mode := header.FileInfo().Mode()
94+
if mode == 0 {
95+
mode = 0600
96+
}
97+
switch header.Typeflag {
98+
case tar.TypeDir:
99+
err = os.MkdirAll(path, mode)
100+
require.NoError(t, err)
101+
case tar.TypeReg:
102+
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode)
103+
require.NoError(t, err)
104+
// Max file size of 10MB.
105+
_, err = io.CopyN(file, reader, (1<<20)*10)
106+
if errors.Is(err, io.EOF) {
107+
err = nil
108+
}
109+
require.NoError(t, err)
110+
err = file.Close()
111+
require.NoError(t, err)
112+
}
113+
}
38114
}

cli/clitest/clitest_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//go:build !windows
2+
3+
package clitest_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/coder/coder/cli/clitest"
9+
"github.com/coder/coder/coderd/coderdtest"
10+
"github.com/stretchr/testify/require"
11+
"go.uber.org/goleak"
12+
)
13+
14+
func TestMain(m *testing.M) {
15+
goleak.VerifyTestMain(m)
16+
}
17+
18+
func TestCli(t *testing.T) {
19+
t.Parallel()
20+
clitest.CreateProjectVersionSource(t, nil)
21+
client := coderdtest.New(t)
22+
cmd, config := clitest.New(t)
23+
clitest.SetupConfig(t, client, config)
24+
console := clitest.NewConsole(t, cmd)
25+
go func() {
26+
err := cmd.Execute()
27+
require.NoError(t, err)
28+
}()
29+
_, err := console.ExpectString("coder")
30+
require.NoError(t, err)
31+
}

cli/login.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func login() *cobra.Command {
5454
}
5555
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", color.HiBlackString(">"))
5656

57-
_, err := runPrompt(cmd, &promptui.Prompt{
57+
_, err := prompt(cmd, &promptui.Prompt{
5858
Label: "Would you like to create the first user?",
5959
IsConfirm: true,
6060
Default: "y",
@@ -66,23 +66,23 @@ func login() *cobra.Command {
6666
if err != nil {
6767
return xerrors.Errorf("get current user: %w", err)
6868
}
69-
username, err := runPrompt(cmd, &promptui.Prompt{
69+
username, err := prompt(cmd, &promptui.Prompt{
7070
Label: "What username would you like?",
7171
Default: currentUser.Username,
7272
})
7373
if err != nil {
7474
return xerrors.Errorf("pick username prompt: %w", err)
7575
}
7676

77-
organization, err := runPrompt(cmd, &promptui.Prompt{
77+
organization, err := prompt(cmd, &promptui.Prompt{
7878
Label: "What is the name of your organization?",
7979
Default: "acme-corp",
8080
})
8181
if err != nil {
8282
return xerrors.Errorf("pick organization prompt: %w", err)
8383
}
8484

85-
email, err := runPrompt(cmd, &promptui.Prompt{
85+
email, err := prompt(cmd, &promptui.Prompt{
8686
Label: "What's your email?",
8787
Validate: func(s string) error {
8888
err := validator.New().Var(s, "email")
@@ -96,7 +96,7 @@ func login() *cobra.Command {
9696
return xerrors.Errorf("specify email prompt: %w", err)
9797
}
9898

99-
password, err := runPrompt(cmd, &promptui.Prompt{
99+
password, err := prompt(cmd, &promptui.Prompt{
100100
Label: "Enter a password:",
101101
Mask: '*',
102102
})

cli/login_test.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ import (
77

88
"github.com/coder/coder/cli/clitest"
99
"github.com/coder/coder/coderd/coderdtest"
10+
<<<<<<< HEAD
1011

1112
"github.com/coder/coder/expect"
13+
||||||| df13fef
14+
"github.com/stretchr/testify/require"
15+
16+
"github.com/Netflix/go-expect"
17+
=======
18+
"github.com/stretchr/testify/require"
19+
>>>>>>> main
1220
)
1321

1422
func TestLogin(t *testing.T) {
@@ -23,15 +31,22 @@ func TestLogin(t *testing.T) {
2331

2432
t.Run("InitialUserTTY", func(t *testing.T) {
2533
t.Parallel()
26-
console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t)))
27-
require.NoError(t, err)
2834
client := coderdtest.New(t)
35+
<<<<<<< HEAD
2936
// The --force-tty flag is required on Windows, because the `isatty` library does not
3037
// accurately detect Windows ptys when they are not attached to a process:
3138
// https://github.com/mattn/go-isatty/issues/59
3239
root, _ := clitest.New(t, "login", client.URL.String(), "--force-tty")
3340
root.SetIn(console.InTty())
3441
root.SetOut(console.OutTty())
42+
||||||| df13fef
43+
root, _ := clitest.New(t, "login", client.URL.String())
44+
root.SetIn(console.Tty())
45+
root.SetOut(console.Tty())
46+
=======
47+
root, _ := clitest.New(t, "login", client.URL.String())
48+
console := clitest.NewConsole(t, root)
49+
>>>>>>> main
3550
go func() {
3651
err := root.Execute()
3752
require.NoError(t, err)
@@ -47,12 +62,12 @@ func TestLogin(t *testing.T) {
4762
for i := 0; i < len(matches); i += 2 {
4863
match := matches[i]
4964
value := matches[i+1]
50-
_, err = console.ExpectString(match)
65+
_, err := console.ExpectString(match)
5166
require.NoError(t, err)
5267
_, err = console.SendLine(value)
5368
require.NoError(t, err)
5469
}
55-
_, err = console.ExpectString("Welcome to Coder")
70+
_, err := console.ExpectString("Welcome to Coder")
5671
require.NoError(t, err)
5772
})
5873
}

0 commit comments

Comments
 (0)