Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e53d1e5

Browse files
committed
fix: fix loss of buffered input on cliui.Prompt
1 parent f0a4de5 commit e53d1e5

File tree

1 file changed

+54
-15
lines changed

1 file changed

+54
-15
lines changed

cli/cliui/prompt_test.go

+54-15
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import (
77
"os"
88
"os/exec"
99
"testing"
10+
"time"
1011

1112
"github.com/stretchr/testify/assert"
1213
"github.com/stretchr/testify/require"
14+
"golang.org/x/xerrors"
1315

1416
"github.com/coder/coder/v2/cli/cliui"
1517
"github.com/coder/coder/v2/pty"
@@ -22,26 +24,29 @@ func TestPrompt(t *testing.T) {
2224
t.Parallel()
2325
t.Run("Success", func(t *testing.T) {
2426
t.Parallel()
27+
ctx := testutil.Context(t, testutil.WaitShort)
2528
ptty := ptytest.New(t)
2629
msgChan := make(chan string)
2730
go func() {
28-
resp, err := newPrompt(ptty, cliui.PromptOptions{
31+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
2932
Text: "Example",
3033
}, nil)
3134
assert.NoError(t, err)
3235
msgChan <- resp
3336
}()
3437
ptty.ExpectMatch("Example")
3538
ptty.WriteLine("hello")
36-
require.Equal(t, "hello", <-msgChan)
39+
resp := testutil.RequireRecvCtx(ctx, t, msgChan)
40+
require.Equal(t, "hello", resp)
3741
})
3842

3943
t.Run("Confirm", func(t *testing.T) {
4044
t.Parallel()
45+
ctx := testutil.Context(t, testutil.WaitShort)
4146
ptty := ptytest.New(t)
4247
doneChan := make(chan string)
4348
go func() {
44-
resp, err := newPrompt(ptty, cliui.PromptOptions{
49+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
4550
Text: "Example",
4651
IsConfirm: true,
4752
}, nil)
@@ -50,18 +55,20 @@ func TestPrompt(t *testing.T) {
5055
}()
5156
ptty.ExpectMatch("Example")
5257
ptty.WriteLine("yes")
53-
require.Equal(t, "yes", <-doneChan)
58+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
59+
require.Equal(t, "yes", resp)
5460
})
5561

5662
t.Run("Skip", func(t *testing.T) {
5763
t.Parallel()
64+
ctx := testutil.Context(t, testutil.WaitShort)
5865
ptty := ptytest.New(t)
5966
var buf bytes.Buffer
6067

6168
// Copy all data written out to a buffer. When we close the ptty, we can
6269
// no longer read from the ptty.Output(), but we can read what was
6370
// written to the buffer.
64-
dataRead, doneReading := context.WithTimeout(context.Background(), testutil.WaitShort)
71+
dataRead, doneReading := context.WithCancel(ctx)
6572
go func() {
6673
// This will throw an error sometimes. The underlying ptty
6774
// has its own cleanup routines in t.Cleanup. Instead of
@@ -74,7 +81,7 @@ func TestPrompt(t *testing.T) {
7481

7582
doneChan := make(chan string)
7683
go func() {
77-
resp, err := newPrompt(ptty, cliui.PromptOptions{
84+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
7885
Text: "ShouldNotSeeThis",
7986
IsConfirm: true,
8087
}, func(inv *serpent.Invocation) {
@@ -85,7 +92,8 @@ func TestPrompt(t *testing.T) {
8592
doneChan <- resp
8693
}()
8794

88-
require.Equal(t, "yes", <-doneChan)
95+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
96+
require.Equal(t, "yes", resp)
8997
// Close the reader to end the io.Copy
9098
require.NoError(t, ptty.Close(), "close eof reader")
9199
// Wait for the IO copy to finish
@@ -96,42 +104,47 @@ func TestPrompt(t *testing.T) {
96104
})
97105
t.Run("JSON", func(t *testing.T) {
98106
t.Parallel()
107+
ctx := testutil.Context(t, testutil.WaitShort)
99108
ptty := ptytest.New(t)
100109
doneChan := make(chan string)
101110
go func() {
102-
resp, err := newPrompt(ptty, cliui.PromptOptions{
111+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
103112
Text: "Example",
104113
}, nil)
105114
assert.NoError(t, err)
106115
doneChan <- resp
107116
}()
108117
ptty.ExpectMatch("Example")
109118
ptty.WriteLine("{}")
110-
require.Equal(t, "{}", <-doneChan)
119+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
120+
require.Equal(t, "{}", resp)
111121
})
112122

113123
t.Run("BadJSON", func(t *testing.T) {
114124
t.Parallel()
125+
ctx := testutil.Context(t, testutil.WaitShort)
115126
ptty := ptytest.New(t)
116127
doneChan := make(chan string)
117128
go func() {
118-
resp, err := newPrompt(ptty, cliui.PromptOptions{
129+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
119130
Text: "Example",
120131
}, nil)
121132
assert.NoError(t, err)
122133
doneChan <- resp
123134
}()
124135
ptty.ExpectMatch("Example")
125136
ptty.WriteLine("{a")
126-
require.Equal(t, "{a", <-doneChan)
137+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
138+
require.Equal(t, "{a", resp)
127139
})
128140

129141
t.Run("MultilineJSON", func(t *testing.T) {
130142
t.Parallel()
143+
ctx := testutil.Context(t, testutil.WaitShort)
131144
ptty := ptytest.New(t)
132145
doneChan := make(chan string)
133146
go func() {
134-
resp, err := newPrompt(ptty, cliui.PromptOptions{
147+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
135148
Text: "Example",
136149
}, nil)
137150
assert.NoError(t, err)
@@ -141,11 +154,37 @@ func TestPrompt(t *testing.T) {
141154
ptty.WriteLine(`{
142155
"test": "wow"
143156
}`)
144-
require.Equal(t, `{"test":"wow"}`, <-doneChan)
157+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
158+
require.Equal(t, `{"test":"wow"}`, resp)
159+
})
160+
161+
t.Run("InvalidValid", func(t *testing.T) {
162+
t.Parallel()
163+
ctx := testutil.Context(t, time.Minute*5)
164+
ptty := ptytest.New(t)
165+
doneChan := make(chan string)
166+
go func() {
167+
resp, err := newPrompt(ctx, ptty, cliui.PromptOptions{
168+
Text: "Example",
169+
Validate: func(s string) error {
170+
t.Logf("validate: %q", s)
171+
if s != "valid" {
172+
return xerrors.New("invalid")
173+
}
174+
return nil
175+
},
176+
}, nil)
177+
assert.NoError(t, err)
178+
doneChan <- resp
179+
}()
180+
ptty.ExpectMatch("Example")
181+
ptty.WriteLine("foo\nbar\nbaz\n\n\nvalid\n")
182+
resp := testutil.RequireRecvCtx(ctx, t, doneChan)
183+
require.Equal(t, "valid", resp)
145184
})
146185
}
147186

148-
func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *serpent.Invocation)) (string, error) {
187+
func newPrompt(ctx context.Context, ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *serpent.Invocation)) (string, error) {
149188
value := ""
150189
cmd := &serpent.Command{
151190
Handler: func(inv *serpent.Invocation) error {
@@ -163,7 +202,7 @@ func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *ser
163202
inv.Stdout = ptty.Output()
164203
inv.Stderr = ptty.Output()
165204
inv.Stdin = ptty.Input()
166-
return value, inv.WithContext(context.Background()).Run()
205+
return value, inv.WithContext(ctx).Run()
167206
}
168207

169208
func TestPasswordTerminalState(t *testing.T) {

0 commit comments

Comments
 (0)