@@ -7,9 +7,11 @@ import (
7
7
"os"
8
8
"os/exec"
9
9
"testing"
10
+ "time"
10
11
11
12
"github.com/stretchr/testify/assert"
12
13
"github.com/stretchr/testify/require"
14
+ "golang.org/x/xerrors"
13
15
14
16
"github.com/coder/coder/v2/cli/cliui"
15
17
"github.com/coder/coder/v2/pty"
@@ -22,26 +24,29 @@ func TestPrompt(t *testing.T) {
22
24
t .Parallel ()
23
25
t .Run ("Success" ,func (t * testing.T ) {
24
26
t .Parallel ()
27
+ ctx := testutil .Context (t ,testutil .WaitShort )
25
28
ptty := ptytest .New (t )
26
29
msgChan := make (chan string )
27
30
go func () {
28
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
31
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
29
32
Text :"Example" ,
30
33
},nil )
31
34
assert .NoError (t ,err )
32
35
msgChan <- resp
33
36
}()
34
37
ptty .ExpectMatch ("Example" )
35
38
ptty .WriteLine ("hello" )
36
- require .Equal (t ,"hello" ,<- msgChan )
39
+ resp := testutil .RequireRecvCtx (ctx ,t ,msgChan )
40
+ require .Equal (t ,"hello" ,resp )
37
41
})
38
42
39
43
t .Run ("Confirm" ,func (t * testing.T ) {
40
44
t .Parallel ()
45
+ ctx := testutil .Context (t ,testutil .WaitShort )
41
46
ptty := ptytest .New (t )
42
47
doneChan := make (chan string )
43
48
go func () {
44
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
49
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
45
50
Text :"Example" ,
46
51
IsConfirm :true ,
47
52
},nil )
@@ -50,18 +55,20 @@ func TestPrompt(t *testing.T) {
50
55
}()
51
56
ptty .ExpectMatch ("Example" )
52
57
ptty .WriteLine ("yes" )
53
- require .Equal (t ,"yes" ,<- doneChan )
58
+ resp := testutil .RequireRecvCtx (ctx ,t ,doneChan )
59
+ require .Equal (t ,"yes" ,resp )
54
60
})
55
61
56
62
t .Run ("Skip" ,func (t * testing.T ) {
57
63
t .Parallel ()
64
+ ctx := testutil .Context (t ,testutil .WaitShort )
58
65
ptty := ptytest .New (t )
59
66
var buf bytes.Buffer
60
67
61
68
// Copy all data written out to a buffer. When we close the ptty, we can
62
69
// no longer read from the ptty.Output(), but we can read what was
63
70
// written to the buffer.
64
- dataRead ,doneReading := context .WithTimeout ( context . Background (), testutil . WaitShort )
71
+ dataRead ,doneReading := context .WithCancel ( ctx )
65
72
go func () {
66
73
// This will throw an error sometimes. The underlying ptty
67
74
// has its own cleanup routines in t.Cleanup. Instead of
@@ -74,7 +81,7 @@ func TestPrompt(t *testing.T) {
74
81
75
82
doneChan := make (chan string )
76
83
go func () {
77
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
84
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
78
85
Text :"ShouldNotSeeThis" ,
79
86
IsConfirm :true ,
80
87
},func (inv * serpent.Invocation ) {
@@ -85,7 +92,8 @@ func TestPrompt(t *testing.T) {
85
92
doneChan <- resp
86
93
}()
87
94
88
- require .Equal (t ,"yes" ,<- doneChan )
95
+ resp := testutil .RequireRecvCtx (ctx ,t ,doneChan )
96
+ require .Equal (t ,"yes" ,resp )
89
97
// Close the reader to end the io.Copy
90
98
require .NoError (t ,ptty .Close (),"close eof reader" )
91
99
// Wait for the IO copy to finish
@@ -96,42 +104,47 @@ func TestPrompt(t *testing.T) {
96
104
})
97
105
t .Run ("JSON" ,func (t * testing.T ) {
98
106
t .Parallel ()
107
+ ctx := testutil .Context (t ,testutil .WaitShort )
99
108
ptty := ptytest .New (t )
100
109
doneChan := make (chan string )
101
110
go func () {
102
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
111
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
103
112
Text :"Example" ,
104
113
},nil )
105
114
assert .NoError (t ,err )
106
115
doneChan <- resp
107
116
}()
108
117
ptty .ExpectMatch ("Example" )
109
118
ptty .WriteLine ("{}" )
110
- require .Equal (t ,"{}" ,<- doneChan )
119
+ resp := testutil .RequireRecvCtx (ctx ,t ,doneChan )
120
+ require .Equal (t ,"{}" ,resp )
111
121
})
112
122
113
123
t .Run ("BadJSON" ,func (t * testing.T ) {
114
124
t .Parallel ()
125
+ ctx := testutil .Context (t ,testutil .WaitShort )
115
126
ptty := ptytest .New (t )
116
127
doneChan := make (chan string )
117
128
go func () {
118
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
129
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
119
130
Text :"Example" ,
120
131
},nil )
121
132
assert .NoError (t ,err )
122
133
doneChan <- resp
123
134
}()
124
135
ptty .ExpectMatch ("Example" )
125
136
ptty .WriteLine ("{a" )
126
- require .Equal (t ,"{a" ,<- doneChan )
137
+ resp := testutil .RequireRecvCtx (ctx ,t ,doneChan )
138
+ require .Equal (t ,"{a" ,resp )
127
139
})
128
140
129
141
t .Run ("MultilineJSON" ,func (t * testing.T ) {
130
142
t .Parallel ()
143
+ ctx := testutil .Context (t ,testutil .WaitShort )
131
144
ptty := ptytest .New (t )
132
145
doneChan := make (chan string )
133
146
go func () {
134
- resp ,err := newPrompt (ptty , cliui.PromptOptions {
147
+ resp ,err := newPrompt (ctx , ptty , cliui.PromptOptions {
135
148
Text :"Example" ,
136
149
},nil )
137
150
assert .NoError (t ,err )
@@ -141,11 +154,37 @@ func TestPrompt(t *testing.T) {
141
154
ptty .WriteLine (`{
142
155
"test": "wow"
143
156
}` )
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\n bar\n baz\n \n \n valid\n " )
182
+ resp := testutil .RequireRecvCtx (ctx ,t ,doneChan )
183
+ require .Equal (t ,"valid" ,resp )
145
184
})
146
185
}
147
186
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 ) {
149
188
value := ""
150
189
cmd := & serpent.Command {
151
190
Handler :func (inv * serpent.Invocation )error {
@@ -163,7 +202,7 @@ func newPrompt(ptty *ptytest.PTY, opts cliui.PromptOptions, invOpt func(inv *ser
163
202
inv .Stdout = ptty .Output ()
164
203
inv .Stderr = ptty .Output ()
165
204
inv .Stdin = ptty .Input ()
166
- return value ,inv .WithContext (context . Background () ).Run ()
205
+ return value ,inv .WithContext (ctx ).Run ()
167
206
}
168
207
169
208
func TestPasswordTerminalState (t * testing.T ) {