Skip to content

Commit 471ebd1

Browse files
committed
Tests for authTokenManagers.basic()
1 parent 0c9b9c3 commit 471ebd1

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
import { auth } from '../src'
20+
import AuthTokenManager, { SecurityErrorCode, authTokenManagers } from '../src/auth-token-manager'
21+
import { AuthToken } from '../src/types'
22+
23+
describe('authTokenManagers', () => {
24+
const SECURITY_ERROR_CODES = [
25+
'Neo.ClientError.Security.TokenExpired',
26+
'Neo.ClientError.Security.MadeUp',
27+
'Neo.ClientError.Security.AuthorizationExpired',
28+
'Neo.ClientError.Security.Unauthorized'
29+
]
30+
31+
describe('.basic()', () => {
32+
const BASIC_HANDLED_ERROR_CODES = Object.freeze(['Neo.ClientError.Security.Unauthorized'])
33+
const BASIC_NOT_HANDLED_ERROR_CODES = Object.freeze(SECURITY_ERROR_CODES.filter(code => !BASIC_HANDLED_ERROR_CODES.includes(code)))
34+
35+
it.each([
36+
undefined,
37+
null,
38+
{},
39+
{ tokenProvider: null },
40+
{ tokenProvider: undefined },
41+
{ tokenProvider: false },
42+
{ tokenProvider: auth.basic('user', 'password') }
43+
])('should throw when instantiate with wrong parameters (params=%o)', (param) => {
44+
// @ts-expect-error
45+
expect(() => authTokenManagers.basic(param)).toThrowError(TypeError)
46+
})
47+
48+
it('should create an AuthTokenManager instance', () => {
49+
const basic: AuthTokenManager = authTokenManagers.basic({ tokenProvider: async () => auth.basic('user', 'password') })
50+
51+
expect(basic).toBeDefined()
52+
expect(basic.getToken).toBeInstanceOf(Function)
53+
expect(basic.handleSecurityException).toBeInstanceOf(Function)
54+
})
55+
56+
describe('.handleSecurityException()', () => {
57+
let basic: AuthTokenManager
58+
let tokenProvider: jest.Mock<Promise<AuthToken>>
59+
60+
beforeEach(async () => {
61+
tokenProvider = jest.fn(async () => auth.basic('user', 'password'))
62+
basic = authTokenManagers.basic({ tokenProvider })
63+
// init auth token
64+
await basic.getToken()
65+
tokenProvider.mockReset()
66+
})
67+
68+
describe.each(BASIC_HANDLED_ERROR_CODES)('when error code equals to "%s"', (code: SecurityErrorCode) => {
69+
describe('and same auth token', () => {
70+
const authToken = auth.basic('user', 'password')
71+
72+
it('should call tokenProvider and return true', () => {
73+
const handled = basic.handleSecurityException(authToken, code)
74+
75+
expect(handled).toBe(true)
76+
expect(tokenProvider).toHaveBeenCalled()
77+
})
78+
79+
it('should call tokenProvider only if there is not an ongoing call', async () => {
80+
const promise = { resolve: (_: AuthToken) => { } }
81+
tokenProvider.mockReturnValue(new Promise<AuthToken>((resolve) => { promise.resolve = resolve }))
82+
83+
expect(basic.handleSecurityException(authToken, code)).toBe(true)
84+
expect(tokenProvider).toHaveBeenCalled()
85+
86+
await triggerEventLoop()
87+
88+
expect(basic.handleSecurityException(authToken, code)).toBe(true)
89+
expect(tokenProvider).toHaveBeenCalledTimes(1)
90+
91+
promise.resolve(authToken)
92+
await triggerEventLoop()
93+
94+
expect(basic.handleSecurityException(authToken, code)).toBe(true)
95+
expect(tokenProvider).toHaveBeenCalledTimes(2)
96+
97+
promise.resolve(authToken)
98+
})
99+
})
100+
101+
describe('and different auth token', () => {
102+
const authToken = auth.basic('other_user', 'other_password')
103+
104+
it('should return true and not call the provider', () => {
105+
const handled = basic.handleSecurityException(authToken, code)
106+
107+
expect(handled).toBe(true)
108+
expect(tokenProvider).not.toHaveBeenCalled()
109+
})
110+
})
111+
})
112+
113+
it.each(BASIC_NOT_HANDLED_ERROR_CODES)('should not handle "%s"', (code: SecurityErrorCode) => {
114+
const handled = basic.handleSecurityException(auth.basic('user', 'password'), code)
115+
116+
expect(handled).toBe(false)
117+
expect(tokenProvider).not.toHaveBeenCalled()
118+
})
119+
})
120+
121+
describe('.getToken()', () => {
122+
let basic: AuthTokenManager
123+
let tokenProvider: jest.Mock<Promise<AuthToken>>
124+
let authToken: AuthToken
125+
126+
beforeEach(async () => {
127+
authToken = auth.basic('user', 'password')
128+
tokenProvider = jest.fn(async () => authToken)
129+
basic = authTokenManagers.basic({ tokenProvider })
130+
})
131+
132+
it('should call tokenProvider once and return the provided token many times', async () => {
133+
await expect(basic.getToken()).resolves.toBe(authToken)
134+
135+
expect(tokenProvider).toHaveBeenCalledTimes(1)
136+
137+
await expect(basic.getToken()).resolves.toBe(authToken)
138+
await expect(basic.getToken()).resolves.toBe(authToken)
139+
140+
expect(tokenProvider).toHaveBeenCalledTimes(1)
141+
})
142+
143+
it.each(BASIC_HANDLED_ERROR_CODES)('should reflect the authToken refreshed by handleSecurityException(authToken, "%s")', async (code: SecurityErrorCode) => {
144+
const newAuthToken = auth.basic('other_user', 'other_password')
145+
await expect(basic.getToken()).resolves.toBe(authToken)
146+
147+
expect(tokenProvider).toHaveBeenCalledTimes(1)
148+
149+
tokenProvider.mockReturnValueOnce(Promise.resolve(newAuthToken))
150+
151+
basic.handleSecurityException(authToken, code)
152+
expect(tokenProvider).toHaveBeenCalledTimes(2)
153+
154+
await expect(basic.getToken()).resolves.toBe(newAuthToken)
155+
await expect(basic.getToken()).resolves.toBe(newAuthToken)
156+
157+
expect(tokenProvider).toHaveBeenCalledTimes(2)
158+
})
159+
})
160+
})
161+
})
162+
163+
async function triggerEventLoop (): Promise<unknown> {
164+
return await new Promise(resolve => setTimeout(resolve, 0))
165+
}

0 commit comments

Comments
 (0)