Feat: code refactoring and jest tests

chore: More refactoring

chore: More tests and refactors

fix: Fixed age not getting calculated properly

chore: Implemented mocking in jest

chore: More mock tests

chore: checkStuckScreen tests
This commit is contained in:
Cesar 2020-11-23 16:08:12 +00:00 committed by Josh Harvey
parent 65165b943b
commit 04fd82454d
12 changed files with 1047 additions and 66 deletions

View file

@ -0,0 +1,28 @@
const email = require('../email')
const alertRec = {
devices: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: {
balanceAlerts: [],
deviceAlerts: [
{ code: 'PING', age: 602784301.446, machineName: 'Abc123' }
]
}
},
deviceNames: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123'
},
general: []
}
const printEmailMsg = `Errors were reported by your Lamassu Machines.
Errors for Abc123:
Machine down for ~6 days
`
test('Print Email Alers', () => {
expect(email.printEmailAlerts(alertRec, { active: true, errors: true })).toBe(
printEmailMsg
)
})

View file

@ -0,0 +1,234 @@
const notifier = require('..')
// mock plugins object with mock data to test functions
const plugins = {
sendMessage: rec => {
return rec
},
getNotificationConfig: () => ({
email_active: false,
sms_active: true,
email_errors: false,
sms_errors: true,
sms: { active: true, errors: true },
email: { active: false, errors: false }
}),
getMachineNames: () => [
{
deviceId:
'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
cashbox: 0,
cassette1: 444,
cassette2: 222,
version: '7.5.0-beta.0',
model: 'unknown',
pairedAt: '2020-11-13T16:20:31.624Z',
lastPing: '2020-11-16T13:11:03.169Z',
name: 'Abc123',
paired: true,
cashOut: true,
statuses: [{ label: 'Unresponsive', type: 'error' }]
}
],
checkBalances: () => []
}
const devices = [
{
deviceId:
'7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4',
lastPing: '2020-11-16T13:11:03.169Z',
name: 'Abc123'
},
{
deviceId:
'9871e58aa2643ff9445cbc299b50397430ada75157d6c29b4c93548fff0f48f7',
lastPing: '2020-11-16T16:21:35.948Z',
name: 'Machine 2'
},
{
deviceId:
'5ae0d02dedeb77b6521bd5eb7c9159bdc025873fa0bcb6f87aaddfbda0c50913',
lastPing: '2020-11-19T15:07:57.089Z',
name: 'Machine 3'
},
{
deviceId:
'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
lastPing: '2020-11-23T19:34:41.031Z',
name: 'New Machine 4 '
}
]
test('Exits checkNotifications with Promise.resolve() if SMS and Email are disabled', async () => {
expect.assertions(1)
await expect(
notifier.checkNotification({
getNotificationConfig: () => ({
sms: { active: false, errors: false },
email: { active: false, errors: false }
})
})
).resolves.toBe(undefined)
})
test('Exits checkNotifications with Promise.resolve() if SMS and Email are disabled even if errors or balance are defined to something', async () => {
expect.assertions(1)
await expect(
notifier.checkNotification({
getNotificationConfig: () => ({
sms: { active: false, errors: true, balance: true },
email: { active: false, errors: true, balance: true }
})
})
).resolves.toBe(undefined)
})
test("Check Pings should return code PING for devices that haven't been pinged recently", () => {
expect(
notifier.checkPings([
{
deviceId:
'7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4',
lastPing: '2020-11-16T13:11:03.169Z',
name: 'Abc123'
}
])
).toMatchObject({
'7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4': [
{ code: 'PING', machineName: 'Abc123' }
]
})
})
test('Checkpings returns empty array as the value for the id prop, if the lastPing is more recent than 60 seconds', () => {
expect(
notifier.checkPings([
{
deviceId:
'7a531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4',
lastPing: new Date(),
name: 'Abc123'
}
])
).toMatchObject({
'7a531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4': []
})
})
afterEach(() => {
// https://stackoverflow.com/questions/58151010/difference-between-resetallmocks-resetmodules-resetmoduleregistry-restoreallm
jest.restoreAllMocks()
})
const utils = require('../utils')
test('Check notification resolves to undefined if shouldNotAlert is called and is true', async () => {
const mockShouldNotAlert = jest.spyOn(utils, 'shouldNotAlert')
mockShouldNotAlert.mockReturnValue(true)
const result = await notifier.checkNotification(plugins)
expect(mockShouldNotAlert).toHaveBeenCalledTimes(1)
expect(result).toBe(undefined)
})
test('Sendmessage is called if shouldNotAlert is called and is false', async () => {
const mockShouldNotAlert = jest.spyOn(utils, 'shouldNotAlert')
const mockSendMessage = jest.spyOn(plugins, 'sendMessage')
mockShouldNotAlert.mockReturnValue(false)
const result = await notifier.checkNotification(plugins)
expect(mockShouldNotAlert).toHaveBeenCalledTimes(1)
expect(mockSendMessage).toHaveBeenCalledTimes(1)
expect(result).toBe(undefined)
})
test('If no alert fingerprint and inAlert is true, exits on call to sendNoAlerts', async () => {
// mock utils.buildAlertFingerprint to return null
// mock utils.getAlertFingerprint to be true which will make inAlert true
const buildFp = jest.spyOn(utils, 'buildAlertFingerprint')
const mockGetFp = jest.spyOn(utils, 'getAlertFingerprint')
const mockSendNoAlerts = jest.spyOn(utils, 'sendNoAlerts')
buildFp.mockReturnValue(null)
mockGetFp.mockReturnValue(true)
await notifier.checkNotification(plugins)
expect(mockGetFp).toHaveBeenCalledTimes(1)
expect(mockSendNoAlerts).toHaveBeenCalledTimes(1)
})
// vvv tests for checkstuckscreen...
test('checkStuckScreen returns [] when no events are found', () => {
expect(notifier.checkStuckScreen([], 'Abc123')).toEqual([])
})
test('checkStuckScreen returns [] if most recent event is idle', () => {
// device_time is what matters for the sorting of the events by recency
expect(notifier.checkStuckScreen([{
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":false}',
created: "2020-11-23T19:30:29.209Z",
device_time: "1999-11-23T19:30:29.177Z",
age: 157352628.123
}, {
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":true}',
created: "2020-11-23T19:30:29.209Z",
device_time: "2020-11-23T19:30:29.177Z",
age: 157352628.123
}])).toEqual([])
})
test('checkStuckScreen returns object array of length 1 with prop code: "STALE" if age > STALE_STATE', () => {
// there is an age 0 and an isIdle true in the first object but it will be below the second one in the sorting order and thus ignored
const result = notifier.checkStuckScreen([{
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":true}',
created: "2020-11-23T19:30:29.209Z",
device_time: "1999-11-23T19:30:29.177Z",
age: 0
}, {
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":false}',
created: "2020-11-23T19:30:29.209Z",
device_time: "2020-11-23T19:30:29.177Z",
age: 157352628.123
}])
expect(result[0]).toMatchObject({code: "STALE"})
})
test('checkStuckScreen returns empty array if age < STALE_STATE', () => {
const STALE_STATE = require("../codes").STALE_STATE
const result1 = notifier.checkStuckScreen([{
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":false}',
created: "2020-11-23T19:30:29.209Z",
device_time: "2020-11-23T19:30:29.177Z",
age: 0
}])
const result2 = notifier.checkStuckScreen([{
id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2',
device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05',
event_type: 'stateChange',
note: '{"state":"chooseCoin","isIdle":false}',
created: "2020-11-23T19:30:29.209Z",
device_time: "2020-11-23T19:30:29.177Z",
age: STALE_STATE
}])
expect(result1).toEqual([])
expect(result2).toEqual([])
})

View file

@ -0,0 +1,22 @@
const sms = require('../sms')
const alertRec = {
devices: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: {
balanceAlerts: [],
deviceAlerts: [
{ code: 'PING', age: 602784301.446, machineName: 'Abc123' }
]
}
},
deviceNames: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123'
},
general: []
}
test('Print SMS alerts', () => {
expect(sms.printSmsAlerts(alertRec, { active: true, errors: true })).toBe(
'[Lamassu] Errors reported: Machine Down (Abc123)'
)
})

View file

@ -0,0 +1,100 @@
const utils = require('../utils')
const plugins = {
sendMessage: rec => {
return rec
}
}
const alertRec = {
devices: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: {
balanceAlerts: [],
deviceAlerts: [
{ code: 'PING', age: 1605532263169, machineName: 'Abc123' }
]
}
},
deviceNames: {
f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123'
},
general: []
}
const notifications = {
sms: { active: true, errors: true },
email: { active: false, errors: false }
}
test('Build alert fingerprint returns null if no sms or email alerts', () => {
expect(
utils.buildAlertFingerprint(
{
devices: {},
deviceNames: {},
general: []
},
notifications
)
).toBe(null)
})
test('Build alert fingerprint returns null if sms and email are disabled', () => {
expect(
utils.buildAlertFingerprint(alertRec, {
sms: { active: false, errors: true },
email: { active: false, errors: false }
})
).toBe(null)
})
test('Build alert fingerprint returns hash if email or [sms] are enabled and there are alerts in alertrec', () => {
expect(
typeof utils.buildAlertFingerprint(alertRec, {
sms: { active: true, errors: true },
email: { active: false, errors: false }
})
).toBe('string')
})
test('Build alert fingerprint returns hash if [email] or sms are enabled and there are alerts in alertrec', () => {
expect(
typeof utils.buildAlertFingerprint(alertRec, {
sms: { active: false, errors: false },
email: { active: true, errors: true }
})
).toBe('string')
})
test('Send no alerts returns empty object with sms and email disabled', () => {
expect(utils.sendNoAlerts(plugins, false, false)).toEqual({})
})
test('Send no alerts returns object with sms prop with sms only enabled', () => {
expect(utils.sendNoAlerts(plugins, true, false)).toEqual({
sms: {
body: '[Lamassu] All clear'
}
})
})
test('Send no alerts returns object with sms and email prop with both enabled', () => {
expect(utils.sendNoAlerts(plugins, true, true)).toEqual({
email: {
body: 'No errors are reported for your machines.',
subject: '[Lamassu] All clear'
},
sms: {
body: '[Lamassu] All clear'
}
})
})
test('Send no alerts returns object with email prop if only email is enabled', () => {
expect(utils.sendNoAlerts(plugins, false, true)).toEqual({
email: {
body: 'No errors are reported for your machines.',
subject: '[Lamassu] All clear'
}
})
})