openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
338 lines (301 loc) • 8.81 kB
JavaScript
/* eslint-env mocha */
/* eslint no-unused-expressions:0 */
import should from 'should' // eslint-disable-line no-unused-vars
import fs from 'fs'
import sinon from 'sinon'
import { ChannelModel, CertificateModel, TransactionModel } from '../../src/model'
import * as testUtils from '../utils'
import { config } from '../../src/config'
import * as constants from '../constants'
import { promisify } from 'util'
import nconf from 'nconf'
import { ObjectId } from 'mongodb'
const { SERVER_PORTS } = constants
nconf.set('tcpAdapter', {
httpReceiver: {
httpPort: SERVER_PORTS.tcpHttpReceiverPort
}
})
const server = require('../../src/server')
const CHANNEL_PORT_START = constants.PORT_START + 60
const SERVER_PORT_START = constants.PORT_START + 70
const tcpToTcpChannelDoc = {
name: 'TCPIntegrationChannel1',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START,
tcpHost: 'localhost',
routes: [{
name: 'tcp route',
host: 'localhost',
port: SERVER_PORT_START,
type: 'tcp',
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const tlsToTcpChannelDoc = {
name: 'TCPIntegrationChannel2',
urlPattern: '/',
allow: ['tls'],
type: 'tls',
tcpPort: CHANNEL_PORT_START + 1,
tcpHost: 'localhost',
routes: [{
name: 'tcp route',
host: 'localhost',
port: SERVER_PORT_START + 1,
type: 'tcp',
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const tcpToHttpChannelDoc = {
name: 'TCPIntegrationChannel3',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START + 2,
tcpHost: 'localhost',
routes: [{
name: 'http route',
host: 'localhost',
port: SERVER_PORT_START + 2,
type: 'http',
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const tcpToTlsChannelDoc = {
name: 'TCPIntegrationChannel4',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START + 3,
tcpHost: 'localhost',
routes: [{
name: 'tls route',
host: 'localhost',
port: SERVER_PORT_START + 3,
type: 'tcp',
secured: true,
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const tcpToTlsNoCertChannelDoc = {
name: 'TCPIntegrationChannel5',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START + 4,
tcpHost: 'localhost',
routes: [{
name: 'tls route',
host: 'localhost',
port: SERVER_PORT_START + 4,
type: 'tcp',
secured: true,
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const tcpToMllpChannelDoc = {
name: 'MLLPIntegrationChannel1',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START + 5,
tcpHost: 'localhost',
routes: [{
name: 'mllp route',
host: 'localhost',
port: SERVER_PORT_START + 5,
type: 'mllp',
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
// We don't create a timeout channel for every possible combination as they all use the same code
const tcpTimeoutChannel = {
name: 'tcpTimeoutChannel',
urlPattern: '/',
allow: ['tcp'],
type: 'tcp',
tcpPort: CHANNEL_PORT_START + 6,
timeout: 20,
tcpHost: 'localhost',
routes: [{
name: 'tcp route',
host: 'localhost',
port: SERVER_PORT_START + 6,
type: 'mllp', // DONT CHANGE TO TCP, it's currently bugged on waiting responses
primary: true
}
],
updatedBy: {
id: new ObjectId(),
name: 'Test'
}
}
const channels = [
tcpToTcpChannelDoc,
tlsToTcpChannelDoc,
tcpToHttpChannelDoc,
tcpToTlsChannelDoc,
tcpToTlsNoCertChannelDoc,
tcpToMllpChannelDoc,
tcpTimeoutChannel
]
describe('TCP/TLS/MLLP Integration Tests', () => {
let mockServer
const sandbox = sinon.createSandbox()
const ORIGINAL_TCP_ADAPTER = config.tcpAdapter
before(async () => {
config.tcpAdapter = config.get(`tcpAdapter`)
const keystore = await testUtils.setupTestKeystore()
const cert = new CertificateModel({
data: fs.readFileSync('test/resources/server-tls/cert.pem')
})
keystore.ca.push(cert)
await keystore.save()
await Promise.all([
testUtils.setupTestUsers(),
...channels.map(c => {
const clone = testUtils.clone(c)
if (clone.routes[0].secured) {
clone.routes[0].cert = cert._id
}
return ChannelModel(clone).save()
})
])
return promisify(server.start)({ tcpHttpReceiverPort: SERVER_PORTS.tcpHttpReceiverPort })
})
after(async () => {
config.tcpAdapter = ORIGINAL_TCP_ADAPTER
await Promise.all([
promisify(server.stop)(),
testUtils.cleanupTestUsers()
])
})
afterEach(async () => {
await Promise.all([
mockServer.close(),
TransactionModel.deleteMany({})
])
sandbox.reset()
mockServer = null
})
it('will route tcp -> tcp', async () => {
const request = 'Tcp Request'
let expectedResp
const spy = sandbox.spy(async data => {
expectedResp = data + ' with tcp response'
return expectedResp
})
mockServer = await testUtils.createMockTCPServer(spy, tcpToTcpChannelDoc.routes[0].port)
const res = await testUtils.socketTest(tcpToTcpChannelDoc.tcpPort, request)
res.toString().should.eql(expectedResp)
spy.callCount.should.eql(1)
})
it('will timeout a socket', async () => {
const mllpEndChar = String.fromCharCode(0o034)
const request = 'Tcp Request'
const spy = sandbox.spy(async data => {
await testUtils.wait(30)
return `should never get this with tcp response` + mllpEndChar
})
mockServer = await testUtils.createMockTCPServer(spy, tcpTimeoutChannel.routes[0].port)
const res = await testUtils.socketTest(tcpTimeoutChannel.tcpPort, request)
res.toString().should.eql(`An internal server error occurred`)
spy.callCount.should.eql(1)
const transactions = await TransactionModel.find({})
transactions.length.should.eql(1)
transactions[0].error.message.should.eql('Request took longer than 20ms')
})
it('will route tls -> tcp', async () => {
const request = 'Tls Request'
let expectedResp
const spy = sandbox.spy(async data => {
expectedResp = data + ' with tcp response'
return expectedResp
})
mockServer = await testUtils.createMockTCPServer(spy, tlsToTcpChannelDoc.routes[0].port)
const res = await testUtils.secureSocketTest(tlsToTcpChannelDoc.tcpPort, request)
res.toString().should.eql(expectedResp)
spy.callCount.should.eql(1)
})
it('will route tcp -> http', async () => {
const request = 'Tcp Request'
let expectedResp
const spy = sandbox.spy(async req => {
const body = await testUtils.readBody(req)
expectedResp = body + ' with http response'
return expectedResp
})
mockServer = await testUtils.createMockHttpServer(spy, tcpToHttpChannelDoc.routes[0].port)
const res = await testUtils.socketTest(tcpToHttpChannelDoc.tcpPort, request)
res.toString().should.eql(expectedResp)
spy.callCount.should.eql(1)
})
it('will route tcp -> tls', async () => {
const request = 'Tcp Request'
let expectedResp
const spy = sandbox.spy(async data => {
expectedResp = data + ' with tls response'
return expectedResp
})
mockServer = await testUtils.createMockTLSServerWithMutualAuth(spy, tcpToTlsChannelDoc.routes[0].port)
const res = await testUtils.socketTest(tcpToTlsChannelDoc.tcpPort, request)
res.toString().should.eql(expectedResp)
spy.callCount.should.eql(1)
})
it('will route tcp -> tls no auth will fail', async () => {
const spy = sandbox.spy()
mockServer = await testUtils.createMockTLSServerWithMutualAuth(spy, tcpToTlsNoCertChannelDoc.routes[0].port, false)
const resp = await testUtils.socketTest(tcpToTlsNoCertChannelDoc.tcpPort, 'Data')
resp.toString().should.eql('An internal server error occurred')
spy.callCount.should.eql(0)
await testUtils.pollCondition(() => TransactionModel.countDocuments().then(c => c === 1))
const tran = await TransactionModel.findOne()
tran.status.should.eql('Failed')
})
it(`will route tcp -> mllp`, async () => {
const mllpEndChar = String.fromCharCode(0o034)
const request = 'Tcp Request'
let expectedResp
const spy = sandbox.spy(async data => {
expectedResp = data + ' with tcp response' + mllpEndChar
return expectedResp
})
mockServer = await testUtils.createMockTCPServer(spy, tcpToMllpChannelDoc.routes[0].port)
const res = await testUtils.socketTest(tcpToMllpChannelDoc.tcpPort, request)
res.toString().should.eql(expectedResp)
spy.callCount.should.eql(1)
})
})