1
1
package tailnet_test
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"net"
7
+ "net/http"
8
+ "net/http/httptest"
6
9
"testing"
7
10
"time"
8
11
12
+ "nhooyr.io/websocket"
13
+
9
14
"cdr.dev/slog"
10
15
"cdr.dev/slog/sloggers/slogtest"
11
16
@@ -74,7 +79,10 @@ func TestCoordinator(t *testing.T) {
74
79
logger := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
75
80
coordinator := tailnet .NewCoordinator (logger )
76
81
77
- agentWS , agentServerWS := net .Pipe ()
82
+ // in this test we use real websockets to test use of deadlines
83
+ ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitSuperLong )
84
+ defer cancel ()
85
+ agentWS , agentServerWS := websocketConn (ctx , t )
78
86
defer agentWS .Close ()
79
87
agentNodeChan := make (chan []* tailnet.Node )
80
88
sendAgentNode , agentErrChan := tailnet .ServeCoordinator (agentWS , func (nodes []* tailnet.Node ) error {
@@ -93,7 +101,7 @@ func TestCoordinator(t *testing.T) {
93
101
return coordinator .Node (agentID ) != nil
94
102
}, testutil .WaitShort , testutil .IntervalFast )
95
103
96
- clientWS , clientServerWS := net . Pipe ( )
104
+ clientWS , clientServerWS := websocketConn ( ctx , t )
97
105
defer clientWS .Close ()
98
106
defer clientServerWS .Close ()
99
107
clientNodeChan := make (chan []* tailnet.Node )
@@ -108,16 +116,28 @@ func TestCoordinator(t *testing.T) {
108
116
assert .NoError (t , err )
109
117
close (closeClientChan )
110
118
}()
111
- agentNodes := <- clientNodeChan
112
- require .Len (t , agentNodes , 1 )
119
+ select {
120
+ case agentNodes := <- clientNodeChan :
121
+ require .Len (t , agentNodes , 1 )
122
+ case <- ctx .Done ():
123
+ t .Fatal ("timed out" )
124
+ }
113
125
sendClientNode (& tailnet.Node {})
114
126
clientNodes := <- agentNodeChan
115
127
require .Len (t , clientNodes , 1 )
116
128
129
+ // wait longer than the internal wait timeout.
130
+ // this tests for regression of https://github.com/coder/coder/issues/7428
131
+ time .Sleep (tailnet .WriteTimeout * 3 / 2 )
132
+
117
133
// Ensure an update to the agent node reaches the client!
118
134
sendAgentNode (& tailnet.Node {})
119
- agentNodes = <- clientNodeChan
120
- require .Len (t , agentNodes , 1 )
135
+ select {
136
+ case agentNodes := <- clientNodeChan :
137
+ require .Len (t , agentNodes , 1 )
138
+ case <- ctx .Done ():
139
+ t .Fatal ("timed out" )
140
+ }
121
141
122
142
// Close the agent WebSocket so a new one can connect.
123
143
err := agentWS .Close ()
@@ -334,3 +354,26 @@ func TestCoordinator_AgentUpdateWhileClientConnects(t *testing.T) {
334
354
require .Len (t , cNodes , 1 )
335
355
require .Equal (t , 1 , cNodes [0 ].PreferredDERP )
336
356
}
357
+
358
+ func websocketConn (ctx context.Context , t * testing.T ) (client net.Conn , server net.Conn ) {
359
+ t .Helper ()
360
+ sc := make (chan net.Conn , 1 )
361
+ s := httptest .NewServer (http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
362
+ wss , err := websocket .Accept (rw , r , nil )
363
+ require .NoError (t , err )
364
+ conn := websocket .NetConn (r .Context (), wss , websocket .MessageBinary )
365
+ sc <- conn
366
+ close (sc ) // there can be only one
367
+
368
+ // hold open until context canceled
369
+ <- ctx .Done ()
370
+ }))
371
+ t .Cleanup (s .Close )
372
+ // nolint: bodyclose
373
+ wsc , _ , err := websocket .Dial (ctx , s .URL , nil )
374
+ require .NoError (t , err )
375
+ client = websocket .NetConn (ctx , wsc , websocket .MessageBinary )
376
+ server , ok := <- sc
377
+ require .True (t , ok )
378
+ return client , server
379
+ }
0 commit comments