@@ -5,12 +5,14 @@ import (
5
5
"bytes"
6
6
"context"
7
7
"io"
8
+ "os"
8
9
"strings"
9
10
"sync/atomic"
10
11
"testing"
11
12
"time"
12
13
13
14
"github.com/google/uuid"
15
+ "github.com/stretchr/testify/assert"
14
16
"github.com/stretchr/testify/require"
15
17
"golang.org/x/xerrors"
16
18
@@ -25,9 +27,31 @@ import (
25
27
func TestAgent (t * testing.T ) {
26
28
t .Parallel ()
27
29
30
+ waitLines := func (t * testing.T ,output <- chan string ,lines ... string )error {
31
+ t .Helper ()
32
+
33
+ var got []string
34
+ outerLoop:
35
+ for _ ,want := range lines {
36
+ for {
37
+ select {
38
+ case line := <- output :
39
+ got = append (got ,line )
40
+ if strings .Contains (line ,want ) {
41
+ continue outerLoop
42
+ }
43
+ case <- time .After (testutil .WaitShort ):
44
+ assert .Failf (t ,"timed out waiting for line" ,"want: %q; got: %q" ,want ,got )
45
+ return xerrors .Errorf ("timed out waiting for line: %q; got: %q" ,want ,got )
46
+ }
47
+ }
48
+ }
49
+ return nil
50
+ }
51
+
28
52
for _ ,tc := range []struct {
29
53
name string
30
- iter []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error
54
+ iter []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error
31
55
logs chan []codersdk.WorkspaceAgentLog
32
56
opts cliui.AgentOptions
33
57
want []string
@@ -38,12 +62,15 @@ func TestAgent(t *testing.T) {
38
62
opts : cliui.AgentOptions {
39
63
FetchInterval :time .Millisecond ,
40
64
},
41
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
42
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
65
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
66
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
43
67
agent .Status = codersdk .WorkspaceAgentConnecting
44
68
return nil
45
69
},
46
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
70
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
71
+ return waitLines (t ,output ,"⧗ Waiting for the workspace agent to connect" )
72
+ },
73
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
47
74
agent .Status = codersdk .WorkspaceAgentConnected
48
75
agent .FirstConnectedAt = ptr .Ref (time .Now ())
49
76
return nil
@@ -62,12 +89,15 @@ func TestAgent(t *testing.T) {
62
89
opts : cliui.AgentOptions {
63
90
FetchInterval :time .Millisecond ,
64
91
},
65
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
66
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
92
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
93
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
67
94
agent .Status = codersdk .WorkspaceAgentConnecting
68
95
return nil
69
96
},
70
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
97
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
98
+ return waitLines (t ,output ,"⧗ Waiting for the workspace agent to connect" )
99
+ },
100
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
71
101
agent .Status = codersdk .WorkspaceAgentConnected
72
102
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleStartTimeout
73
103
agent .FirstConnectedAt = ptr .Ref (time .Now ())
@@ -87,18 +117,24 @@ func TestAgent(t *testing.T) {
87
117
opts : cliui.AgentOptions {
88
118
FetchInterval :1 * time .Millisecond ,
89
119
},
90
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
91
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
120
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
121
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
92
122
agent .Status = codersdk .WorkspaceAgentConnecting
93
123
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleStarting
94
124
agent .StartedAt = ptr .Ref (time .Now ())
95
125
return nil
96
126
},
97
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
127
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
128
+ return waitLines (t ,output ,"⧗ Waiting for the workspace agent to connect" )
129
+ },
130
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
98
131
agent .Status = codersdk .WorkspaceAgentTimeout
99
132
return nil
100
133
},
101
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
134
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
135
+ return waitLines (t ,output ,"The workspace agent is having trouble connecting, wait for it to connect or restart your workspace." )
136
+ },
137
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
102
138
agent .Status = codersdk .WorkspaceAgentConnected
103
139
agent .FirstConnectedAt = ptr .Ref (time .Now ())
104
140
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleReady
@@ -120,8 +156,8 @@ func TestAgent(t *testing.T) {
120
156
opts : cliui.AgentOptions {
121
157
FetchInterval :1 * time .Millisecond ,
122
158
},
123
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
124
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
159
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
160
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
125
161
agent .Status = codersdk .WorkspaceAgentDisconnected
126
162
agent .FirstConnectedAt = ptr .Ref (time .Now ().Add (- 1 * time .Minute ))
127
163
agent .LastConnectedAt = ptr .Ref (time .Now ().Add (- 1 * time .Minute ))
@@ -131,7 +167,10 @@ func TestAgent(t *testing.T) {
131
167
agent .ReadyAt = ptr .Ref (time .Now ())
132
168
return nil
133
169
},
134
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
170
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
171
+ return waitLines (t ,output ,"⧗ The workspace agent lost connection" )
172
+ },
173
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
135
174
agent .Status = codersdk .WorkspaceAgentConnected
136
175
agent .DisconnectedAt = nil
137
176
agent .LastConnectedAt = ptr .Ref (time .Now ())
@@ -151,8 +190,8 @@ func TestAgent(t *testing.T) {
151
190
FetchInterval :time .Millisecond ,
152
191
Wait :true ,
153
192
},
154
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
155
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
193
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
194
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,logs chan []codersdk.WorkspaceAgentLog )error {
156
195
agent .Status = codersdk .WorkspaceAgentConnected
157
196
agent .FirstConnectedAt = ptr .Ref (time .Now ())
158
197
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleStarting
@@ -170,7 +209,7 @@ func TestAgent(t *testing.T) {
170
209
}
171
210
return nil
172
211
},
173
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
212
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,logs chan []codersdk.WorkspaceAgentLog )error {
174
213
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleReady
175
214
agent .ReadyAt = ptr .Ref (time .Now ())
176
215
logs <- []codersdk.WorkspaceAgentLog {
@@ -195,8 +234,8 @@ func TestAgent(t *testing.T) {
195
234
FetchInterval :time .Millisecond ,
196
235
Wait :true ,
197
236
},
198
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
199
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
237
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
238
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , output <- chan string ,logs chan []codersdk.WorkspaceAgentLog )error {
200
239
agent .Status = codersdk .WorkspaceAgentConnected
201
240
agent .FirstConnectedAt = ptr .Ref (time .Now ())
202
241
agent .StartedAt = ptr .Ref (time .Now ())
@@ -224,8 +263,8 @@ func TestAgent(t *testing.T) {
224
263
opts : cliui.AgentOptions {
225
264
FetchInterval :time .Millisecond ,
226
265
},
227
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
228
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
266
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
267
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , output <- chan string ,logs chan []codersdk.WorkspaceAgentLog )error {
229
268
agent .Status = codersdk .WorkspaceAgentDisconnected
230
269
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleOff
231
270
return nil
@@ -239,8 +278,8 @@ func TestAgent(t *testing.T) {
239
278
FetchInterval :time .Millisecond ,
240
279
Wait :true ,
241
280
},
242
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
243
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
281
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
282
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , output <- chan string ,logs chan []codersdk.WorkspaceAgentLog )error {
244
283
agent .Status = codersdk .WorkspaceAgentConnected
245
284
agent .FirstConnectedAt = ptr .Ref (time .Now ())
246
285
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleStarting
@@ -253,7 +292,10 @@ func TestAgent(t *testing.T) {
253
292
}
254
293
return nil
255
294
},
256
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,logs chan []codersdk.WorkspaceAgentLog )error {
295
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
296
+ return waitLines (t ,output ,"Hello world" )
297
+ },
298
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
257
299
agent .ReadyAt = ptr .Ref (time .Now ())
258
300
agent .LifecycleState = codersdk .WorkspaceAgentLifecycleShuttingDown
259
301
return nil
@@ -272,12 +314,15 @@ func TestAgent(t *testing.T) {
272
314
FetchInterval :time .Millisecond ,
273
315
Wait :true ,
274
316
},
275
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
276
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
317
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
318
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
277
319
agent .Status = codersdk .WorkspaceAgentConnecting
278
320
return nil
279
321
},
280
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
322
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
323
+ return waitLines (t ,output ,"⧗ Waiting for the workspace agent to connect" )
324
+ },
325
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,_ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
281
326
return xerrors .New ("bad" )
282
327
},
283
328
},
@@ -292,13 +337,16 @@ func TestAgent(t *testing.T) {
292
337
FetchInterval :time .Millisecond ,
293
338
Wait :true ,
294
339
},
295
- iter : []func (context.Context ,* codersdk.WorkspaceAgent ,chan []codersdk.WorkspaceAgentLog )error {
296
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
340
+ iter : []func (context.Context ,* testing. T , * codersdk.WorkspaceAgent , <- chan string ,chan []codersdk.WorkspaceAgentLog )error {
341
+ func (_ context.Context ,_ * testing. T , agent * codersdk.WorkspaceAgent , _ <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
297
342
agent .Status = codersdk .WorkspaceAgentTimeout
298
343
agent .TroubleshootingURL = "https://troubleshoot"
299
344
return nil
300
345
},
301
- func (_ context.Context ,agent * codersdk.WorkspaceAgent ,_ chan []codersdk.WorkspaceAgentLog )error {
346
+ func (_ context.Context ,t * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
347
+ return waitLines (t ,output ,"The workspace agent is having trouble connecting, wait for it to connect or restart your workspace." )
348
+ },
349
+ func (_ context.Context ,_ * testing.T ,agent * codersdk.WorkspaceAgent ,output <- chan string ,_ chan []codersdk.WorkspaceAgentLog )error {
302
350
return xerrors .New ("bad" )
303
351
},
304
352
},
@@ -317,21 +365,27 @@ func TestAgent(t *testing.T) {
317
365
ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitShort )
318
366
defer cancel ()
319
367
320
- var buf bytes.Buffer
368
+ r ,w ,err := os .Pipe ()
369
+ require .NoError (t ,err ,"create pipe failed" )
370
+ defer r .Close ()
371
+ defer w .Close ()
372
+
321
373
agent := codersdk.WorkspaceAgent {
322
374
ID :uuid .New (),
323
375
Status :codersdk .WorkspaceAgentConnecting ,
324
376
CreatedAt :time .Now (),
325
377
LifecycleState :codersdk .WorkspaceAgentLifecycleCreated ,
326
378
}
379
+ output := make (chan string ,100 )// Buffered to avoid blocking, overflow is discarded.
327
380
logs := make (chan []codersdk.WorkspaceAgentLog ,1 )
328
381
329
382
cmd := & clibase.Cmd {
330
383
Handler :func (inv * clibase.Invocation )error {
331
384
tc .opts .Fetch = func (_ context.Context ,_ uuid.UUID ) (codersdk.WorkspaceAgent ,error ) {
385
+ t .Log ("iter" ,len (tc .iter ))
332
386
var err error
333
387
if len (tc .iter )> 0 {
334
- err = tc .iter [0 ](ctx ,& agent ,logs )
388
+ err = tc .iter [0 ](ctx ,t , & agent , output ,logs )
335
389
tc .iter = tc .iter [1 :]
336
390
}
337
391
return agent ,err
@@ -352,27 +406,25 @@ func TestAgent(t *testing.T) {
352
406
close (fetchLogs )
353
407
return fetchLogs ,closeFunc (func ()error {return nil }),nil
354
408
}
355
- err := cliui .Agent (inv .Context (),& buf ,uuid .Nil ,tc .opts )
409
+ err := cliui .Agent (inv .Context (),w ,uuid .Nil ,tc .opts )
410
+ _ = w .Close ()
356
411
return err
357
412
},
358
413
}
359
414
inv := cmd .Invoke ()
360
415
361
- w := clitest .StartWithWaiter (t ,inv )
362
- if tc .wantErr {
363
- w .RequireError ()
364
- }else {
365
- w .RequireSuccess ()
366
- }
416
+ waiter := clitest .StartWithWaiter (t ,inv )
367
417
368
- s := bufio .NewScanner (& buf )
418
+ s := bufio .NewScanner (r )
369
419
for s .Scan () {
370
420
line := s .Text ()
371
421
t .Log (line )
422
+ select {
423
+ case output <- line :
424
+ default :
425
+ t .Logf ("output overflow: %s" ,line )
426
+ }
372
427
if len (tc .want )== 0 {
373
- for i := 0 ;i < 5 ;i ++ {
374
- t .Log (line )
375
- }
376
428
require .Fail (t ,"unexpected line" ,line )
377
429
}
378
430
require .Contains (t ,line ,tc .want [0 ])
@@ -382,6 +434,12 @@ func TestAgent(t *testing.T) {
382
434
if len (tc .want )> 0 {
383
435
require .Fail (t ,"missing lines: " + strings .Join (tc .want ,", " ))
384
436
}
437
+
438
+ if tc .wantErr {
439
+ waiter .RequireError ()
440
+ }else {
441
+ waiter .RequireSuccess ()
442
+ }
385
443
})
386
444
}
387
445