UNPKG

openhim-core

Version:

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

542 lines (476 loc) 21.5 kB
should = require "should" sinon = require "sinon" http = require "http" messageStore = require "../../lib/middleware/messageStore" Transaction = require("../../lib/model/transactions").Transaction ObjectId = require('mongoose').Types.ObjectId Channel = require("../../lib/model/channels").Channel transactionId = null describe "MessageStore", -> channel1 = { name: "TestChannel1" urlPattern: "test/sample" allow: [ "PoC", "Test1", "Test2" ] routes: [ { name: "test route" host: "localhost" port: 9876 primary: true }, { name: "test route 2" host: "localhost" port: 9876 primary: true } ] txViewAcl: "aGroup" } channel2 = { name: "TestChannel2" urlPattern: "test/sample" allow: [ "PoC", "Test1", "Test2" ] routes: [ name: "test route" host: "localhost" port: 9876 primary: true ] txViewAcl: "group1" } req = new Object() req.path = "/api/test/request" req.headers = headerName: "headerValue" "Content-Type": "application/json" "Content-Length": "9313219921" req.querystring = "param1=value1&param2=value2" req.body = "<HTTP body>" req.method = "POST" req.timestamp = new Date() res = new Object() res.status = "200" res.headers = header: "value" header2: "value2" res.body = "<HTTP response>" res.timestamp = new Date() ctx = null beforeEach (done) -> ctx = new Object() ctx.host = 'localhost:5000' ctx.path = "/api/test/request" ctx.header = headerName: "headerValue" "Content-Type": "application/json" "Content-Length": "9313219921" ctx.querystring = "param1=value1&param2=value2" ctx.body = "<HTTP body>" ctx.method = "POST" ctx.status = "Processing" ctx.authenticated = new Object() ctx.authenticated._id = new ObjectId "313233343536373839319999" ctx.authorisedChannel = new Object() ctx.authorisedChannel.requestBody = true ctx.authorisedChannel.responseBody = true Transaction.remove {}, -> Channel.remove {}, -> (new Channel channel1).save (err, ch1) -> channel1._id = ch1._id ctx.authorisedChannel._id = ch1._id (new Channel channel2).save (err, ch2) -> channel2._id = ch2._id done() afterEach (done)-> Transaction.remove {}, -> Channel.remove {}, -> done() describe ".storeTransaction", -> it "should be able to save the transaction in the db", (done) -> messageStore.storeTransaction ctx, (error, result) -> should.not.exist(error) Transaction.findOne { '_id': result._id }, (error, trans) -> should.not.exist(error) (trans != null).should.be.true() trans.clientID.toString().should.equal "313233343536373839319999" trans.status.should.equal "Processing" trans.status.should.not.equal "None" trans.request.path.should.equal "/api/test/request" trans.request.headers['Content-Type'].should.equal "application/json" trans.request.querystring.should.equal "param1=value1&param2=value2" trans.request.host.should.equal 'localhost' trans.request.port.should.equal '5000' trans.channelID.toString().should.equal channel1._id.toString() done() it "should be able to save the transaction if the headers contain Mongo reserved characters ($ or .)", (done) -> ctx.header['dot.header'] = '123' ctx.header['dollar$header'] = '124' messageStore.storeTransaction ctx, (error, result) -> #cleanup ctx before moving on in case there's a failure delete ctx.header['dot.header'] delete ctx.header['dollar$header'] should.not.exist(error) Transaction.findOne { '_id': result._id }, (error, trans) -> should.not.exist(error) (trans != null).should.be.true() trans.request.headers['dot.header'].should.equal '123' trans.request.headers['dollar$header'].should.equal '124' ctx.header['X-OpenHIM-TransactionID'].should.equal result._id.toString() done() it "should truncate the request body if it exceeds storage limits", (done) -> ctx.body = '' # generate a big body ctx.body += '1234567890' for i in [0...2000*1024] messageStore.storeTransaction ctx, (error, result) -> should.not.exist(error) Transaction.findOne { '_id': result._id }, (error, trans) -> should.not.exist(error) (trans != null).should.be.true() trans.request.body.length.should.be.exactly messageStore.MAX_BODIES_SIZE trans.canRerun.should.be.false() done() describe ".storeResponse", -> beforeEach (done) -> Channel.remove {}, -> (new Channel channel1).save (err, ch1) -> channel1._id = ch1._id ctx.authorisedChannel._id = ch1._id (new Channel channel2).save (err, ch2) -> channel2._id = ch2._id done() afterEach (done)-> Transaction.remove {}, -> Channel.remove {}, -> done() createResponse = (status) -> return { status: status header: testHeader: "value" body: new Buffer "<HTTP response body>" timestamp: new Date() } createRoute = (name, status) -> return { name: name request: host: "localhost" port: "4466" path: "/test" timestamp: new Date() response: status: status headers: test: "test" body: "route body" timestamp: new Date() } it "should update the transaction with the response", (done) -> ctx.response = createResponse 201 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) messageStore.setFinalStatus ctx, -> Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.response.status.should.equal 201 trans.response.headers.testHeader.should.equal "value" trans.response.body.should.equal "<HTTP response body>" trans.status.should.equal "Successful" done() it "should update the transaction with the responses from non-primary routes", (done) -> ctx.response = createResponse 201 route = createRoute "route1", 200 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) messageStore.storeNonPrimaryResponse ctx, route, -> Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.routes.length.should.be.exactly 1 trans.routes[0].name.should.equal "route1" trans.routes[0].response.status.should.equal 200 trans.routes[0].response.headers.test.should.equal "test" trans.routes[0].response.body.should.equal "route body" trans.routes[0].request.path.should.equal "/test" trans.routes[0].request.host.should.equal 'localhost' trans.routes[0].request.port.should.equal '4466' done() it "should set the ctx.transactionStatus variable with the final status", (done) -> ctx.response = createResponse 201 ctx.transactionStatus = null messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) messageStore.setFinalStatus ctx, -> should(ctx.transactionStatus).be.exactly 'Successful' done() it "should set the status to successful if all route return a status in 2xx", (done) -> ctx.response = createResponse 201 route1 = createRoute "route1", 200 route2 = createRoute "route2", 201 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, route1, -> messageStore.storeNonPrimaryResponse ctx, route2, -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Successful" done() it "should set the status to failed if the primary route return a status in 5xx", (done) -> ctx.response = createResponse 500 ctx.routes = [] ctx.routes.push createRoute "route1", 200 ctx.routes.push createRoute "route2", 201 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Failed" done() it "should set the status to completed with errors if the primary route return a status in 2xx or 4xx but one or more routes return 5xx", (done) -> ctx.response = createResponse 404 ctx.routes = [] ctx.routes.push createRoute "route1", 201 ctx.routes.push createRoute "route2", 501 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Completed with error(s)" done() it "should set the status to completed if any route returns a status in 4xx (test 1)", (done) -> ctx.response = createResponse 201 ctx.routes = [] ctx.routes.push createRoute "route1", 201 ctx.routes.push createRoute "route2", 404 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Completed" done() it "should set the status to completed if any route returns a status in 4xx (test 2)", (done) -> ctx.response = createResponse 404 ctx.routes = [] ctx.routes.push createRoute "route1", 201 ctx.routes.push createRoute "route2", 404 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Completed" done() it "should set the status to completed if any other response code is recieved on primary", (done) -> ctx.response = createResponse 302 ctx.routes = [] ctx.routes.push createRoute "route1", 201 ctx.routes.push createRoute "route2", 200 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Completed" done() it "should set the status to completed if any other response code is recieved on secondary routes", (done) -> ctx.response = createResponse 200 ctx.routes = [] ctx.routes.push createRoute "route1", 302 ctx.routes.push createRoute "route2", 200 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.request = storedTrans.request ctx.request.header = {} ctx.transactionId = storedTrans._id ctx.request.header["X-OpenHIM-TransactionID"] = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.status.should.be.exactly "Completed" done() createResponseWithReservedChars = (status) -> return { status: status header: "dot.header": "123" "dollar$header": "124" body: new Buffer "<HTTP response body>" timestamp: new Date() } it "should be able to save the response if the headers contain Mongo reserved characters ($ or .)", (done) -> ctx.response = createResponseWithReservedChars 200 messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.response.headers['dot.header'].should.equal '123' trans.response.headers['dollar$header'].should.equal '124' done() it "should remove the request body if set in channel settings and save to the DB", (done) -> ctx.authorisedChannel.requestBody = false messageStore.storeTransaction ctx, (error, result) -> should.not.exist(error) Transaction.findOne { '_id': result._id }, (error, trans) -> should.not.exist(error) (trans != null).should.be.true() trans.clientID.toString().should.equal "313233343536373839319999" trans.channelID.toString().should.equal channel1._id.toString() trans.status.should.equal "Processing" trans.request.body.should.equal "" trans.canRerun.should.equal false done() it "should update the transaction with the response and remove the response body", (done) -> ctx.response = createResponse 201 ctx.authorisedChannel.responseBody = false messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() trans.response.status.should.equal 201 trans.response.body.should.equal "" done() it "should truncate the response body if it exceeds storage limits", (done) -> ctx.response = createResponse 201 ctx.response.body = '' ctx.response.body += '1234567890' for i in [0...2000*1024] messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) messageStore.setFinalStatus ctx, -> Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() expectedLen = messageStore.MAX_BODIES_SIZE - ctx.body.length trans.response.body.length.should.be.exactly expectedLen done() it "should truncate the response body for orchestrations if it exceeds storage limits", (done) -> ctx.response = createResponse 201 ctx.mediatorResponse = orchestrations: [ name: 'orch1' request: host: "localhost" port: "4466" path: "/test" body: "orch body" timestamp: new Date() response: status: 201 timestamp: new Date() , name: 'orch2' request: host: "localhost" port: "4466" path: "/test" timestamp: new Date() response: status: 200 headers: test: "test" timestamp: new Date() ] ctx.mediatorResponse.orchestrations[1].response.body += '1234567890' for i in [0...2000*1024] messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> should.not.exist(err2) messageStore.setFinalStatus ctx, -> Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() expectedLen = messageStore.MAX_BODIES_SIZE - ctx.body.length - ctx.response.body.length - ctx.mediatorResponse.orchestrations[0].request.body.length trans.orchestrations[1].response.body.length.should.be.exactly expectedLen done() it "should truncate the response body for routes if they exceed storage limits", (done) -> ctx.response = createResponse 201 ctx.routes = [] ctx.routes.push createRoute "route1", 201 ctx.routes.push createRoute "route2", 200 ctx.routes[1].response.body += '1234567890' for i in [0...2000*1024] messageStore.storeTransaction ctx, (err, storedTrans) -> ctx.transactionId = storedTrans._id messageStore.storeResponse ctx, (err2) -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[0], -> messageStore.storeNonPrimaryResponse ctx, ctx.routes[1], -> messageStore.setFinalStatus ctx, -> Transaction.findOne { '_id': storedTrans._id }, (err3, trans) -> should.not.exist(err3) (trans != null).should.be.true() expectedLen = messageStore.MAX_BODIES_SIZE - ctx.body.length - ctx.response.body.length - ctx.routes[0].response.body.length trans.routes[1].response.body.length.should.be.exactly expectedLen done()