@@ -170,6 +170,55 @@ func TestCoder(t *testing.T) {
170
170
require .ErrorIs (t , err , context .DeadlineExceeded )
171
171
<- handlerDone
172
172
})
173
+
174
+ // In this test, we validate that a 401 error on the initial connect
175
+ // results in a retry. When envbuilder initially attempts to connect
176
+ // using the Coder agent token, the workspace build may not yet have
177
+ // completed.
178
+ t .Run ("V2Retry" , func (t * testing.T ) {
179
+ t .Parallel ()
180
+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
181
+ defer cancel ()
182
+
183
+ token := uuid .NewString ()
184
+ done := make (chan struct {})
185
+ handlerSend := make (chan int )
186
+ handler := func (w http.ResponseWriter , r * http.Request ) {
187
+ t .Logf ("test handler: %s" , r .URL .Path )
188
+ if r .URL .Path == "/api/v2/buildinfo" {
189
+ w .Header ().Set ("Content-Type" , "application/json" )
190
+ _ , _ = w .Write ([]byte (`{"version": "v2.9.0"}` ))
191
+ return
192
+ }
193
+ code := <- handlerSend
194
+ t .Logf ("test handler response: %d" , code )
195
+ w .WriteHeader (code )
196
+ }
197
+ srv := httptest .NewServer (http .HandlerFunc (handler ))
198
+ defer srv .Close ()
199
+
200
+ u , err := url .Parse (srv .URL )
201
+ require .NoError (t , err )
202
+ var connectError error
203
+ go func () {
204
+ defer close (handlerSend )
205
+ defer close (done )
206
+ _ , _ , connectError = Coder (ctx , u , token )
207
+ }()
208
+
209
+ // Initial: unauthorized
210
+ handlerSend <- http .StatusUnauthorized
211
+ // 2nd try: still unauthorized
212
+ handlerSend <- http .StatusUnauthorized
213
+ // 3rd try: authorized
214
+ handlerSend <- http .StatusOK
215
+
216
+ cancel ()
217
+
218
+ <- done
219
+ require .ErrorContains (t , connectError , "failed to WebSocket dial" )
220
+ require .ErrorIs (t , connectError , context .Canceled )
221
+ })
173
222
}
174
223
175
224
type fakeLogDest struct {
0 commit comments