Skip to content

Commit 515134d

Browse files
committed
Add tests to the DirectConnectionProvider pool configuration
1 parent 459909b commit 515134d

File tree

2 files changed

+243
-2
lines changed

2 files changed

+243
-2
lines changed

packages/bolt-connection/src/connection-provider/connection-provider-direct.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const {
3333
const { SERVICE_UNAVAILABLE } = error
3434

3535
export default class DirectConnectionProvider extends PooledConnectionProvider {
36-
constructor ({ id, config, log, address, userAgent, authTokenProvider }) {
37-
super({ id, config, log, userAgent, authTokenProvider })
36+
constructor ({ id, config, log, address, userAgent, authTokenProvider, newPool }) {
37+
super({ id, config, log, userAgent, authTokenProvider, newPool })
3838

3939
this._address = address
4040
}

packages/bolt-connection/test/connection-provider/connection-provider-direct.test.js

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import DirectConnectionProvider from '../../src/connection-provider/connection-p
2121
import { Pool } from '../../src/pool'
2222
import { Connection, DelegateConnection } from '../../src/connection'
2323
import { internal, newError, ServerInfo } from 'neo4j-driver-core'
24+
import AuthenticationProvider from '../../src/connection-provider/authentication-provider'
2425

2526
const {
2627
serverAddress: { ServerAddress },
@@ -376,6 +377,236 @@ describe('.verifyConnectivityAndGetServerInfo()', () => {
376377
}
377378
})
378379
})
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+
})
379610
})
380611

381612
function newDirectConnectionProvider (address, pool) {
@@ -412,6 +643,12 @@ class FakeConnection extends Connection {
412643
this._release = jest.fn(() => release(address, this))
413644
this._server = server
414645
this._authToken = auth
646+
this._closed = false
647+
this._id = 1
648+
}
649+
650+
get id () {
651+
return this._id
415652
}
416653

417654
get authToken () {
@@ -431,6 +668,10 @@ class FakeConnection extends Connection {
431668
}
432669

433670
async close () {
671+
this._closed = true
672+
}
434673

674+
isOpen () {
675+
return !this._closed
435676
}
436677
}

0 commit comments

Comments
 (0)