openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
753 lines (626 loc) • 27.4 kB
JavaScript
/* eslint-env mocha */
/* eslint no-unused-expressions:0 */
import request from 'supertest'
import { ChannelModelAPI } from '../../src/model/channels'
import { ClientModelAPI } from '../../src/model/clients'
import { MediatorModelAPI } from '../../src/model/mediators'
import { UserModelAPI } from '../../src/model/users'
import { ContactGroupModelAPI } from '../../src/model/contactGroups'
import * as constants from '../constants'
import { promisify } from 'util'
import * as server from '../../src/server'
import * as testUtils from '../utils'
import {ObjectId} from 'mongodb'
const sampleMetadata = {
Channels: [{
name: 'TestChannel1',
urlPattern: 'test/sample',
allow: ['PoC', 'Test1', 'Test2'],
routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }],
txViewAcl: 'group1',
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}],
Clients: [{
clientID: 'YUIAIIIICIIAIA',
clientDomain: 'him.jembi.org',
name: 'OpenMRS Ishmael instance',
roles: ['OpenMRS_PoC', 'PoC'],
passwordHash: '$2a$10$w8GyqInkl72LMIQNpMM/fenF6VsVukyya.c6fh/GRtrKq05C2.Zgy',
certFingerprint: '23:37:6A:5E:A9:13:A4:8C:66:C5:BB:9F:0E:0D:68:9B:99:80:10:FC'
}],
Mediators: [{
urn: 'urn:uuid:EEA84E13-1C92-467C-B0BD-7C480462D1ED',
version: '1.0.0',
name: 'Save Encounter Mediator',
description: 'A mediator for testing',
endpoints: [{ name: 'Save Encounter', host: 'localhost', port: '8005', type: 'http' }],
defaultChannelConfig: [{
name: 'Save Encounter 1',
urlPattern: '/encounters',
type: 'http',
allow: [],
routes: [{ name: 'Save Encounter 1', host: 'localhost', port: '8005', type: 'http' }]
}]
}],
Users: [{
firstname: 'Namey',
surname: 'mcTestName',
email: 'r..@jembi.org',
passwordAlgorithm: 'sha512',
passwordHash: '796a5a8e-4e44-4d9f-9e04-c27ec6374ffa',
passwordSalt: 'bf93caba-6eec-4c0c-a1a3-d968a7533fd7',
groups: ['admin', 'RHIE']
}],
ContactGroups: [{
group: 'Group 1',
users: [
{ user: 'User 1', method: 'sms', maxAlerts: 'no max' },
{ user: 'User 2', method: 'email', maxAlerts: '1 per hour' },
{ user: 'User 3', method: 'sms', maxAlerts: '1 per day' },
{ user: 'User 4', method: 'email', maxAlerts: 'no max' },
{ user: 'User 5', method: 'sms', maxAlerts: '1 per hour' },
{ user: 'User 6', method: 'email', maxAlerts: '1 per day' }
]
}]
}
let authDetails = {}
describe('API Integration Tests', () => {
const { SERVER_PORTS } = constants
describe('Metadata REST Api Testing', () => {
before(async () => {
await promisify(server.start)({ apiPort: SERVER_PORTS.apiPort })
await testUtils.setupTestUsers()
})
beforeEach(() => {
authDetails = testUtils.getAuthDetails()
})
after(async () => {
await testUtils.cleanupTestUsers()
await promisify(server.stop)()
})
// GET TESTS
describe('*getMetadata', () => {
describe('Channels', () => {
beforeEach(async () => {
await new ChannelModelAPI(sampleMetadata.Channels[0]).save()
})
afterEach(async () => {
await ChannelModelAPI.deleteMany({})
})
it('should fetch channels and return status 200', async () => {
const res = await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(200)
res.body[0].Channels.length.should.equal(1)
res.body[0].Channels[0].should.have.property('urlPattern', 'test/sample')
})
})
describe('Clients', () => {
beforeEach(async () => {
await new ClientModelAPI(sampleMetadata.Clients[0]).save()
})
afterEach(async () => {
await ClientModelAPI.deleteMany({})
})
it('should fetch clients and return status 200', async () => {
const res = await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(200)
res.body[0].Clients.length.should.equal(1)
res.body[0].Clients[0].should.have.property('name', 'OpenMRS Ishmael instance')
})
})
describe('Mediators', () => {
beforeEach(async () => {
await new MediatorModelAPI(sampleMetadata.Mediators[0]).save()
})
afterEach(async () => {
await MediatorModelAPI.deleteMany({})
})
it('should fetch mediators and return status 200', async () => {
const res = await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(200)
res.body[0].Mediators.length.should.equal(1)
res.body[0].Mediators[0].should.have.property('name', 'Save Encounter Mediator')
})
})
describe('Users', () => {
it('should fetch users and return status 200', async () => {
const res = await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(200)
res.body[0].Users.length.should.equal(3) // Due to 3 auth test users
})
})
describe('ContactGroups', () => {
beforeEach(async () => {
await (new ContactGroupModelAPI(sampleMetadata.ContactGroups[0])).save()
})
afterEach(async () => {
await ContactGroupModelAPI.deleteMany({})
})
it('should fetch contact groups and return status 200', async () => {
const res = await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(200)
res.body[0].ContactGroups.length.should.equal(1)
res.body[0].ContactGroups[0].should.have.property('group', 'Group 1')
})
})
describe('Other Get Metadata', () => {
it('should not allow a non admin user to get metadata', async () => {
await request(constants.BASE_URL)
.get('/metadata')
.set('auth-username', testUtils.nonRootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.expect(403)
})
it('should return 404 if not found', async () => {
await request(constants.BASE_URL)
.get('/metadata/bleh')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(404)
})
})
})
// IMPORT TESTS
describe('*importMetadata', () => {
describe('Channels', () => {
let testMetadata = {}
beforeEach(async () => {
testMetadata = await { Channels: JSON.parse(JSON.stringify(sampleMetadata.Channels)) }
})
afterEach(async () => {
await ChannelModelAPI.deleteMany({})
})
it('should insert a channel and return 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Inserted')
const channel = await ChannelModelAPI.findOne({ name: 'TestChannel1' })
channel.should.have.property('urlPattern', 'test/sample')
channel.allow.should.have.length(3)
})
it('should update a channel and return 201', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
testMetadata.Channels[0].urlPattern = 'sample/test'
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Updated')
const channel = await ChannelModelAPI.findOne({ name: 'TestChannel1' })
channel.should.have.property('urlPattern', 'sample/test')
channel.allow.should.have.length(3)
})
it('should fail to insert a Channel and return 201', async () => {
testMetadata.Channels = [{ fakeChannel: 'fakeChannel' }]
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Error')
})
})
describe('Clients', () => {
let testMetadata = {}
beforeEach(async () => {
testMetadata = await { Clients: JSON.parse(JSON.stringify(sampleMetadata.Clients)) }
})
afterEach(async () => {
await ClientModelAPI.deleteMany({})
})
it('should insert a client and return 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Inserted')
const client = await ClientModelAPI.findOne({ clientID: 'YUIAIIIICIIAIA' })
client.should.have.property('name', 'OpenMRS Ishmael instance')
})
it('should update a client and return 201', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
testMetadata.Clients[0].name = 'Test Update'
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Updated')
const client = await ClientModelAPI.findOne({ clientID: 'YUIAIIIICIIAIA' })
client.should.have.property('name', 'Test Update')
})
it('should fail to insert a Client and return 201', async () => {
testMetadata.Clients = [{ fakeClient: 'fakeClient' }]
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Error')
})
})
describe('Mediators', () => {
let testMetadata = {}
beforeEach(async () => {
testMetadata =
await { Mediators: JSON.parse(JSON.stringify(sampleMetadata.Mediators)) }
})
afterEach(async () => {
await MediatorModelAPI.deleteMany({})
})
it('should insert a mediator and return 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Inserted')
const mediator = await MediatorModelAPI.findOne({ urn: 'urn:uuid:EEA84E13-1C92-467C-B0BD-7C480462D1ED' })
mediator.should.have.property('name', 'Save Encounter Mediator')
})
it('should update a mediator and return 201', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
testMetadata.Mediators[0].name = 'Updated Encounter Mediator'
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Updated')
const mediator = await MediatorModelAPI.findOne({ urn: 'urn:uuid:EEA84E13-1C92-467C-B0BD-7C480462D1ED' })
mediator.should.have.property('name', 'Updated Encounter Mediator')
})
it('should fail to insert a mediator and return 201', async () => {
testMetadata.Mediators = [{ fakeMediator: 'fakeMediator' }]
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Error')
})
})
describe('Users', () => {
let testMetadata = {}
beforeEach(async () => {
testMetadata = { Users: testUtils.clone(sampleMetadata.Users) }
})
afterEach(async () => {
await UserModelAPI.deleteMany({ email: { $in: testMetadata.Users.map(u => u.email) } })
})
it('should insert a user and return 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Inserted')
const user = await UserModelAPI.findOne({ email: 'r..@jembi.org' })
user.should.have.property('firstname', 'Namey')
})
it('should update a user and return 201', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
testMetadata.Users[0].firstname = 'updatedNamey'
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Updated')
const user = await UserModelAPI.findOne({ email: 'r..@jembi.org' })
user.should.have.property('firstname', 'updatedNamey')
})
it('should fail to insert a user and return 201', async () => {
testMetadata.Users = [{ fakeUser: 'fakeUser' }]
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Error')
})
})
describe('ContactGroups', () => {
let testMetadata = {}
beforeEach(async () => {
testMetadata =
await { ContactGroups: JSON.parse(JSON.stringify(sampleMetadata.ContactGroups)) }
})
afterEach(async () => {
await ContactGroupModelAPI.deleteMany({})
})
it('should insert a contactGroup and return 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Inserted')
const cg = await ContactGroupModelAPI.findOne({ group: 'Group 1' })
cg.users.should.have.length(6)
})
it('should update a contactGroup and return 201', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
await testMetadata.ContactGroups[0].users.push({ user: 'User 6', method: 'email', maxAlerts: '1 per day' })
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Updated')
const cg = await ContactGroupModelAPI.findOne({ group: 'Group 1' })
cg.users.should.have.length(7)
})
it('should fail to insert a ContactGroup and return 201', async () => {
testMetadata.ContactGroups = [{ fakeContactGroup: 'fakeContactGroup' }]
const res = await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
res.body[0].should.have.property('status', 'Error')
})
})
describe('Full Metadata Import', () => {
after(async () => {
await Promise.all([
ChannelModelAPI.deleteMany({}),
ClientModelAPI.deleteMany({}),
MediatorModelAPI.deleteMany({}),
ContactGroupModelAPI.deleteMany({})
// User?
])
authDetails = await testUtils.getAuthDetails()
})
it('should ignore invalid metadata, insert valid metadata and return 201', async () => {
let testMetadata = await JSON.parse(JSON.stringify(sampleMetadata))
testMetadata.Channels = [{ InvalidChannel: 'InvalidChannel' }]
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
const channel = await ChannelModelAPI.findOne({ name: 'TestChannel1' })
const noChannel = channel ? 'false' : 'true'
noChannel.should.equal('true')
const client = await ClientModelAPI.findOne({ clientID: 'YUIAIIIICIIAIA' })
client.should.have.property('name', 'OpenMRS Ishmael instance')
const mediator = await MediatorModelAPI.findOne({ urn: 'urn:uuid:EEA84E13-1C92-467C-B0BD-7C480462D1ED' })
mediator.should.have.property('name', 'Save Encounter Mediator')
const user = await UserModelAPI.findOne({ email: 'r..@jembi.org' })
user.should.have.property('firstname', 'Namey')
const cg = await ContactGroupModelAPI.findOne({ group: 'Group 1' })
cg.users.should.have.length(6)
})
})
describe('Bad metadata import requests', () => {
it('should not allow a non admin user to insert metadata', async () => {
await request(constants.BASE_URL)
.post('/metadata')
.set('auth-username', testUtils.nonRootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(403)
})
it('should return 404 if not found', async () => {
await request(constants.BASE_URL)
.post('/metadata/bleh')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(404)
})
})
})
// POST TO VALIDATE METADATA TESTS
describe('*validateMetadata', () => {
beforeEach(async () => {
await testUtils.cleanupAllTestUsers()
await testUtils.setupTestUsers()
})
it('should validate metadata and return status 201', async () => {
const res = await request(constants.BASE_URL)
.post('/metadata/validate')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(201)
const statusCheckObj = { Valid: 0, Conflict: 0, Error: 0 }
for (const doc of Array.from(res.body)) {
statusCheckObj[doc.status] += 1
}
statusCheckObj.Valid.should.equal(5)
statusCheckObj.Conflict.should.equal(0)
statusCheckObj.Error.should.equal(0)
})
it('should validate partially valid metadata and return status 201', async () => {
let testMetadata = await JSON.parse(JSON.stringify(sampleMetadata))
testMetadata.Channels = [{ 'Invalid Channel': 'Invalid Channel' }]
const res = await request(constants.BASE_URL)
.post('/metadata/validate')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
const statusCheckObj = { Valid: 0, Conflict: 0, Error: 0 }
for (const doc of Array.from(res.body)) {
statusCheckObj[doc.status] += 1
}
statusCheckObj.Valid.should.equal(4)
statusCheckObj.Conflict.should.equal(0)
statusCheckObj.Error.should.equal(1)
})
it('should validate metadata with conflicts and return status 201', async () => {
let testMetadata = {}
testMetadata = await JSON.parse(JSON.stringify(sampleMetadata))
await new UserModelAPI(sampleMetadata.Users[0]).save()
await new ChannelModelAPI(sampleMetadata.Channels[0])
const res = await request(constants.BASE_URL)
.post('/metadata/validate')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(testMetadata)
.expect(201)
const statusCheckObj = { Valid: 0, Conflict: 0, Error: 0 }
for (const doc of Array.from(res.body)) {
statusCheckObj[doc.status] += 1
}
statusCheckObj.Valid.should.equal(4)
statusCheckObj.Conflict.should.equal(1)
statusCheckObj.Error.should.equal(0)
ChannelModelAPI.deleteMany({})
})
it('should not allow a non admin user to validate metadata', async () => {
await request(constants.BASE_URL)
.post('/metadata/validate')
.set('auth-username', testUtils.nonRootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(403)
})
it('should return 404 if not found', async () => {
await request(constants.BASE_URL)
.post('/metadata/validate/bleh')
.set('auth-username', testUtils.rootUser.email)
.set('auth-ts', authDetails.authTS)
.set('auth-salt', authDetails.authSalt)
.set('auth-token', authDetails.authToken)
.send(sampleMetadata)
.expect(404)
})
})
})
})