UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

1,514 lines (1,371 loc) 62.1 kB
/* eslint-env mocha */ /* eslint no-unused-expressions:0 */ import request from 'supertest' import sinon from 'sinon' import mongoose from 'mongoose' import * as server from '../../src/server' import * as tcpAdapter from '../../src/tcpAdapter' import * as polling from '../../src/polling' import { ChannelModelAPI } from '../../src/model/channels' import { TransactionModelAPI } from '../../src/model/transactions' import * as testUtils from '../utils' import { promisify } from 'util' import * as constants from '../constants' import should from 'should' import { ObjectId } from 'mongodb' import { config } from '../../src/config' import { ClientModelAPI } from '../../src/model/clients' const { SERVER_PORTS } = constants let sandbox = sinon.createSandbox() describe('API Integration Tests', () => { const httpPortPlus40 = constants.PORT_START + 40 const httpPortPlus41 = constants.PORT_START + 41 describe('Channels REST Api testing', () => { const channel1 = { name: 'TestChannel1', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true } ], txViewAcl: 'aGroup', updatedBy: { id: new ObjectId(), name: 'Test' } } const channel2 = { name: 'TestChannel2', 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' } } let authDetails = {} before(async () => { await testUtils.setupTestUsers() await promisify(server.start)({ apiPort: SERVER_PORTS.apiPort, tcpHttpReceiverPort: SERVER_PORTS.tcpHttpReceiverPort, pollingPort: SERVER_PORTS.pollingPort }) authDetails = await testUtils.getAuthDetails() await Promise.all([ TransactionModelAPI.deleteMany({}), ChannelModelAPI.deleteMany({}) ]) }) after(async () => { await Promise.all([ TransactionModelAPI.deleteMany({}), ChannelModelAPI.deleteMany({}), testUtils.cleanupTestUsers(), promisify(server.stop)() ]) }) afterEach(async () => { await sandbox.restore() }) beforeEach(async () => { await Promise.all([ TransactionModelAPI.deleteMany({}), ChannelModelAPI.deleteMany({}) ]) const ch1 = await (new ChannelModelAPI(channel1)).save() channel1._id = ch1._id const ch2 = await (new ChannelModelAPI(channel2)).save() channel2._id = ch2._id sandbox.stub(tcpAdapter, 'notifyMasterToStartTCPServer') sandbox.stub(tcpAdapter, 'notifyMasterToStopTCPServer') }) describe('*getChannels()', () => { it('should fetch all channels', async () => { const result = await request(constants.BASE_URL) .get('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .expect(200) result.body.length.should.be.eql(2) }) it('should only allow non root user to fetch channel that they are allowed to view', async () => { const result = await request(constants.BASE_URL) .get('/channels') .set('auth-username', testUtils.nonRootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .expect(200) result.body.length.should.be.eql(1) result.body[0].name.should.be.eql('TestChannel2') }) }) describe('*addChannel()', () => { it('should add a new channel', async () => { const newChannel = { name: 'NewChannel', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(201) const channel = await ChannelModelAPI.findOne({ name: 'NewChannel' }) channel.should.have.property('urlPattern', 'test/sample') channel.allow.should.have.length(3) }) it('should reject a channel without a name', async () => { const newChannel = { urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(400) }) it('should reject invalid channels with invalid pathTransform', async () => { const invalidChannel = { name: 'InvalidChannel', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', pathTransform: 'invalid', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(invalidChannel) .expect(400) }) it('should reject channels containing both path and pathTransform', async () => { const invalidChannel = { name: 'InvalidChannel', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', path: '/target', pathTransform: 's/foo/bar', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(invalidChannel) .expect(400) }) it('should not allow a non admin user to add a channel', async () => { const newChannel = {} await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.nonRootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(403) }) it('should notify master to startup TCP server if the new channel is of type "tcp"', async () => { const tcpChannel = { name: 'TCPTestChannel-Add', urlPattern: '/', allow: ['tcp'], type: 'tcp', tcpHost: '0.0.0.0', tcpPort: SERVER_PORTS.tcpPort, routes: [{ name: 'TcpRoute', host: 'localhost', port: 9876, primary: true, type: 'tcp' }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(tcpChannel) .expect(201) sinon.assert.calledOnce(tcpAdapter.notifyMasterToStartTCPServer) }) it('should NOT notify master to startup TCP server if the new channel is of type "tcp" but is disabled', async () => { const tcpChannelDisabled = { name: 'TCPTestChannel-Add-Disabled', urlPattern: '/', allow: ['tcp'], type: 'tcp', tcpHost: '0.0.0.0', tcpPort: SERVER_PORTS.tcpPort, routes: [{ name: 'TcpRoute', host: 'localhost', port: 9876, primary: true, type: 'tcp' }], status: 'disabled' } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(tcpChannelDisabled) .expect(201) sinon.assert.notCalled(tcpAdapter.notifyMasterToStartTCPServer) }) it('should register the channel with the polling service if of type "polling"', async () => { const pollChannel = { name: 'POLLINGTestChannel-Add', urlPattern: '/trigger', allow: ['polling'], type: 'polling', pollingSchedule: '5 * * * *', routes: [{ name: 'PollRoute', host: 'localhost', port: 9876, primary: true }] } const spy = sinon.spy(polling, 'registerPollingChannel') await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(pollChannel) .expect(201) spy.restore() spy.calledOnce.should.be.true() spy.getCall(0).args[0].should.have.property('name', 'POLLINGTestChannel-Add') spy.getCall(0).args[0].should.have.property('urlPattern', '/trigger') spy.getCall(0).args[0].should.have.property('type', 'polling') }) it('should NOT register the channel with the polling service if of type "polling" but is disabled', async () => { const pollChannelDisabled = { name: 'POLLINGTestChannel-Add-Disabled', urlPattern: '/trigger', allow: ['polling'], type: 'polling', pollingSchedule: '5 * * * *', routes: [{ name: 'PollRoute', host: 'localhost', port: 9876, primary: true }], status: 'disabled' } const spy = sinon.spy(polling, 'registerPollingChannel') await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(pollChannelDisabled) .expect(201) spy.restore() spy.callCount.should.be.exactly(0) }) it('should reject a channel without a primary route', async () => { const newChannel = { name: 'no-primary-route-test', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', port: 9876 }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(400) }) it('should reject a channel with multiple primary routes', async () => { const newChannel = { name: 'mulitple-primary-route-test', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [ { name: 'test route', host: 'localhost', port: 9876, primary: true }, { name: 'test route 2', host: 'localhost', port: 9877, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(400) }) it('should accept a channel with one enabled primary route but multiple disabled primary routes', async () => { const newChannel = { name: 'disabled-primary-route-test', urlPattern: 'test/sample', allow: ['PoC', 'Test1', 'Test2'], routes: [ { name: 'test route', host: 'localhost', port: 9876, primary: true }, { name: 'test route 2', host: 'localhost', port: 9877, primary: true, status: 'disabled' }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(201) }) it('should reject a channel with a priority below 1', async () => { const newChannel = { name: 'Channel-Priority--1', urlPattern: 'test/sample', priority: -1, allow: ['PoC', 'Test1', 'Test2'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(newChannel) .expect(400) }) it('will create a channel with methods', async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', methods: ['GET', 'OPTIONS'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDoc) .expect(201) const channel = await ChannelModelAPI.findOne({ name: methodChannelDoc.name }) channel.methods.should.containDeep(methodChannelDoc.methods) }) it(`will reject the request if the channel has methods but is not http`, async () => { const methodChannelDocRejected = { name: 'method channel rejected', urlPattern: 'test/method', type: 'tcp', methods: ['GET', 'OPTIONS'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDocRejected) .expect(400) const channelCount = await ChannelModelAPI.countDocuments({ name: methodChannelDocRejected.name }) channelCount.should.eql(0) }) it(`will reject the request if the channel repeats methods`, async () => { const methodChannelDocRejected = { name: 'method channel rejected', urlPattern: 'test/method', type: 'http', methods: ['POST', 'POST', 'GET', 'OPTIONS', 'GET'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } const res = await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDocRejected) .expect(400) res.text.should.eql("Channel methods can't be repeated. Repeated methods are GET, POST") const channelCount = await ChannelModelAPI.countDocuments({ name: methodChannelDocRejected.name }) channelCount.should.eql(0) }) it('will reject a channel with a maxBodyAge set if the request or response is not', async () => { const methodChannelDoc = { name: 'maxBodyAgeRejected', urlPattern: 'test/method', maxBodyAgeDays: 5, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDoc) .expect(400) const channelCount = await ChannelModelAPI.countDocuments({ name: methodChannelDoc.name }) channelCount.should.eql(0) }) it('will reject a channel with a maxBodyAge greater than 36500', async () => { const methodChannelDoc = { name: 'maxBodyAgeOver', urlPattern: 'test/method', maxBodyAgeDays: 36501, requestBody: true, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDoc) .expect(400) const channelCount = await ChannelModelAPI.countDocuments({ name: methodChannelDoc.name }) channelCount.should.eql(0) }) it('will create a channel with a maxBodyAge', async () => { const methodChannelDoc = { name: 'maxBodyAge', urlPattern: 'test/method', maxBodyAgeDays: 5, requestBody: true, responseBody: true, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodChannelDoc) .expect(201) const channel = await ChannelModelAPI.findOne({ name: methodChannelDoc.name }) channel.maxBodyAgeDays.should.eql(5) }) it(`will create a channel with a timeout`, async () => { const timeoutChannelDoc = { name: 'timeout', urlPattern: 'test/method', timeout: 10, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(timeoutChannelDoc) .expect(201) const channel = await ChannelModelAPI.findOne({ name: timeoutChannelDoc.name }) channel.timeout.should.eql(10) }) it(`will reject a channel with a timeout with negative value`, async () => { const timeoutChannelDoc = { name: 'timeout', urlPattern: 'test/method', timeout: -1, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }] } await request(constants.BASE_URL) .post('/channels') .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(timeoutChannelDoc) .expect(400) const channel = await ChannelModelAPI.findOne({ name: timeoutChannelDoc.name }) should(channel).null() }) }) describe('*getChannel(channelId)', () => { it('should fetch a specific channel by id', async () => { const res = await request(constants.BASE_URL) .get(`/channels/${channel1._id}`) .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.should.have.property('name', 'TestChannel1') res.body.should.have.property('urlPattern', 'test/sample') res.body.allow.should.have.length(3) }) it('should not allow a non admin user from fetching a channel they dont have access to by name', async () => { await request(constants.BASE_URL) .get(`/channels/${channel1._id}`) .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 allow a non admin user to fetch a channel they have access to by name', async () => { const res = await request(constants.BASE_URL) .get(`/channels/${channel2._id}`) .set('auth-username', testUtils.nonRootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .expect(200) res.body.should.have.property('name', 'TestChannel2') res.body.should.have.property('urlPattern', 'test/sample') res.body.allow.should.have.length(3) }) it(`will default the channel methods as an empty array on existing channels`, async () => { const mongoClient = await testUtils.getMongoClient() const noMethodChannelDoc = { name: 'method channel', urlPattern: 'test/method', routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { insertedId: id } = await mongoClient.db().collection('channels').insertOne(noMethodChannelDoc) const resp = await request(constants.BASE_URL) .get(`/channels/${id.toString()}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .expect(200) resp.body.should.property('methods') }) it('should return a 404 if that channel doesnt exist', async () => { await request(constants.BASE_URL) .get('/channels/999999999999999999999999') .set('auth-username', testUtils.nonRootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .expect(404) }) }) describe('getChannelAudits(channelId)', () => { let expectedPatches beforeEach(async () => { await ChannelModelAPI.Patches.deleteMany({}).exec() const patches = await ChannelModelAPI.Patches.create([ { ref: channel1._id, ops: [ { value: 'before', path: '/name', op: 'add' } ], updatedBy: { id: new ObjectId(), name: 'Test' } }, { ref: channel2._id, ops: [ { value: 'nope', path: '/name', op: 'add' } ], updatedBy: { id: new ObjectId(), name: 'Test' } }, { ref: channel1._id, ops: [ { value: 'after', path: '/name', op: 'replace' } ], updatedBy: { id: new ObjectId(), name: 'Test' } }, { ref: channel1._id, ops: [ { value: new Date(), path: '/lastBodyCleared', op: 'replace' } ], updatedBy: { id: new ObjectId(), name: 'Test' } }, { ref: channel1._id, ops: [ { value: new Date(), path: '/lastBodyCleared', op: 'add' } ], updatedBy: { id: new ObjectId(), name: 'Test' } } ]) expectedPatches = patches.reverse().filter(patch => patch.ref.equals(channel1._id)).filter(patch => patch.ops[0].path !== '/lastBodyCleared').map(patch => { const convertedPatch = patch.toObject() convertedPatch._id = convertedPatch._id.toString() convertedPatch.ref = convertedPatch.ref.toString() convertedPatch.date = convertedPatch.date.toISOString() convertedPatch.updatedBy.id = convertedPatch.updatedBy.id.toString() return convertedPatch }) }) it('should return the patches for the correct channel', async () => { const res = await request(constants.BASE_URL) .get(`/channels/${channel1._id}/audits`) .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.should.eql(expectedPatches) }) it('should return an empty array when the channel does not exist', async () => { const res = await request(constants.BASE_URL) .get('/channels/59f6d57b07552f280271efac/audits') .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.should.eql([]) }) }) describe('*updateChannel(channelId)', () => { it('should update a specific channel by id', async () => { const updates = { _id: 'thisShouldBeIgnored', urlPattern: 'test/changed', allow: ['PoC', 'Test1', 'Test2', 'another'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }, { name: 'test route2', host: 'localhost', port: 8899 }] } await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(200) const channel = await ChannelModelAPI.findOne({ name: 'TestChannel1' }) channel.should.have.property('name', 'TestChannel1') channel.should.have.property('urlPattern', 'test/changed') channel.allow.should.have.length(4) channel.routes.should.have.length(2) }) it('should not allow a non admin user to update a channel', async () => { const updates = {} await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.nonRootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(403) }) it('should notify master to startup a TCP server if the type is set to "tcp"', async () => { const httpChannel = new ChannelModelAPI({ name: 'TestChannelForTCPUpdate', urlPattern: '/', allow: ['test'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], txViewAcl: 'group1', updatedBy: { id: new ObjectId(), name: 'Test' } }) const changeToTCP = { type: 'tcp', tcpHost: '0.0.0.0', tcpPort: 3601 } await httpChannel.save() await request(constants.BASE_URL) .put(`/channels/${httpChannel._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(changeToTCP) .expect(200) sinon.assert.calledOnce(tcpAdapter.notifyMasterToStartTCPServer) }) it('should NOT notify master to startup a TCP server if the type is set to "tcp" but it is disabled', async () => { const httpChannel = new ChannelModelAPI({ name: 'TestChannelForTCPUpdate-Disabled', urlPattern: '/', allow: ['test'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], txViewAcl: 'group1', updatedBy: { id: new ObjectId(), name: 'Test' } }) const changeToTCPDisabled = { type: 'tcp', tcpHost: '0.0.0.0', tcpPort: 3603, status: 'disabled' } await httpChannel.save() await request(constants.BASE_URL) .put(`/channels/${httpChannel._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(changeToTCPDisabled) .expect(200) sinon.assert.notCalled(tcpAdapter.notifyMasterToStartTCPServer) sinon.assert.calledOnce(tcpAdapter.notifyMasterToStopTCPServer) }) it('should register the updated channel with the polling service if of type "polling"', async () => { const pollChannel = new ChannelModelAPI({ name: 'POLLINGTestChannel-Update', urlPattern: '/trigger', allow: ['polling'], type: 'polling', pollingSchedule: '5 * * * *', routes: [{ name: 'PollRoute', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } }) const spy = sinon.spy(polling, 'registerPollingChannel') await pollChannel.save() await request(constants.BASE_URL) .put(`/channels/${pollChannel._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(pollChannel) .expect(200) spy.restore() spy.calledOnce.should.be.true() spy.getCall(0).args[0].should.have.property('name', 'POLLINGTestChannel-Update') spy.getCall(0).args[0].should.have.property('urlPattern', '/trigger') spy.getCall(0).args[0].should.have.property('type', 'polling') spy.getCall(0).args[0].should.have.property('_id', pollChannel._id) }) it('should NOT register the updated channel with the polling service if of type "polling" but it is disabled', async () => { const pollChannel = new ChannelModelAPI({ name: 'POLLINGTestChannel-Update-Disabled', urlPattern: '/trigger', allow: ['polling'], type: 'polling', pollingSchedule: '5 * * * *', routes: [{ name: 'PollRoute', host: 'localhost', port: 9876, primary: true }], status: 'disabled', updatedBy: { id: new ObjectId(), name: 'Test' } }) const spy = sinon.spy(polling, 'registerPollingChannel') await pollChannel.save() await request(constants.BASE_URL) .put(`/channels/${pollChannel._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(pollChannel) .expect(200) spy.restore() spy.callCount.should.be.exactly(0) }) it('should reject an update with no primary routes', async () => { const updates = { urlPattern: 'test/changed', allow: ['PoC', 'Test1', 'Test2', 'another'], routes: [{ name: 'test route', host: 'localhost', port: 9876 }, { name: 'test route2', host: 'localhost', port: 8899 }] } await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(400) }) it('should reject an update with multiple primary routes', async () => { const updates = { urlPattern: 'test/changed', allow: ['PoC', 'Test1', 'Test2', 'another'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }, { name: 'test route2', host: 'localhost', port: 8899, primary: true }] } await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(400) }) it('should accept an update with one primary route and multiple disabled primary routes', async () => { const updates = { urlPattern: 'test/changed', allow: ['PoC', 'Test1', 'Test2', 'another'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }, { name: 'test route2', host: 'localhost', port: 8899, primary: true, status: 'disabled' }] } await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(200) }) it('should NOT update a channel with a priority below 1', async () => { const updates = { urlPattern: 'test/changed', priority: -1 } await request(constants.BASE_URL) .put(`/channels/${channel1._id}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(updates) .expect(400) const channel = await ChannelModelAPI.findOne({ name: 'TestChannel1' }) channel.should.have.property('urlPattern', 'test/sample') }) it('should remove the methods if the type is chaned from http', async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', methods: ['GET', 'OPTIONS'], routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ type: 'tcp' }) .expect(200) const channel = await ChannelModelAPI.findById(channelId) channel.should.have.property('type', 'tcp') channel.methods.length.should.eql(0) }) it('should reject the update if the methods is defined but type is not http', async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', type: 'tcp', routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ methods: ['GET'] }) .expect(400) const channel = await ChannelModelAPI.findById(channelId) channel.should.have.property('type', 'tcp') channel.methods.length.should.eql(0) }) it('should update the methods', async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ methods: ['GET'] }) .expect(200) const channel = await ChannelModelAPI.findById(channelId) channel.should.have.property('type', 'http') channel.methods.length.should.eql(1) channel.methods[0].should.eql('GET') }) it(`should reject the update if the channel repeats methods`, async () => { const methodChannelDocRejected = { name: 'method channel rejected', urlPattern: 'test/method', type: 'http', routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const methodUpdate = { methods: ['POST', 'POST', 'GET', 'OPTIONS', 'GET'] } const { _id: channelId } = await new ChannelModelAPI(methodChannelDocRejected).save() const res = await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send(methodUpdate) .expect(400) res.text.should.eql("Channel methods can't be repeated. Repeated methods are GET, POST") const channelCount = await ChannelModelAPI.countDocuments({ name: methodChannelDocRejected.name }) channelCount.should.eql(1) const channel = await ChannelModelAPI.findById(channelId) channel.methods.length.should.eql(0) }) it(`should fail to update a channel with maxBodyAgeDays if requestBody nor responseBody is true`, async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: 2 }) .expect(400) const channel = await ChannelModelAPI.findById(channelId) should(channel.maxBodyAgeDays == null).true() }) it(`should update the channel with maxBodyAgeDays`, async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', requestBody: true, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: 2 }) .expect(200) const channel = await ChannelModelAPI.findById(channelId) channel.maxBodyAgeDays.should.eql(2) }) it(`should fail to update the channel with maxBodyAgeDays with a negative value`, async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', requestBody: true, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: -1 }) .expect(400) const channel = await ChannelModelAPI.findById(channelId) channel.should.not.property('maxBodyAge') }) it(`should fail to update the channel with maxBodyAgeDays a value greater than 36500`, async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', requestBody: true, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: 36501 }) .expect(400) const channel = await ChannelModelAPI.findById(channelId) channel.should.not.property('maxBodyAge') }) it(`should be able to remove the maxBodyAgeDays value`, async () => { const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', maxBodyAgeDays: 1, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: null }) .expect(200) const channel = await ChannelModelAPI.findById(channelId) channel.should.not.property('maxBodyAge') }) it(`will clear the lastBodyCleared if the maxBodyAgeDays is cleared`, async () => { // if the maxBodyAgeDays differ then clear the lastTime it was cleared const methodChannelDoc = { name: 'method channel', urlPattern: 'test/method', requestBody: true, maxBodyAgeDays: 1, lastBodyCleared: new Date(), routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(methodChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .set('auth-salt', authDetails.authSalt) .set('auth-token', authDetails.authToken) .send({ maxBodyAgeDays: 2 }) .expect(200) const channel = await ChannelModelAPI.findById(channelId) channel.should.property('lastBodyCleared', undefined) }) it('will update a timeout', async () => { const timeoutChannelDoc = { name: 'timeout', urlPattern: 'test/method', timeout: 10, routes: [{ name: 'test route', host: 'localhost', port: 9876, primary: true }], updatedBy: { id: new ObjectId(), name: 'Test' } } const { _id: channelId } = await new ChannelModelAPI(timeoutChannelDoc).save() await request(constants.BASE_URL) .put(`/channels/${channelId}`) .set('auth-username', testUtils.rootUser.email) .set('auth-ts', authDetails.authTS) .