@@ -104,13 +104,22 @@ describe("api", () => {
104
104
false ,
105
105
] ,
106
106
[
107
- "should handle null/undefined config values" ,
107
+ "should handle null config values" ,
108
108
{ "coder.tlsCertFile" : null , "coder.tlsKeyFile" : null } ,
109
109
true ,
110
110
] ,
111
+ [
112
+ "should handle undefined config values" ,
113
+ { "coder.tlsCertFile" : undefined , "coder.tlsKeyFile" : undefined } ,
114
+ true ,
115
+ ] ,
116
+ [ "should handle missing config entries" , { } , true ] ,
111
117
] ) ( "%s" , ( _ , configValues : Record < string , unknown > , expected ) => {
112
118
mockConfiguration . get . mockImplementation ( ( key : string ) => {
113
- return configValues [ key ] ?? "" ;
119
+ if ( key in configValues ) {
120
+ return configValues [ key ] ;
121
+ }
122
+ return undefined ;
114
123
} ) ;
115
124
116
125
// Mock expandPath to return the path as-is
@@ -173,12 +182,32 @@ describe("api", () => {
173
182
rejectUnauthorized : true ,
174
183
} ,
175
184
] ,
185
+ [
186
+ "undefined configuration values" ,
187
+ {
188
+ "coder.tlsCertFile" : undefined ,
189
+ "coder.tlsKeyFile" : undefined ,
190
+ "coder.tlsCaFile" : undefined ,
191
+ "coder.tlsAltHost" : undefined ,
192
+ "coder.insecure" : undefined ,
193
+ } ,
194
+ {
195
+ cert : undefined ,
196
+ key : undefined ,
197
+ ca : undefined ,
198
+ servername : undefined ,
199
+ rejectUnauthorized : true ,
200
+ } ,
201
+ ] ,
176
202
] ) (
177
203
"should create ProxyAgent with %s" ,
178
204
async ( _ , configValues : Record < string , unknown > , expectedAgentConfig ) => {
179
- mockConfiguration . get . mockImplementation (
180
- ( key : string ) => configValues [ key ] ?? "" ,
181
- ) ;
205
+ mockConfiguration . get . mockImplementation ( ( key : string ) => {
206
+ if ( key in configValues ) {
207
+ return configValues [ key ] ;
208
+ }
209
+ return undefined ;
210
+ } ) ;
182
211
183
212
if ( configValues [ "coder.tlsCertFile" ] ) {
184
213
vi . mocked ( fs . readFile )
@@ -374,6 +403,171 @@ describe("api", () => {
374
403
expect . objectContaining ( { url : "https://example.com/api" } ) ,
375
404
) ;
376
405
} ) ;
406
+
407
+ it ( "should handle stream data events" , async ( ) => {
408
+ let dataHandler : ( chunk : Buffer ) => void ;
409
+ const mockData = {
410
+ on : vi . fn ( ( event : string , handler : ( chunk : Buffer ) => void ) => {
411
+ if ( event === "data" ) {
412
+ dataHandler = handler ;
413
+ }
414
+ } ) ,
415
+ destroy : vi . fn ( ) ,
416
+ } ;
417
+
418
+ const mockAxiosInstance = {
419
+ request : vi
420
+ . fn ( )
421
+ . mockResolvedValue ( createMockAxiosResponse ( { data : mockData } ) ) ,
422
+ } ;
423
+
424
+ const adapter = createStreamingFetchAdapter ( mockAxiosInstance as never ) ;
425
+
426
+ let enqueuedData : Buffer | undefined ;
427
+ global . ReadableStream = vi . fn ( ) . mockImplementation ( ( options ) => {
428
+ const controller = {
429
+ enqueue : vi . fn ( ( chunk : Buffer ) => {
430
+ enqueuedData = chunk ;
431
+ } ) ,
432
+ close : vi . fn ( ) ,
433
+ error : vi . fn ( ) ,
434
+ } ;
435
+ if ( options . start ) {
436
+ options . start ( controller ) ;
437
+ }
438
+ return { getReader : vi . fn ( ( ) => ( { read : vi . fn ( ) } ) ) } ;
439
+ } ) as never ;
440
+
441
+ await adapter ( "https://example.com/api" ) ;
442
+
443
+ // Simulate data event
444
+ const testData = Buffer . from ( "test data" ) ;
445
+ dataHandler ! ( testData ) ;
446
+
447
+ expect ( enqueuedData ) . toEqual ( testData ) ;
448
+ expect ( mockData . on ) . toHaveBeenCalledWith ( "data" , expect . any ( Function ) ) ;
449
+ } ) ;
450
+
451
+ it ( "should handle stream end event" , async ( ) => {
452
+ let endHandler : ( ) => void ;
453
+ const mockData = {
454
+ on : vi . fn ( ( event : string , handler : ( ) => void ) => {
455
+ if ( event === "end" ) {
456
+ endHandler = handler ;
457
+ }
458
+ } ) ,
459
+ destroy : vi . fn ( ) ,
460
+ } ;
461
+
462
+ const mockAxiosInstance = {
463
+ request : vi
464
+ . fn ( )
465
+ . mockResolvedValue ( createMockAxiosResponse ( { data : mockData } ) ) ,
466
+ } ;
467
+
468
+ const adapter = createStreamingFetchAdapter ( mockAxiosInstance as never ) ;
469
+
470
+ let streamClosed = false ;
471
+ global . ReadableStream = vi . fn ( ) . mockImplementation ( ( options ) => {
472
+ const controller = {
473
+ enqueue : vi . fn ( ) ,
474
+ close : vi . fn ( ( ) => {
475
+ streamClosed = true ;
476
+ } ) ,
477
+ error : vi . fn ( ) ,
478
+ } ;
479
+ if ( options . start ) {
480
+ options . start ( controller ) ;
481
+ }
482
+ return { getReader : vi . fn ( ( ) => ( { read : vi . fn ( ) } ) ) } ;
483
+ } ) as never ;
484
+
485
+ await adapter ( "https://example.com/api" ) ;
486
+
487
+ // Simulate end event
488
+ endHandler ! ( ) ;
489
+
490
+ expect ( streamClosed ) . toBe ( true ) ;
491
+ expect ( mockData . on ) . toHaveBeenCalledWith ( "end" , expect . any ( Function ) ) ;
492
+ } ) ;
493
+
494
+ it ( "should handle stream error event" , async ( ) => {
495
+ let errorHandler : ( err : Error ) => void ;
496
+ const mockData = {
497
+ on : vi . fn ( ( event : string , handler : ( err : Error ) => void ) => {
498
+ if ( event === "error" ) {
499
+ errorHandler = handler ;
500
+ }
501
+ } ) ,
502
+ destroy : vi . fn ( ) ,
503
+ } ;
504
+
505
+ const mockAxiosInstance = {
506
+ request : vi
507
+ . fn ( )
508
+ . mockResolvedValue ( createMockAxiosResponse ( { data : mockData } ) ) ,
509
+ } ;
510
+
511
+ const adapter = createStreamingFetchAdapter ( mockAxiosInstance as never ) ;
512
+
513
+ let streamError : Error | undefined ;
514
+ global . ReadableStream = vi . fn ( ) . mockImplementation ( ( options ) => {
515
+ const controller = {
516
+ enqueue : vi . fn ( ) ,
517
+ close : vi . fn ( ) ,
518
+ error : vi . fn ( ( err : Error ) => {
519
+ streamError = err ;
520
+ } ) ,
521
+ } ;
522
+ if ( options . start ) {
523
+ options . start ( controller ) ;
524
+ }
525
+ return { getReader : vi . fn ( ( ) => ( { read : vi . fn ( ) } ) ) } ;
526
+ } ) as never ;
527
+
528
+ await adapter ( "https://example.com/api" ) ;
529
+
530
+ // Simulate error event
531
+ const testError = new Error ( "Stream error" ) ;
532
+ errorHandler ! ( testError ) ;
533
+
534
+ expect ( streamError ) . toBe ( testError ) ;
535
+ expect ( mockData . on ) . toHaveBeenCalledWith ( "error" , expect . any ( Function ) ) ;
536
+ } ) ;
537
+
538
+ it ( "should handle stream cancel" , async ( ) => {
539
+ const mockData = {
540
+ on : vi . fn ( ) ,
541
+ destroy : vi . fn ( ) ,
542
+ } ;
543
+
544
+ const mockAxiosInstance = {
545
+ request : vi
546
+ . fn ( )
547
+ . mockResolvedValue ( createMockAxiosResponse ( { data : mockData } ) ) ,
548
+ } ;
549
+
550
+ const adapter = createStreamingFetchAdapter ( mockAxiosInstance as never ) ;
551
+
552
+ let cancelFunction : ( ( ) => Promise < void > ) | undefined ;
553
+ global . ReadableStream = vi . fn ( ) . mockImplementation ( ( options ) => {
554
+ if ( options . cancel ) {
555
+ cancelFunction = options . cancel ;
556
+ }
557
+ if ( options . start ) {
558
+ options . start ( { enqueue : vi . fn ( ) , close : vi . fn ( ) , error : vi . fn ( ) } ) ;
559
+ }
560
+ return { getReader : vi . fn ( ( ) => ( { read : vi . fn ( ) } ) ) } ;
561
+ } ) as never ;
562
+
563
+ await adapter ( "https://example.com/api" ) ;
564
+
565
+ // Call cancel
566
+ expect ( cancelFunction ) . toBeDefined ( ) ;
567
+ await cancelFunction ! ( ) ;
568
+
569
+ expect ( mockData . destroy ) . toHaveBeenCalled ( ) ;
570
+ } ) ;
377
571
} ) ;
378
572
379
573
describe ( "startWorkspaceIfStoppedOrFailed" , ( ) => {
0 commit comments