Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/testing token refresher #22

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api",
"version": "0.0.1",
"version": "0.0.3",
"description": "Integration med fastAPI.",
"main": "./index.js",
"scripts": {
Expand Down
80 changes: 80 additions & 0 deletions src/adapters/fastapiadapter/__tests__/databaseHelper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-disable @typescript-eslint/camelcase */
import { getAccessTokenFromDb, setAccessTokenInDb } from '../databaseHelper'
import { query } from '@app/adapters/postgres'
import { FastAPIToken } from '../types'

jest.mock('@app/adapters/postgres')

describe('#databaseHelper', () => {
beforeEach(() => {
jest.resetAllMocks()
})

describe('#getAccessTokenFromDb', () => {
test('gets the newest token and returns it value', async () => {
const tokens: FastAPIToken[] = [
{
id: 1,
created: new Date(),
token_value: 'token value',
},
]
;(query as jest.Mock).mockResolvedValue(tokens)

const result = await getAccessTokenFromDb()

expect(result).toEqual('token value')
expect(query.mock.calls[0][0]).toMatchInlineSnapshot(
`"SELECT created, token_value FROM fastapi_tokens ORDER BY created DESC LIMIT 1"`
)
})

test('returns null if there are no tokens in DB', async () => {
const tokens: FastAPIToken[] = []
;(query as jest.Mock).mockResolvedValue(tokens)

const result = await getAccessTokenFromDb()

expect(result).toEqual(null)
})

test('throws error if call to DB fails', async () => {
;(query as jest.Mock).mockImplementation(() => {
throw new Error('fail')
})

try {
await getAccessTokenFromDb()
} catch (error) {
expect(error.message).toEqual('fail')
}
})
})

describe('#setAccessTokenInDb', () => {
test('writes a token to db and returns the id', async () => {
;(query as jest.Mock).mockResolvedValue([1])

const result = await setAccessTokenInDb('token')

expect(result).toEqual(1)
expect(query.mock.calls[0][0]).toMatchInlineSnapshot(`
"INSERT INTO fastapi_tokens (token_value)
VALUES ('token')
RETURNING id"
`)
})

test('throws error if call to DB fails', async () => {
;(query as jest.Mock).mockImplementation(() => {
throw new Error('fail')
})

try {
await setAccessTokenInDb('token')
} catch (error) {
expect(error.message).toEqual('fail')
}
})
})
})
47 changes: 38 additions & 9 deletions src/adapters/fastapiadapter/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { client } from '../index'
import { fi2PartnerFixture } from '../__fixtures__/fi2partner.fixture'
// import tokenHelper from '../tokenHelper'
import axios from 'axios'
import xml2json from 'xml2json'
import config from '../../../config'
Expand All @@ -9,8 +8,23 @@ jest.mock('axios', () => ({
create: jest.fn(),
}))
jest.mock('xml2json')
jest.mock('../../../config')
// jest.mock('../tokenHelper')
jest.mock('../../../config', () => {
return {
fastAPI: {
baseUrl: 'test',
user: 'user',
password: 'pwd',
},
}
})
jest.mock('../tokenHelper', () => ({
tokenRefresher: (innerGet) => {
return (arg) => {
arg.token = 'a test token'
return innerGet(arg)
}
},
}))

describe('#fastapiadapter', () => {
let xmlClientMock
Expand All @@ -32,15 +46,11 @@ describe('#fastapiadapter', () => {
user: 'user',
password: 'pwd',
}

//tokenHelper.tokenRefresher = jest.fn(async () => async () => Promise.resolve({}))
})

describe.skip('#get', () => {
describe('#get', () => {
test('gets xml and returns json from xml2json', async () => {
const result = await client.get({ url: 'an url' })

expect(xmlClientMock.get).toHaveBeenCalledTimes(1)
expect(xml2json.toJson).toHaveBeenCalledWith(fi2PartnerFixture, {
arrayNotation: [
'fi2lease_actor',
Expand All @@ -55,10 +65,29 @@ describe('#fastapiadapter', () => {
'fi2_id',
'fi2cont_tel',
'fi2cont_email',
'fi2spsys_address',
],
})
expect(result).toEqual(JSON.parse(jsonResult))
})
})

test('calls the correct url', async () => {
await client.get({ url: 'an url' })

expect(xmlClientMock.get).toHaveBeenCalledTimes(1)
expect(xmlClientMock.get).toHaveBeenCalledWith('an url')
})

test('uses token from tokenRefresher in header', async () => {
await client.get({ url: 'an url' })

expect(axios.create).toHaveBeenCalledWith({
baseURL: 'test',
headers: {
Accept: '*/*',
'Access-Token': 'a test token',
},
responseType: 'text',
})
})
})
102 changes: 102 additions & 0 deletions src/adapters/fastapiadapter/__tests__/tokenHelper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import tokenHelper from '../tokenHelper'
import axios from 'axios'
import { getAccessTokenFromDb } from '../databaseHelper'

jest.mock('axios', () => ({
get: jest.fn(),
}))
jest.mock('../databaseHelper')
jest.mock('../../../config', () => {
return {
fastAPI: {
baseUrl: 'test',
user: 'user',
password: 'pwd',
},
}
})
const mockFn = jest.fn()

describe('#tokenHelper', () => {
beforeEach(() => {
jest.resetAllMocks()
;(getAccessTokenFromDb as jest.Mock).mockResolvedValue('a test token')
})

describe('#tokenRefresher', () => {
test('calls wrapped function and returns the result', async () => {
mockFn.mockResolvedValueOnce('that return value')

const wrappedFunction = tokenHelper.tokenRefresher(mockFn)
const result = await wrappedFunction({ url: 'cat' })

expect(mockFn).toHaveBeenCalledTimes(1)
expect(result).toEqual('that return value')
})

test('gets token from DB and sets it in call to wrapped function', async () => {
mockFn.mockResolvedValueOnce('that return value')

const wrappedFunction = tokenHelper.tokenRefresher(mockFn)
await wrappedFunction({ url: 'cat' })

expect(getAccessTokenFromDb).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenCalledWith({ url: 'cat', token: 'a test token' })
})

test('when token is missing in DB, it calls fastAPI to get a new one and passes it to wrapped function', async () => {
mockFn.mockResolvedValueOnce('that return value')
;(getAccessTokenFromDb as jest.Mock).mockResolvedValueOnce(null)
;(axios.get as jest.Mock).mockResolvedValueOnce({
headers: {
'access-token': 'a access token from fastAPI',
},
})

const wrappedFunction = tokenHelper.tokenRefresher(mockFn)
await wrappedFunction({ url: 'cat' })

expect(getAccessTokenFromDb).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenCalledWith({ url: 'cat', token: 'a access token from fastAPI' })
})

test('when token is invalid, it calls fastAPI to get a new one and passes it to wrapped function', async () => {
mockFn
.mockImplementationOnce(() => {
throw new Error('Request failed with status code 403')
})
.mockResolvedValueOnce('that return value')
;(getAccessTokenFromDb as jest.Mock).mockResolvedValueOnce('invalid token')
;(axios.get as jest.Mock).mockResolvedValueOnce({
headers: {
'access-token': 'a access token from fastAPI',
},
})

const wrappedFunction = tokenHelper.tokenRefresher(mockFn)
await wrappedFunction({ url: 'cat' })

expect(getAccessTokenFromDb).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledWith('login?user=user&password=pwd', {
baseURL: 'test',
})
expect(mockFn).toHaveBeenCalledTimes(2) //to verify re-try
expect(mockFn).toHaveBeenCalledWith({ url: 'cat', token: 'a access token from fastAPI' })
})

test('when wrapped function fails for other reasons, the error is thrown', async () => {
mockFn.mockImplementationOnce(() => {
throw new Error('Any other reason')
})

const wrappedFunction = tokenHelper.tokenRefresher(mockFn)
try {
await wrappedFunction({ url: 'cat' })
} catch (error) {
expect(error.message).toEqual('Any other reason')
}
})
})
})
7 changes: 6 additions & 1 deletion src/adapters/fastapiadapter/databaseHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { query } from '@app/adapters/postgres'
import { FastAPIToken } from './types'

export const getAccessTokenFromDb = async (): Promise<string | null> => {
const sql = `SELECT created, token_value FROM fastapi_tokens ORDER BY created DESC`
const sql = `SELECT created, token_value FROM fastapi_tokens ORDER BY created DESC LIMIT 1`
const tokens: FastAPIToken[] = await query<FastAPIToken>(sql)
return Array.isArray(tokens) && tokens.length > 0 ? tokens[0].token_value : null
}
Expand All @@ -14,3 +14,8 @@ export const setAccessTokenInDb = async (token: string): Promise<number | null>
const ids = await query<number>(sql)
return Array.isArray(ids) && ids.length > 0 ? ids[0] : null
}

export default {
getAccessTokenFromDb,
setAccessTokenInDb,
}
3 changes: 1 addition & 2 deletions src/adapters/fastapiadapter/tokenHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ export const tokenRefresher = <T extends (arg: FastAPIRequest) => any>(
return async (arg: FastAPIRequest): Promise<ReturnType<T>> => {
try {
const token = await getAccessTokenFromDb()

if (token === null) {
if (token === null || !token) {
throw new Error(NO_TOKENS_IN_DB_ERROR)
}

Expand Down
6 changes: 0 additions & 6 deletions src/services/leasecontractservice/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
} from '../__fixtures__/fastAPIAdapterResult.fixture'
import { client } from '@app/adapters/fastapiadapter'
import service from '../index'
import config from '../../../config'

jest.mock('@app/adapters/fastapiadapter')
jest.mock('../../../config')
Expand All @@ -16,11 +15,6 @@ describe('#leasecontractservice', () => {
console.error = jest.fn()

jest.resetAllMocks()

config.fastAPI = {
baseUrl: 'test',
accessToken: 'test',
}
})

describe('#getLeaseContracts', () => {
Expand Down