Skip to content

Commit

Permalink
Merge pull request #52 from foxford/add-jest
Browse files Browse the repository at this point in the history
Add jest tests
  • Loading branch information
komarov-v authored Dec 29, 2021
2 parents 57b417b + 4c89042 commit b1ac8eb
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .babelrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"env":{
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
},
"es": {
"presets": [
["@babel/preset-env", {
Expand Down
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"extends": "standard"
"extends": "standard",
"env": {
"jest": true
}
}
101 changes: 101 additions & 0 deletions __tests__/basic-client.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { BasicClient } from '../src/basic-client'

const baseUrl = 'https://test.url'
const httpClient = {
get: jest.fn(),
put: jest.fn(),
post: jest.fn(),
patch: jest.fn(),
delete: jest.fn()
}
const tokenProvider = {
getToken: jest.fn().mockImplementation(() => 'token')
}

const headersWithoutLabels = { authorization: 'Bearer token', 'content-type': 'application/json' }

const Client = new BasicClient(baseUrl, httpClient, tokenProvider)

const labels = {
app_audience: 'audience',
app_label: 'label',
app_version: 'version',
scope: 'scope'
}

const ulmsLabels = {
'ulms-app-audience': 'audience',
'ulms-app-label': 'label',
'ulms-app-version': 'version',
'ulms-scope': 'scope'
}

const urlGeneratorCases = [
['objectParams', { a: 0, b: 1, c: 2 }, '?a=0&b=1&c=2'],
['advancedObjectParams', { a: 'a', b: [1, 2, 3], c: { cc: 3, dd: 4 } }, '?a=a&b[]=1&b[]=2&b[]=3&c[cc]=3&c[dd]=4'],
['objectParamsUndefined', { a: undefined }, ''],
['arrayParams', ['a', 'b', 'c'], '?0=a&1=b&2=c'],
['advancedArrayParams', { alphabet: ['a', 'b', 'c'] }, '?alphabet[]=a&alphabet[]=b&alphabet[]=c'],
['withoutParams', undefined, '']
]

const methodsWithoutDataCases = [
['get'],
['delete']
]

const methodsWithDataCases = [
['post', 'data'],
['put', 'data'],
['patch', 'data']
]
describe('Basic client suite', () => {
it.each(urlGeneratorCases)('URL generator should work with: endpoint — %s and %j as params', (endpoint, params, expectedParams) => {
const resultUrl = Client._url(`/${endpoint}`, params)
expect(resultUrl).toBe(`${baseUrl}/${endpoint}${expectedParams}`)
})

it('Headers should compare with labels', () => {
const resultHeaders = BasicClient._headers('token', { label: 'a' })
expect(resultHeaders.authorization).toBe('Bearer token')
expect(resultHeaders['content-type']).toBe('application/json')
expect(resultHeaders.label).toBe('a')
})

it('Headers should compare without labels', () => {
const resultHeaders = BasicClient._headers('token')
expect(resultHeaders).toEqual(headersWithoutLabels)
})

it('Labes should set and destroy', () => {
Client.setLabels(labels)
expect(Client._labels).toEqual(ulmsLabels)

Client.clearLabels()
expect(Client._labels).toEqual({})
})

it.each(methodsWithoutDataCases)('%s method should work (without labels)', async (method) => {
await Client['_' + method](baseUrl)
expect(httpClient[method]).toBeCalledWith(baseUrl, { headers: headersWithoutLabels })
})

it.each(methodsWithoutDataCases)('%s method should work (with labels)', async (method) => {
Client.setLabels(labels)
await Client['_' + method](baseUrl)
Client.clearLabels()
expect(httpClient[method]).toBeCalledWith(baseUrl, { headers: { ...headersWithoutLabels, ...ulmsLabels } })
})

it.each(methodsWithDataCases)('%s method should work (without labels) with payload: %s', async (method, data) => {
await Client['_' + method](baseUrl, data)
expect(httpClient[method]).toBeCalledWith(baseUrl, data, { headers: headersWithoutLabels })
})

it.each(methodsWithDataCases)('%s method should work (with labels) with payload: %s', async (method, data) => {
Client.setLabels(labels)
await Client['_' + method](baseUrl, data)
Client.clearLabels()
expect(httpClient[method]).toBeCalledWith(baseUrl, data, { headers: { ...headersWithoutLabels, ...ulmsLabels } })
})
})
35 changes: 35 additions & 0 deletions __tests__/codec.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Codec } from '../src/codec'

const encoder = (data) => JSON.stringify(data)
const decoder = (data) => {
let payload

try {
payload = JSON.parse(data.toString())
} catch (error) {
payload = {}
}

return payload
}

const codec = new Codec(encoder, decoder)
const payload = {
data: {
a: 'a',
b: 'b',
c: 'c'
},
another: [
'a', 'b', 'c'
]
}

describe('Codec works correctly', () => {
it('Encode works', () => {
expect(codec.encode(payload)).toEqual(encoder(payload))
})
it('Decode works', () => {
expect(codec.decode(encoder(payload))).toEqual(payload)
})
})
31 changes: 31 additions & 0 deletions __tests__/error.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// import { describe, it, expect} from 'jest'

import { MQTTClientError, MQTTRPCServiceError, TimeoutError } from '../src/error'

const clientError = new MQTTClientError('Message')
const serviceError = new MQTTRPCServiceError('Message')
const timeoutError = new TimeoutError('Message')

describe('Custom errors have right titles', () => {
it('MQTTClientError name is valid', () => {
expect(clientError.name).toEqual('MQTTClientError')
})
it('MQTTClientError message is valid (default)', () => {
expect(clientError.message).toEqual('Message')
})
it('MQTTClientError message is valid (from error)', () => {
expect(MQTTClientError.fromError(new Error('Message')).message).toEqual('Message')
})
it('MQTTRPCServiceError name is valid', () => {
expect(serviceError.name).toEqual('MQTTRPCServiceError')
})
it('MQTTRPCServiceError message is valid', () => {
expect(serviceError.message).toEqual('Message')
})
it('TimeoutError name is valid', () => {
expect(timeoutError.name).toEqual('TimeoutError')
})
it('TimeoutError message is valid', () => {
expect(timeoutError.message).toEqual('Message')
})
})
101 changes: 101 additions & 0 deletions __tests__/mqtt-rpc.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { MQTTRPCService } from '../src/mqtt-rpc'

const mqttClient = {
connected: true,
on: jest.fn(),
off: jest.fn(),
attachRoute: jest.fn(),
detachRoute: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
publish: jest.fn()
}

const handler = jest.fn()

const topicIn = 'topicIn'
const topicOut = 'topicOut'
const codec = {
encode: jest.fn(),
decode: jest.fn()
}
const methods = {
method: jest.fn()
}

const method = 'aaa'
const params = 'bbb'

const options = {
properties: {
correlationData: expect.any(String),
responseTopic: topicIn,
userProperties: {
local_timestamp: expect.any(String),
method: method,
type: 'request'
}
},
qos: 1
}

const labels = {
app_audience: 'app_audience',
app_label: 'app_label',
app_version: 'app_version',
scope: 'scope'
}

const optionsWithLabel = {
...options,
properties: {
...options.properties,
userProperties: {
...options.properties.userProperties,
...labels
}
}
}

const serviceMQTTRPC = new MQTTRPCService(mqttClient, topicIn, topicOut, codec, methods)

describe('MQTT-RPC Service is work', () => {
it('addSubscription works', () => {
serviceMQTTRPC._addSubscription()
expect(mqttClient.on).toBeCalledWith('close', expect.any(Function))
expect(mqttClient.on).toBeCalledWith('connect', expect.any(Function))
expect(mqttClient.on).toBeCalledTimes(2)
expect(mqttClient.attachRoute).toBeCalledWith(topicIn, expect.any(Function))
expect(mqttClient.attachRoute).toBeCalledTimes(1)
})
it('removeSubscription works', () => {
serviceMQTTRPC._removeSubscription()
expect(mqttClient.off).toBeCalledWith('close', expect.any(Function))
expect(mqttClient.off).toBeCalledWith('connect', expect.any(Function))
expect(mqttClient.off).toBeCalledTimes(2)
expect(mqttClient.detachRoute).toBeCalledWith(topicIn)
expect(mqttClient.detachRoute).toBeCalledTimes(1)
})
it('subscribeIn', () => {
serviceMQTTRPC._subscribeIn()
expect(mqttClient.subscribe).toBeCalledWith(topicIn, null, expect.any(Function))
})
it.skip('sent() is work', () => {
serviceMQTTRPC.register(method, handler)
serviceMQTTRPC.send(method, params)
expect(mqttClient.publish).toBeCalledWith(topicOut, undefined, expect.objectContaining(options), expect.any(Function))
expect(codec.encode).toBeCalledWith(params)
})
it.skip('Label is set', () => {
serviceMQTTRPC.setLabels(labels)
serviceMQTTRPC.send(method, params)
expect(mqttClient.publish).toBeCalledWith(topicOut, undefined, expect.objectContaining(optionsWithLabel), expect.any(Function))
expect(codec.encode).toBeCalledWith(params)
})
it('Register is work', () => {
serviceMQTTRPC.register(method, handler)
serviceMQTTRPC.send(method, params)
serviceMQTTRPC._processIncomingRequest(options.properties, params)
expect(handler).toBeCalledWith(params)
})
})
78 changes: 78 additions & 0 deletions __tests__/telemetry.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Telemetry } from '../src'

const mqttClient = {
connected: true,
publish: jest.fn()
}
const mqttClientDisconected = {
connected: false,
publish: jest.fn()
}

const agentId = 123
const appName = 'Test'
const topicOut = `agents/${agentId}/api/v1/out/${appName}`
const telemetry = new Telemetry(mqttClient, agentId, appName)
const disconnectedTelemetry = new Telemetry(mqttClientDisconected, agentId, appName)
const options = {
properties:
{
userProperties: {
label: 'metric.create',
local_timestamp: expect.any(String),
type: 'event'
}
},
qos: 1
}

const optionsWithLabel = {
properties: {
userProperties: {
app_audience: 'app_audience',
app_label: 'app_label',
app_version: 'app_version',
label: 'metric.create',
local_timestamp: expect.any(String),
scope: 'scope',
type: 'event'
}
},
qos: 1
}

const labels = {
app_audience: 'app_audience',
app_label: 'app_label',
app_version: 'app_version',
scope: 'scope'
}

describe('Telemetry works', () => {
it('It works', () => {
telemetry.send('aaa')
expect(mqttClient.publish).toBeCalledWith(topicOut, '"aaa"', expect.objectContaining(options))
})
it('Label is setting', () => {
telemetry.setLabels(labels)
telemetry.send('bbb')
expect(mqttClient.publish).toBeCalledWith(topicOut, '"bbb"', expect.objectContaining(optionsWithLabel))
})
it('Label is deleted', () => {
telemetry.clearLabels()
telemetry.send('ccc')
expect(mqttClient.publish).toBeCalledWith(topicOut, '"ccc"', expect.objectContaining(options))
})
it('It will destroy', () => {
telemetry.setLabels(labels)
telemetry.send('ddd')
expect(mqttClient.publish).toBeCalledWith(topicOut, '"ddd"', expect.objectContaining(optionsWithLabel))
telemetry.destroy()
telemetry.send('eee')
expect(mqttClient.publish).toBeCalledWith(topicOut, '"eee"', expect.objectContaining(options))
})
it('Handle MQTT disconected', () => {
disconnectedTelemetry.send('fff')
expect(mqttClientDisconected.publish).toBeCalledTimes(0)
})
})
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
clearMocks: true,
coverageDirectory: 'coverage',
testEnvironment: 'node'
}
Loading

0 comments on commit b1ac8eb

Please sign in to comment.