@@ -9,6 +9,56 @@ import { ConnectionState as CS } from '../src/types/PubSub';
9
9
10
10
import { AWSAppSyncEventProvider } from '../src/Providers/AWSAppSyncEventsProvider' ;
11
11
12
+ // Mock all calls to signRequest
13
+ jest . mock ( '@aws-amplify/core/internals/aws-client-utils' , ( ) => {
14
+ const original = jest . requireActual (
15
+ '@aws-amplify/core/internals/aws-client-utils' ,
16
+ ) ;
17
+ return {
18
+ ...original ,
19
+ signRequest : ( _request : any , _options : any ) => {
20
+ return {
21
+ method : 'test' ,
22
+ headers : { test : 'test' } ,
23
+ url : new URL ( 'http://example/' ) ,
24
+ } ;
25
+ } ,
26
+ } ;
27
+ } ) ;
28
+
29
+ // Mock all calls to signRequest
30
+ jest . mock ( '@aws-amplify/core' , ( ) => {
31
+ const original = jest . requireActual ( '@aws-amplify/core' ) ;
32
+ const session = {
33
+ tokens : {
34
+ accessToken : {
35
+ toString : ( ) => 'test' ,
36
+ } ,
37
+ } ,
38
+ credentials : {
39
+ accessKeyId : 'test' ,
40
+ secretAccessKey : 'test' ,
41
+ } ,
42
+ } ;
43
+ return {
44
+ ...original ,
45
+ fetchAuthSession : ( _request : any , _options : any ) => {
46
+ return Promise . resolve ( session ) ;
47
+ } ,
48
+ Amplify : {
49
+ Auth : {
50
+ fetchAuthSession : async ( ) => session ,
51
+ } ,
52
+ } ,
53
+ browserOrNode ( ) {
54
+ return {
55
+ isBrowser : true ,
56
+ isNode : false ,
57
+ } ;
58
+ } ,
59
+ } ;
60
+ } ) ;
61
+
12
62
describe ( 'AppSyncEventProvider' , ( ) => {
13
63
describe ( 'subscribe()' , ( ) => {
14
64
describe ( 'returned observer' , ( ) => {
@@ -245,4 +295,210 @@ describe('AppSyncEventProvider', () => {
245
295
} ) ;
246
296
} ) ;
247
297
} ) ;
298
+ describe ( 'publish' , ( ) => {
299
+ let fakeWebSocketInterface : FakeWebSocketInterface ;
300
+ const loggerSpy : jest . SpyInstance = jest . spyOn (
301
+ ConsoleLogger . prototype ,
302
+ '_log' ,
303
+ ) ;
304
+ let provider : AWSAppSyncEventProvider ;
305
+ let reachabilityObserver : Observer < { online : boolean } > ;
306
+
307
+ beforeEach ( async ( ) => {
308
+ // Set the network to "online" for these tests
309
+ jest
310
+ . spyOn ( Reachability . prototype , 'networkMonitor' )
311
+ . mockImplementationOnce ( ( ) => {
312
+ return new Observable ( observer => {
313
+ reachabilityObserver = observer ;
314
+ } ) ;
315
+ } )
316
+ // Twice because we subscribe to get the initial state then again to monitor reachability
317
+ . mockImplementationOnce ( ( ) => {
318
+ return new Observable ( observer => {
319
+ reachabilityObserver = observer ;
320
+ } ) ;
321
+ } ) ;
322
+
323
+ fakeWebSocketInterface = new FakeWebSocketInterface ( ) ;
324
+ provider = new AWSAppSyncEventProvider ( ) ;
325
+ Object . defineProperty ( provider , 'socketStatus' , {
326
+ value : constants . SOCKET_STATUS . READY ,
327
+ } ) ;
328
+
329
+ Object . defineProperty ( provider , 'awsRealTimeSocket' , {
330
+ get : function ( ) {
331
+ fakeWebSocketInterface . newWebSocket ( ) ;
332
+ return fakeWebSocketInterface . webSocket ;
333
+ } ,
334
+ } ) ;
335
+
336
+ // Saving this spy and resetting it by hand causes badness
337
+ // Saving it causes new websockets to be reachable across past tests that have not fully closed
338
+ // Resetting it proactively causes those same past tests to be dealing with null while they reach a settled state
339
+
340
+ // Set the addEventListener() to do nothing for the test
341
+ jest
342
+ . spyOn ( fakeWebSocketInterface . webSocket , 'addEventListener' )
343
+ . mockImplementation ( ( ) => { } ) ;
344
+
345
+ jest . spyOn ( provider as any , '_getNewWebSocket' ) . mockImplementation ( ( ) => {
346
+ fakeWebSocketInterface . newWebSocket ( ) ;
347
+ return fakeWebSocketInterface . webSocket as WebSocket ;
348
+ } ) ;
349
+
350
+ // Reduce retry delay for tests to 100ms
351
+ Object . defineProperty ( constants , 'MAX_DELAY_MS' , {
352
+ value : 100 ,
353
+ } ) ;
354
+ // Reduce retry delay for tests to 100ms
355
+ Object . defineProperty ( constants , 'RECONNECT_DELAY' , {
356
+ value : 100 ,
357
+ } ) ;
358
+ } ) ;
359
+
360
+ afterEach ( async ( ) => {
361
+ provider ?. close ( ) ;
362
+ await fakeWebSocketInterface ?. closeInterface ( ) ;
363
+ fakeWebSocketInterface ?. teardown ( ) ;
364
+ loggerSpy . mockClear ( ) ;
365
+ } ) ;
366
+
367
+ test ( 'authenticating with AWS_IAM' , async ( ) => {
368
+ expect . assertions ( 1 ) ;
369
+ provider . publish ( {
370
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
371
+ query : 'events/test' ,
372
+ variables : { some : 'data' } ,
373
+ authenticationType : 'iam' ,
374
+ } ) ;
375
+
376
+ await fakeWebSocketInterface ?. readyForUse ;
377
+
378
+ expect ( loggerSpy ) . toHaveBeenCalledWith (
379
+ 'DEBUG' ,
380
+ 'Authenticating with "iam"' ,
381
+ ) ;
382
+ } ) ;
383
+
384
+ test ( 'authenticating with API_KEY' , async ( ) => {
385
+ expect . assertions ( 1 ) ;
386
+ fakeWebSocketInterface . webSocket . readyState = WebSocket . OPEN ;
387
+ provider . publish ( {
388
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
389
+ query : 'events/test' ,
390
+ variables : { some : 'data' } ,
391
+ authenticationType : 'apiKey' ,
392
+ apiKey : 'test' ,
393
+ } ) ;
394
+
395
+ await fakeWebSocketInterface ?. readyForUse ;
396
+
397
+ expect ( loggerSpy ) . toHaveBeenCalledWith (
398
+ 'DEBUG' ,
399
+ 'Authenticating with "apiKey"' ,
400
+ ) ;
401
+ } ) ;
402
+
403
+ test ( 'authenticating with OPENID_CONNECT' , async ( ) => {
404
+ expect . assertions ( 1 ) ;
405
+
406
+ provider . publish ( {
407
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
408
+ query : 'events/test' ,
409
+ variables : { some : 'data' } ,
410
+ authenticationType : 'oidc' ,
411
+ } ) ;
412
+
413
+ await fakeWebSocketInterface ?. readyForUse ;
414
+
415
+ expect ( loggerSpy ) . toHaveBeenCalledWith (
416
+ 'DEBUG' ,
417
+ 'Authenticating with "oidc"' ,
418
+ ) ;
419
+ } ) ;
420
+
421
+ test ( 'authenticating with AWS_LAMBDA/custom' , async ( ) => {
422
+ expect . assertions ( 1 ) ;
423
+
424
+ provider . publish ( {
425
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
426
+ query : 'events/test' ,
427
+ variables : { some : 'data' } ,
428
+ authenticationType : 'none' ,
429
+ additionalHeaders : {
430
+ Authorization : 'test' ,
431
+ } ,
432
+ } ) ;
433
+
434
+ await fakeWebSocketInterface ?. readyForUse ;
435
+
436
+ expect ( loggerSpy ) . toHaveBeenCalledWith (
437
+ 'DEBUG' ,
438
+ 'Authenticating with "none"' ,
439
+ ) ;
440
+ } ) ;
441
+
442
+ test ( 'authenticating with userPool / custom library options token' , async ( ) => {
443
+ expect . assertions ( 1 ) ;
444
+
445
+ provider . publish ( {
446
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
447
+ query : 'events/test' ,
448
+ variables : { some : 'data' } ,
449
+ authenticationType : 'userPool' ,
450
+ /**
451
+ * When Amplify is configured with a `header` function
452
+ * that returns an `Authorization` token, the GraphQL
453
+ * API will pass this function as the `libraryConfigHeaders`
454
+ * option to the AWSAppSyncRealTimeProvider's `subscribe`
455
+ * function.
456
+ */
457
+ libraryConfigHeaders : async ( ) => ( {
458
+ Authorization : 'test' ,
459
+ } ) ,
460
+ } ) ;
461
+
462
+ await fakeWebSocketInterface ?. readyForUse ;
463
+
464
+ expect ( loggerSpy ) . toHaveBeenCalledWith (
465
+ 'DEBUG' ,
466
+ 'Authenticating with "userPool"' ,
467
+ ) ;
468
+ } ) ;
469
+
470
+ test ( 'authenticating with AWS_LAMBDA/custom w/ custom header function' , async ( ) => {
471
+ expect . assertions ( 1 ) ;
472
+
473
+ provider . publish ( {
474
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
475
+ query : 'events/test' ,
476
+ variables : { some : 'data' } ,
477
+ authenticationType : 'none' ,
478
+ additionalHeaders : async ( ) => ( {
479
+ Authorization : 'test' ,
480
+ } ) ,
481
+ } ) ;
482
+
483
+ await fakeWebSocketInterface ?. readyForUse ;
484
+
485
+ expect ( loggerSpy ) . toBeCalledWith ( 'DEBUG' , 'Authenticating with "none"' ) ;
486
+ } ) ;
487
+
488
+ test ( 'authenticating with AWS_LAMBDA/custom without Authorization' , async ( ) => {
489
+ expect . assertions ( 1 ) ;
490
+
491
+ await expect (
492
+ provider . publish ( {
493
+ appSyncGraphqlEndpoint : 'ws://localhost:8080' ,
494
+ query : 'events/test' ,
495
+ variables : { some : 'data' } ,
496
+ authenticationType : 'none' ,
497
+ additionalHeaders : {
498
+ Authorization : '' ,
499
+ } ,
500
+ } ) ,
501
+ ) . rejects . toThrow ( 'No auth token specified' ) ;
502
+ } ) ;
503
+ } ) ;
248
504
} ) ;
0 commit comments