UNPKG

openhim-core

Version:

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

866 lines (781 loc) 33.5 kB
should = require "should" request = require "supertest" Transaction = require("../../lib/model/transactions").Transaction Channel = require("../../lib/model/channels").Channel User = require('../../lib/model/users').User server = require "../../lib/server" testUtils = require "../testUtils" auth = require("../testUtils").auth FakeServer = require "../fakeTcpServer" config = require '../../lib/config/config' apiConf = config.get 'api' Event = require("../../lib/model/events").Event AutoRetry = require('../../lib/model/autoRetry').AutoRetry application = config.get 'application' os = require "os" domain = os.hostname() + '.' + application.name describe "API Integration Tests", -> beforeEach (done) -> Transaction.remove {}, -> done() afterEach (done)-> Transaction.remove {}, -> done() describe "Transactions REST Api testing", -> transactionId = null requ = path: "/api/test" headers: "header-title": "header1-value" "another-header": "another-header-value" querystring: "param1=value1&param2=value2" body: "<HTTP body request>" method: "POST" timestamp: "2014-06-09T11:17:25.929Z" respo = status: "200" headers: header: "value" header2: "value2" body: "<HTTP response>" timestamp: "2014-06-09T11:17:25.929Z" transactionData = _id: "111111111111111111111111" status: "Processing" clientID: "999999999999999999999999" channelID: "888888888888888888888888" request: requ response: respo routes: [ name: "dummy-route" request: requ response: respo ] orchestrations: [ name: "dummy-orchestration" request: requ response: respo ] properties: "prop1": "prop1-value1" "prop2": "prop-value1" authDetails = {} channel = new Channel name: "TestChannel1" urlPattern: "test/sample" allow: [ "PoC", "Test1", "Test2" ] routes: [ name: "test route" host: "localhost" port: 9876 primary: true ] txViewAcl: [ "group1" ] txViewFullAcl: [] channel2 = new Channel name: "TestChannel2" urlPattern: "test2/sample" allow: [ "PoC", "Test1", "Test2" ] routes: [ name: "test route" host: "localhost" port: 9876 primary: true ] txViewAcl: [ "not-for-non-root" ] txViewFullAcl: [] autoRetryEnabled: true autoRetryPeriodMinutes: 60 autoRetryMaxAttempts: 5 before (done) -> auth.setupTestUsers (err) -> channel.save (err) -> channel2.save (err) -> server.start apiPort: 8080, -> done() after (done) -> auth.cleanupTestUsers (err) -> Channel.remove (err) -> server.stop -> done() beforeEach (done) -> authDetails = auth.getAuthDetails() Event.ensureIndexes done afterEach (done) -> Event.remove {}, done describe "*addTransaction()", -> it "should add a transaction and return status 201 - transaction created", (done) -> transactionData.channelID = channel._id request("https://localhost:8080") .post("/transactions") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .send(transactionData) .expect(201) .end (err, res) -> if err done err else Transaction.findOne { clientID: "999999999999999999999999" }, (error, newTransaction) -> should.not.exist (error) (newTransaction != null).should.be.true newTransaction.status.should.equal "Processing" newTransaction.clientID.toString().should.equal "999999999999999999999999" newTransaction.channelID.toString().should.equal channel._id.toString() newTransaction.request.path.should.equal "/api/test" newTransaction.request.headers['header-title'].should.equal "header1-value" newTransaction.request.headers['another-header'].should.equal "another-header-value" newTransaction.request.querystring.should.equal "param1=value1&param2=value2" newTransaction.request.body.should.equal "<HTTP body request>" newTransaction.request.method.should.equal "POST" done() it "should only allow admin users to add transactions", (done) -> request("https://localhost:8080") .post("/transactions") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .send(transactionData) .expect(403) .end (err, res) -> if err done err else done() it "should generate events after adding a transaction", (done) -> transactionData.channelID = channel._id request("https://localhost:8080") .post("/transactions") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .send(transactionData) .expect(201) .end (err, res) -> return done err if err validateEvents = -> Event.find {}, (err, events) -> return done err if err # expect 8: start+end for primary route, secondary route and orchestration events.length.should.be.exactly 6 for ev in events ev.channelID.toString().should.be.exactly channel._id.toString() evs = (events.map (event) -> "#{event.type}-#{event.name}-#{event.event}") evs.should.containEql "primary-test route-start" evs.should.containEql "primary-test route-end" evs.should.containEql "route-dummy-route-start" evs.should.containEql "route-dummy-route-end" evs.should.containEql "orchestration-dummy-orchestration-start" evs.should.containEql "orchestration-dummy-orchestration-end" done() setTimeout validateEvents, 100 * global.testTimeoutFactor describe "*updateTransaction()", -> s = {} beforeEach (done) -> s = new FakeServer() s.start done afterEach -> s.stop() it "should call /updateTransaction ", (done) -> tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id reqUp = new Object() reqUp.path = "/api/test/updated" reqUp.headers = "Content-Type": "text/javascript" "Access-Control": "authentication-required" reqUp.querystring = 'updated=value' reqUp.body = "<HTTP body update>" reqUp.method = "PUT" updates = request: reqUp status: "Completed" clientID: "777777777777777777777777" $push: { routes : { "name": "async", "orchestrations": [ { "name": "test", "request": { "method": "POST", "body": "data", "timestamp": 1425897647329 }, "response": { "status": 201, "body": "OK", "timestamp": 1425897688016 } } ] } } request("https://localhost:8080") .put("/transactions/#{transactionId}") .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) .end (err, res) -> if err done err else Transaction.findOne { "_id": transactionId }, (error, updatedTrans) -> should.not.exist(error) (updatedTrans != null).should.be.true updatedTrans.status.should.equal "Completed" updatedTrans.clientID.toString().should.equal "777777777777777777777777" updatedTrans.request.path.should.equal "/api/test/updated" updatedTrans.request.headers['Content-Type'].should.equal "text/javascript" updatedTrans.request.headers['Access-Control'].should.equal "authentication-required" updatedTrans.request.querystring.should.equal "updated=value" updatedTrans.request.body.should.equal "<HTTP body update>" updatedTrans.request.method.should.equal "PUT" updatedTrans.routes[1].name.should.equal "async" updatedTrans.routes[1].orchestrations[0].name.should.equal "test" s.expectMessage domain + '.channels.888888888888888888888888.async.orchestrations.test:1|c', -> s.expectMessage domain + '.channels.888888888888888888888888.async.orchestrations.test.statusCodes.201:1|c', done done() it "should only allow admin user to update a transaction", (done) -> tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id updates = {} request("https://localhost:8080") .put("/transactions/#{transactionId}") .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) .end (err, res) -> if err done err else done() it "should generate events on update", (done) -> tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id reqUp = new Object() reqUp.path = "/api/test/updated" reqUp.headers = "Content-Type": "text/javascript" "Access-Control": "authentication-required" reqUp.querystring = 'updated=value' reqUp.body = "<HTTP body update>" reqUp.method = "PUT" updates = status: "Failed" "orchestrations": [ { "name": "test", "request": { "method": "POST", "body": "data", "timestamp": 1425897647329 }, "response": { "status": 500, "body": "OK", "timestamp": 1425897688016 } } ] request("https://localhost:8080") .put("/transactions/#{transactionId}") .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) .end (err, res) -> return done err if err validateEvents = -> Event.find {}, (err, events) -> return done err if err # events should only be generated for the updated fields events.length.should.be.exactly 2 for ev in events ev.channelID.toString().should.be.exactly channel._id.toString() evs = (events.map (event) -> "#{event.type}-#{event.name}-#{event.event}") evs.should.containEql "orchestration-test-start" evs.should.containEql "orchestration-test-end" done() setTimeout validateEvents, 100 * global.testTimeoutFactor it 'should queue a transaction for auto retry', (done) -> transactionData.channelID = channel2._id tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id updates = status: "Failed" error: message: "Error message" stack: "stack\nstack\nstack" request("https://localhost:8080") .put("/transactions/#{transactionId}") .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) .end (err, res) -> return done err if err Transaction.findById transactionId, (err, tx) -> tx.autoRetry.should.be.true() AutoRetry.findOne transactionID: transactionId, (err, queueItem) -> queueItem.should.be.ok() queueItem.channelID.toString().should.be.exactly channel2._id.toString() done() it 'should not queue a transaction for auto retry when max retries have been reached', (done) -> transactionData.autoRetryAttempt = 5 transactionData.channelID = channel2._id tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id updates = status: "Failed" error: message: "Error message" stack: "stack\nstack\nstack" request("https://localhost:8080") .put("/transactions/#{transactionId}") .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) .end (err, res) -> return done err if err Transaction.findById transactionId, (err, tx) -> tx.autoRetry.should.be.false() done() describe "*getTransactions()", -> it "should call getTransactions ", (done) -> Transaction.count {}, (err, countBefore) -> tx = new Transaction transactionData tx.save (error, result) -> should.not.exist (error) request("https://localhost:8080") .get("/transactions?filterPage=0&filterLimit=10&filters={}") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.length.should.equal countBefore + 1 done() it "should call getTransactions with filter parameters ", (done) -> obj = filterPage: 0 filterLimit: 10 filters: 'status': 'Processing' 'request.timestamp': '{"$gte": "2014-06-09T00:00:00.000Z", "$lte": "2014-06-10T00:00:00.000Z" }' 'request.path': '/api/test' 'response.status': '2xx' params = "" for k, v of obj v = JSON.stringify v if params.length > 0 params += "&" params += "#{k}=#{v}" params = encodeURI params Transaction.count {}, (err, countBefore) -> tx = new Transaction transactionData tx.save (error, result) -> should.not.exist (error) request("https://localhost:8080") .get("/transactions?"+params) .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.length.should.equal countBefore + 1 done() it "should call getTransactions with filter parameters (Different filters)", (done) -> obj = filterPage: 0 filterLimit: 10 filters: 'status': 'Processing' 'routes.request.path': '/api/test' 'routes.response.status': '2xx' 'orchestrations.request.path': '/api/test' 'orchestrations.response.status': '2xx' 'properties': 'prop1': 'prop1-value1' params = "" for k, v of obj v = JSON.stringify v if params.length > 0 params += "&" params += "#{k}=#{v}" params = encodeURI params Transaction.count {}, (err, countBefore) -> tx = new Transaction transactionData tx.save (error, result) -> should.not.exist (error) request("https://localhost:8080") .get("/transactions?"+params) .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.length.should.equal countBefore + 1 done() it "should call getTransactions with filter parameters (Different filters - return no results)", (done) -> obj = filterPage: 0 filterLimit: 10 filters: 'status': 'Processing' 'routes.request.path': '/api/test' 'routes.response.status': '2xx' 'orchestrations.request.path': '/api/test' 'orchestrations.response.status': '2xx' 'properties': 'prop3': 'prop3-value3' params = "" for k, v of obj v = JSON.stringify v if params.length > 0 params += "&" params += "#{k}=#{v}" params = encodeURI params Transaction.count {}, (err, countBefore) -> tx = new Transaction transactionData tx.save (error, result) -> should.not.exist (error) request("https://localhost:8080") .get("/transactions?"+params) .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else # prop3 does not exist so no records should be returned res.body.length.should.equal 0 done() it "should only return the transactions that a user can view", (done) -> tx = new Transaction transactionData tx.channelID = channel._id tx.save (err) -> return done err if err tx2 = new Transaction transactionData tx2._id = "111111111111111111111112" tx2.channelID = channel2._id tx2.save (err) -> return done err if err request("https://localhost:8080") .get("/transactions") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> # should NOT retrieve tx2 res.body.should.have.length(1) res.body[0]._id.should.be.equal "111111111111111111111111" done() it "should return the transactions for a channel that a user has permission to view", (done) -> tx = new Transaction transactionData tx.channelID = channel._id tx.save (err) -> return done err if err tx2 = new Transaction transactionData tx2._id = "111111111111111111111112" tx2.channelID = channel2._id tx2.save (err) -> return done err if err request("https://localhost:8080") .get("/transactions?channelID=#{channel._id}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> # should NOT retrieve tx2 res.body.should.have.length(1) res.body[0]._id.should.be.equal "111111111111111111111111" done() it "should return 403 for a channel that a user does NOT have permission to view", (done) -> tx = new Transaction transactionData tx.channelID = channel._id tx.save (err) -> return done err if err tx2 = new Transaction transactionData tx2._id = "111111111111111111111112" tx2.channelID = channel2._id tx2.save (err) -> return done err if err request("https://localhost:8080") .get("/transactions?channelID=#{tx2.channelID}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(403) .end (err, res) -> done() it "should truncate transaction details if filterRepresentation is fulltruncate ", (done) -> Transaction.count {}, (err, countBefore) -> tx = new Transaction transactionData tx.save (error, result) -> should.not.exist (error) request("https://localhost:8080") .get("/transactions?filterRepresentation=fulltruncate") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.length.should.equal countBefore + 1 res.body[countBefore].request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body[countBefore].response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" res.body[countBefore].routes[0].request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body[countBefore].routes[0].response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" res.body[countBefore].orchestrations[0].request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body[countBefore].orchestrations[0].response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" done() describe "*getTransactionById (transactionId)", -> it "should fetch a transaction by ID - admin user", (done) -> tx = new Transaction transactionData tx.save (err, result)-> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .get("/transactions/#{transactionId}") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else (res != null).should.be.true res.body.status.should.equal "Processing" res.body.clientID.toString().should.eql "999999999999999999999999" res.body.request.path.should.equal "/api/test" res.body.request.headers['header-title'].should.equal "header1-value" res.body.request.headers['another-header'].should.equal "another-header-value" res.body.request.querystring.should.equal "param1=value1&param2=value2" res.body.request.body.should.equal "<HTTP body request>" res.body.request.method.should.equal "POST" done() it "should NOT return a transaction that a user is not allowed to view", (done) -> tx = new Transaction transactionData tx.channelID = channel2._id tx.save (err, result)-> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .get("/transactions/#{transactionId}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(403) .end (err, res) -> if err done err else done() it "should return a transaction that a user is allowed to view", (done) -> tx = new Transaction transactionData tx.channelID = channel._id tx.save (err, tx) -> if err return done err should.not.exist(err) transactionId = tx._id request("https://localhost:8080") .get("/transactions/#{transactionId}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else (res != null).should.be.true res.body.status.should.equal "Processing" res.body.clientID.toString().should.eql "999999999999999999999999" res.body.request.path.should.equal "/api/test" res.body.request.headers['header-title'].should.equal "header1-value" res.body.request.headers['another-header'].should.equal "another-header-value" res.body.request.querystring.should.equal "param1=value1&param2=value2" should.not.exist(res.body.request.body) res.body.request.method.should.equal "POST" done() it "should truncate a large body if filterRepresentation is 'fulltruncate'", (done) -> # transactionData body lengths > config.truncateSize tx = new Transaction transactionData tx.save (err, result)-> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .get("/transactions/#{transactionId}?filterRepresentation=fulltruncate") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body.response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" res.body.routes[0].request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body.routes[0].response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" res.body.orchestrations[0].request.body.should.equal "<HTTP body#{apiConf.truncateAppend}" res.body.orchestrations[0].response.body.should.equal "<HTTP resp#{apiConf.truncateAppend}" done() describe "*findTransactionByClientId (clientId)", -> it "should call findTransactionByClientId", (done) -> clientId = "555555555555555555555555" transactionData.clientID = clientId tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) request("https://localhost:8080") .get("/transactions/clients/#{clientId}") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body[0].clientID.should.equal clientId done() it "should NOT return transactions that a user is not allowed to view", (done) -> clientId = "444444444444444444444444" transactionData.clientID = clientId transactionData.channelID = "888888888888888888888888" tx = new Transaction transactionData tx.save (err, result)-> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .get("/transactions/clients/#{clientId}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body.should.have.length(0); done() it "should return transactions that a user is allowed to view", (done) -> clientId = "333333333333333333333333" transactionData.clientID = clientId tx = new Transaction transactionData tx.channelID = channel._id tx.save (err, tx) -> if err return done err should.not.exist(err) transactionId = tx._id request("https://localhost:8080") .get("/transactions/clients/#{clientId}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else res.body[0].clientID.should.equal clientId done() describe "*removeTransaction (transactionId)", -> it "should call removeTransaction", (done) -> transactionData.clientID = "222222222222222222222222" tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .del("/transactions/#{transactionId}") .set("auth-username", testUtils.rootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(200) .end (err, res) -> if err done err else Transaction.findOne { "_id": transactionId }, (err, transDoc) -> should.not.exist(err) (transDoc == null).should.be.true done() it "should only allow admin users to remove transactions", (done) -> transactionData.clientID = "222222222222222222222222" tx = new Transaction transactionData tx.save (err, result) -> should.not.exist(err) transactionId = result._id request("https://localhost:8080") .del("/transactions/#{transactionId}") .set("auth-username", testUtils.nonRootUser.email) .set("auth-ts", authDetails.authTS) .set("auth-salt", authDetails.authSalt) .set("auth-token", authDetails.authToken) .expect(403) .end (err, res) -> if err done err else done()