1
1
package cli_test
2
2
3
3
import (
4
- "bytes"
5
4
"context"
6
5
"fmt"
7
6
"io"
8
7
"net"
9
- "strings"
10
8
"sync"
11
9
"testing"
12
- "time"
13
10
14
11
"github.com/google/uuid"
15
12
"github.com/pion/udp"
@@ -21,6 +18,7 @@ import (
21
18
"github.com/coder/coder/codersdk"
22
19
"github.com/coder/coder/provisioner/echo"
23
20
"github.com/coder/coder/provisionersdk/proto"
21
+ "github.com/coder/coder/pty/ptytest"
24
22
"github.com/coder/coder/testutil"
25
23
)
26
24
@@ -35,15 +33,17 @@ func TestPortForward(t *testing.T) {
35
33
36
34
cmd , root := clitest .New (t , "port-forward" , "blah" )
37
35
clitest .SetupConfig (t , client , root )
38
- buf := newThreadSafeBuffer ()
39
- cmd .SetOut (buf )
36
+ pty := ptytest .New (t )
37
+ cmd .SetIn (pty .Input ())
38
+ cmd .SetOut (pty .Output ())
39
+ cmd .SetErr (pty .Output ())
40
40
41
41
err := cmd .Execute ()
42
42
require .Error (t , err )
43
43
require .ErrorContains (t , err , "no port-forwards" )
44
44
45
45
// Check that the help was printed.
46
- require . Contains ( t , buf . String (), "port-forward <workspace>" )
46
+ pty . ExpectMatch ( "port-forward <workspace>" )
47
47
})
48
48
49
49
cases := []struct {
@@ -135,15 +135,17 @@ func TestPortForward(t *testing.T) {
135
135
// the "local" listener.
136
136
cmd , root := clitest .New (t , "-v" , "port-forward" , workspace .Name , flag )
137
137
clitest .SetupConfig (t , client , root )
138
- buf := newThreadSafeBuffer ()
139
- cmd .SetOut (buf )
138
+ pty := ptytest .New (t )
139
+ cmd .SetIn (pty .Input ())
140
+ cmd .SetOut (pty .Output ())
141
+ cmd .SetErr (pty .Output ())
140
142
ctx , cancel := context .WithCancel (context .Background ())
141
143
defer cancel ()
142
144
errC := make (chan error )
143
145
go func () {
144
146
errC <- cmd .ExecuteContext (ctx )
145
147
}()
146
- waitForPortForwardReady ( t , buf )
148
+ pty . ExpectMatch ( "Ready!" )
147
149
148
150
t .Parallel () // Port is reserved, enable parallel execution.
149
151
@@ -181,15 +183,17 @@ func TestPortForward(t *testing.T) {
181
183
// the "local" listeners.
182
184
cmd , root := clitest .New (t , "-v" , "port-forward" , workspace .Name , flag1 , flag2 )
183
185
clitest .SetupConfig (t , client , root )
184
- buf := newThreadSafeBuffer ()
185
- cmd .SetOut (buf )
186
+ pty := ptytest .New (t )
187
+ cmd .SetIn (pty .Input ())
188
+ cmd .SetOut (pty .Output ())
189
+ cmd .SetErr (pty .Output ())
186
190
ctx , cancel := context .WithCancel (context .Background ())
187
191
defer cancel ()
188
192
errC := make (chan error )
189
193
go func () {
190
194
errC <- cmd .ExecuteContext (ctx )
191
195
}()
192
- waitForPortForwardReady ( t , buf )
196
+ pty . ExpectMatch ( "Ready!" )
193
197
194
198
t .Parallel () // Port is reserved, enable parallel execution.
195
199
@@ -236,15 +240,17 @@ func TestPortForward(t *testing.T) {
236
240
// the "local" listeners.
237
241
cmd , root := clitest .New (t , append ([]string {"-v" , "port-forward" , workspace .Name }, flags ... )... )
238
242
clitest .SetupConfig (t , client , root )
239
- buf := newThreadSafeBuffer ()
240
- cmd .SetOut (buf )
243
+ pty := ptytest .New (t )
244
+ cmd .SetIn (pty .Input ())
245
+ cmd .SetOut (pty .Output ())
246
+ cmd .SetErr (pty .Output ())
241
247
ctx , cancel := context .WithCancel (context .Background ())
242
248
defer cancel ()
243
249
errC := make (chan error )
244
250
go func () {
245
251
errC <- cmd .ExecuteContext (ctx )
246
252
}()
247
- waitForPortForwardReady ( t , buf )
253
+ pty . ExpectMatch ( "Ready!" )
248
254
249
255
t .Parallel () // Port is reserved, enable parallel execution.
250
256
@@ -313,6 +319,10 @@ func runAgent(t *testing.T, client *codersdk.Client, userID uuid.UUID) ([]coders
313
319
// Start workspace agent in a goroutine
314
320
cmd , root := clitest .New (t , "agent" , "--agent-token" , agentToken , "--agent-url" , client .URL .String ())
315
321
clitest .SetupConfig (t , client , root )
322
+ pty := ptytest .New (t )
323
+ cmd .SetIn (pty .Input ())
324
+ cmd .SetOut (pty .Output ())
325
+ cmd .SetErr (pty .Output ())
316
326
errC := make (chan error )
317
327
agentCtx , agentCancel := context .WithCancel (ctx )
318
328
t .Cleanup (func () {
@@ -404,61 +414,7 @@ func assertWritePayload(t *testing.T, w io.Writer, payload []byte) {
404
414
assert .Equal (t , len (payload ), n , "payload length does not match" )
405
415
}
406
416
407
- func waitForPortForwardReady (t * testing.T , output * threadSafeBuffer ) {
408
- t .Helper ()
409
- for i := 0 ; i < 100 ; i ++ {
410
- time .Sleep (testutil .IntervalMedium )
411
-
412
- data := output .String ()
413
- if strings .Contains (data , "Ready!" ) {
414
- return
415
- }
416
- }
417
-
418
- t .Fatal ("port-forward command did not become ready in time" )
419
- }
420
-
421
417
type addr struct {
422
418
network string
423
419
addr string
424
420
}
425
-
426
- type threadSafeBuffer struct {
427
- b * bytes.Buffer
428
- mut * sync.RWMutex
429
- }
430
-
431
- func newThreadSafeBuffer () * threadSafeBuffer {
432
- return & threadSafeBuffer {
433
- b : bytes .NewBuffer (nil ),
434
- mut : new (sync.RWMutex ),
435
- }
436
- }
437
-
438
- var (
439
- _ io.Reader = & threadSafeBuffer {}
440
- _ io.Writer = & threadSafeBuffer {}
441
- )
442
-
443
- // Read implements io.Reader.
444
- func (b * threadSafeBuffer ) Read (p []byte ) (int , error ) {
445
- b .mut .RLock ()
446
- defer b .mut .RUnlock ()
447
-
448
- return b .b .Read (p )
449
- }
450
-
451
- // Write implements io.Writer.
452
- func (b * threadSafeBuffer ) Write (p []byte ) (int , error ) {
453
- b .mut .Lock ()
454
- defer b .mut .Unlock ()
455
-
456
- return b .b .Write (p )
457
- }
458
-
459
- func (b * threadSafeBuffer ) String () string {
460
- b .mut .RLock ()
461
- defer b .mut .RUnlock ()
462
-
463
- return b .b .String ()
464
- }
0 commit comments