@@ -21,6 +21,7 @@ import DirectConnectionProvider from '../../src/connection-provider/connection-p
21
21
import { Pool } from '../../src/pool'
22
22
import { Connection , DelegateConnection } from '../../src/connection'
23
23
import { internal , newError , ServerInfo } from 'neo4j-driver-core'
24
+ import AuthenticationProvider from '../../src/connection-provider/authentication-provider'
24
25
25
26
const {
26
27
serverAddress : { ServerAddress } ,
@@ -376,6 +377,236 @@ describe('.verifyConnectivityAndGetServerInfo()', () => {
376
377
}
377
378
} )
378
379
} )
380
+
381
+ describe ( 'constructor' , ( ) => {
382
+ describe ( 'newPool' , ( ) => {
383
+ const server0 = ServerAddress . fromUrl ( 'localhost:123' )
384
+ const server01 = ServerAddress . fromUrl ( 'localhost:1235' )
385
+
386
+ describe ( 'param.create' , ( ) => {
387
+ it ( 'should create connection' , async ( ) => {
388
+ const { create, createChannelConnectionHook, provider } = setup ( )
389
+
390
+ const connection = await create ( { } , server0 , undefined )
391
+
392
+ expect ( createChannelConnectionHook ) . toHaveBeenCalledWith ( server0 )
393
+ expect ( provider . _openConnections [ connection . id ] ) . toBe ( connection )
394
+ await expect ( createChannelConnectionHook . mock . results [ 0 ] . value ) . resolves . toBe ( connection )
395
+ } )
396
+
397
+ it ( 'should register the release function into the connection' , async ( ) => {
398
+ const { create } = setup ( )
399
+ const releaseResult = { property : 'some property' }
400
+ const release = jest . fn ( ( ) => releaseResult )
401
+
402
+ const connection = await create ( { } , server0 , release )
403
+
404
+ const released = connection . _release ( )
405
+
406
+ expect ( released ) . toBe ( releaseResult )
407
+ expect ( release ) . toHaveBeenCalledWith ( server0 , connection )
408
+ } )
409
+
410
+ it . each ( [
411
+ null ,
412
+ undefined ,
413
+ { scheme : 'bearer' , credentials : 'token01' }
414
+ ] ) ( 'should authenticate connection (auth = %o)' , async ( auth ) => {
415
+ const { create, authenticationProviderHook } = setup ( )
416
+
417
+ const connection = await create ( { auth } , server0 )
418
+
419
+ expect ( authenticationProviderHook . authenticate ) . toHaveBeenCalledWith ( {
420
+ connection,
421
+ auth
422
+ } )
423
+ } )
424
+
425
+ it ( 'should handle create connection failures' , async ( ) => {
426
+ const error = newError ( 'some error' )
427
+ const createConnection = jest . fn ( ( ) => Promise . reject ( error ) )
428
+ const { create, authenticationProviderHook, provider } = setup ( { createConnection } )
429
+ const openConnections = { ...provider . _openConnections }
430
+
431
+ await expect ( create ( { } , server0 ) ) . rejects . toThrow ( error )
432
+
433
+ expect ( authenticationProviderHook . authenticate ) . not . toHaveBeenCalled ( )
434
+ expect ( provider . _openConnections ) . toEqual ( openConnections )
435
+ } )
436
+
437
+ it . each ( [
438
+ null ,
439
+ undefined ,
440
+ { scheme : 'bearer' , credentials : 'token01' }
441
+ ] ) ( 'should handle authentication failures (auth = %o)' , async ( auth ) => {
442
+ const error = newError ( 'some error' )
443
+ const authenticationProvider = jest . fn ( ( ) => Promise . reject ( error ) )
444
+ const { create, authenticationProviderHook, createChannelConnectionHook, provider } = setup ( { authenticationProvider } )
445
+ const openConnections = { ...provider . _openConnections }
446
+
447
+ await expect ( create ( { auth } , server0 ) ) . rejects . toThrow ( error )
448
+
449
+ const connection = await createChannelConnectionHook . mock . results [ 0 ] . value
450
+ expect ( authenticationProviderHook . authenticate ) . toHaveBeenCalledWith ( { auth, connection } )
451
+ expect ( provider . _openConnections ) . toEqual ( openConnections )
452
+ expect ( connection . _closed ) . toBe ( true )
453
+ } )
454
+ } )
455
+
456
+ describe ( 'param.destroy' , ( ) => {
457
+ it ( 'should close connection and unregister it' , async ( ) => {
458
+ const { create, destroy, provider } = setup ( )
459
+ const openConnections = { ...provider . _openConnections }
460
+ const connection = await create ( { } , server0 , undefined )
461
+
462
+ await destroy ( connection )
463
+
464
+ expect ( connection . _closed ) . toBe ( true )
465
+ expect ( provider . _openConnections ) . toEqual ( openConnections )
466
+ } )
467
+ } )
468
+
469
+ describe ( 'param.validateOnAcquire' , ( ) => {
470
+ it . each ( [
471
+ null ,
472
+ undefined ,
473
+ { scheme : 'bearer' , credentials : 'token01' }
474
+ ] ) ( 'should return true when connection is open and within the lifetime and authentication succeed (auth=%o)' , async ( auth ) => {
475
+ const connection = new FakeConnection ( server0 )
476
+ connection . creationTimestamp = Date . now ( )
477
+
478
+ const { validateOnAcquire, authenticationProviderHook } = setup ( )
479
+
480
+ await expect ( validateOnAcquire ( { auth } , connection ) ) . resolves . toBe ( true )
481
+
482
+ expect ( authenticationProviderHook . authenticate ) . toHaveBeenCalledWith ( {
483
+ connection, auth
484
+ } )
485
+ } )
486
+
487
+ it . each ( [
488
+ null ,
489
+ undefined ,
490
+ { scheme : 'bearer' , credentials : 'token01' }
491
+ ] ) ( 'should return true when connection is open and within the lifetime and authentication fails (auth=%o)' , async ( auth ) => {
492
+ const connection = new FakeConnection ( server0 )
493
+ const error = newError ( 'failed' )
494
+ const authenticationProvider = jest . fn ( ( ) => Promise . reject ( error ) )
495
+ connection . creationTimestamp = Date . now ( )
496
+
497
+ const { validateOnAcquire, authenticationProviderHook, log } = setup ( { authenticationProvider } )
498
+
499
+ await expect ( validateOnAcquire ( { auth } , connection ) ) . resolves . toBe ( false )
500
+
501
+ expect ( authenticationProviderHook . authenticate ) . toHaveBeenCalledWith ( {
502
+ connection, auth
503
+ } )
504
+
505
+ expect ( log . debug ) . toHaveBeenCalledWith (
506
+ `The connection ${ connection . id } is not valid because of an error ${ error . code } '${ error . message } '`
507
+ )
508
+ } )
509
+ it ( 'should return false when connection is closed and within the lifetime' , async ( ) => {
510
+ const connection = new FakeConnection ( server0 )
511
+ connection . creationTimestamp = Date . now ( )
512
+ await connection . close ( )
513
+
514
+ const { validateOnAcquire, authenticationProviderHook } = setup ( )
515
+
516
+ await expect ( validateOnAcquire ( { } , connection ) ) . resolves . toBe ( false )
517
+ expect ( authenticationProviderHook . authenticate ) . not . toHaveBeenCalled ( )
518
+ } )
519
+
520
+ it ( 'should return false when connection is open and out of the lifetime' , async ( ) => {
521
+ const connection = new FakeConnection ( server0 )
522
+ connection . creationTimestamp = Date . now ( ) - 4000
523
+
524
+ const { validateOnAcquire, authenticationProviderHook } = setup ( { maxConnectionLifetime : 3000 } )
525
+
526
+ await expect ( validateOnAcquire ( { } , connection ) ) . resolves . toBe ( false )
527
+ expect ( authenticationProviderHook . authenticate ) . not . toHaveBeenCalled ( )
528
+ } )
529
+
530
+ it ( 'should return false when connection is closed and out of the lifetime' , async ( ) => {
531
+ const connection = new FakeConnection ( server0 )
532
+ await connection . close ( )
533
+ connection . creationTimestamp = Date . now ( ) - 4000
534
+
535
+ const { validateOnAcquire, authenticationProviderHook } = setup ( { maxConnectionLifetime : 3000 } )
536
+
537
+ await expect ( validateOnAcquire ( { } , connection ) ) . resolves . toBe ( false )
538
+ expect ( authenticationProviderHook . authenticate ) . not . toHaveBeenCalled ( )
539
+ } )
540
+ } )
541
+
542
+ describe ( 'param.validateOnRelease' , ( ) => {
543
+ it ( 'should return true when connection is open and within the lifetime' , ( ) => {
544
+ const connection = new FakeConnection ( server0 )
545
+ connection . creationTimestamp = Date . now ( )
546
+
547
+ const { validateOnRelease } = setup ( )
548
+
549
+ expect ( validateOnRelease ( connection ) ) . toBe ( true )
550
+ } )
551
+
552
+ it ( 'should return false when connection is closed and within the lifetime' , async ( ) => {
553
+ const connection = new FakeConnection ( server0 )
554
+ connection . creationTimestamp = Date . now ( )
555
+ await connection . close ( )
556
+
557
+ const { validateOnRelease } = setup ( )
558
+
559
+ expect ( validateOnRelease ( connection ) ) . toBe ( false )
560
+ } )
561
+
562
+ it ( 'should return false when connection is open and out of the lifetime' , ( ) => {
563
+ const connection = new FakeConnection ( server0 )
564
+ connection . creationTimestamp = Date . now ( ) - 4000
565
+
566
+ const { validateOnRelease } = setup ( { maxConnectionLifetime : 3000 } )
567
+
568
+ expect ( validateOnRelease ( connection ) ) . toBe ( false )
569
+ } )
570
+
571
+ it ( 'should return false when connection is closed and out of the lifetime' , async ( ) => {
572
+ const connection = new FakeConnection ( server0 )
573
+ await connection . close ( )
574
+ connection . creationTimestamp = Date . now ( ) - 4000
575
+
576
+ const { validateOnRelease } = setup ( { maxConnectionLifetime : 3000 } )
577
+
578
+ expect ( validateOnRelease ( connection ) ) . toBe ( false )
579
+ } )
580
+ } )
581
+
582
+ function setup ( { createConnection, authenticationProvider, maxConnectionLifetime } = { } ) {
583
+ const newPool = jest . fn ( ( ...args ) => new Pool ( ...args ) )
584
+ const log = new Logger ( 'debug' , ( ) => undefined )
585
+ jest . spyOn ( log , 'debug' )
586
+ const createChannelConnectionHook = createConnection || jest . fn ( async ( address ) => new FakeConnection ( address ) )
587
+ const authenticationProviderHook = new AuthenticationProvider ( { } )
588
+ jest . spyOn ( authenticationProviderHook , 'authenticate' )
589
+ . mockImplementation ( authenticationProvider || jest . fn ( ( { connection } ) => Promise . resolve ( connection ) ) )
590
+ const provider = new DirectConnectionProvider ( {
591
+ newPool,
592
+ config : {
593
+ maxConnectionLifetime : maxConnectionLifetime || 1000
594
+ } ,
595
+ address : server01 ,
596
+ log
597
+ } )
598
+ provider . _createChannelConnection = createChannelConnectionHook
599
+ provider . _authenticationProvider = authenticationProviderHook
600
+ return {
601
+ provider,
602
+ ...newPool . mock . calls [ 0 ] [ 0 ] ,
603
+ createChannelConnectionHook,
604
+ authenticationProviderHook,
605
+ log
606
+ }
607
+ }
608
+ } )
609
+ } )
379
610
} )
380
611
381
612
function newDirectConnectionProvider ( address , pool ) {
@@ -412,6 +643,12 @@ class FakeConnection extends Connection {
412
643
this . _release = jest . fn ( ( ) => release ( address , this ) )
413
644
this . _server = server
414
645
this . _authToken = auth
646
+ this . _closed = false
647
+ this . _id = 1
648
+ }
649
+
650
+ get id ( ) {
651
+ return this . _id
415
652
}
416
653
417
654
get authToken ( ) {
@@ -431,6 +668,10 @@ class FakeConnection extends Connection {
431
668
}
432
669
433
670
async close ( ) {
671
+ this . _closed = true
672
+ }
434
673
674
+ isOpen ( ) {
675
+ return ! this . _closed
435
676
}
436
677
}
0 commit comments