endo-core
Version:
Put some description here
524 lines (459 loc) • 19.3 kB
text/coffeescript
{afterEach, beforeEach, describe, it} = global
{expect} = require 'chai'
sinon = require 'sinon'
fs = require 'fs'
request = require 'request'
Encryption = require 'meshblu-encryption'
enableDestroy = require 'server-destroy'
shmock = require 'shmock'
MockStrategy = require '../mock-strategy'
Server = require '../..'
moment = require 'moment'
describe 'messages', ->
beforeEach (done) ->
@privateKey = fs.readFileSync "#{__dirname}/../data/private-key.pem", 'utf8'
@encryption = Encryption.fromPem @privateKey
@publicKey = @encryption.key.exportKey 'public'
encrypted =
secrets:
credentials:
secret: 'this is secret'
@encrypted = @encryption.encrypt encrypted
@meshblu = shmock()
enableDestroy @meshblu
@apiStrategy = new MockStrategy name: 'api'
@octobluStrategy = new MockStrategy name: 'octoblu'
@messageHandler = onMessage: sinon.stub()
@meshblu
.get '/v2/whoami'
.set 'Authorization', "Basic cGV0ZXI6aS1jb3VsZC1lYXQ="
.reply 200, {
options:
imageUrl: "http://this-is-an-image.exe"
}
@meshblu
.get '/publickey'
.reply 200, {@publicKey}
serverOptions =
logFn: ->
port: undefined,
disableLogging: true
apiStrategy: @apiStrategy
octobluStrategy: @octobluStrategy
messageHandler: @messageHandler
serviceUrl: 'http://octoblu.xxx'
deviceType: 'endo-endor'
meshbluConfig:
hostname: 'localhost'
protocol: 'http'
port: @meshblu.address().port
uuid: 'peter'
token: 'i-could-eat'
privateKey: @privateKey
appOctobluHost: 'http://app.octoblu.mom'
userDeviceManagerUrl: 'http://manage-my.endo'
meshbluPublicKeyUri: 'http://localhost:53261/publickey'
healthcheckService: healthcheck: =>
@server = new Server serverOptions
@server.run (error) =>
return done error if error?
@serverPort = @server.address().port
done()
afterEach (done) ->
@server.stop done
afterEach (done) ->
@meshblu.destroy done
describe 'On POST /v1/messages', ->
describe 'when authorized', ->
beforeEach ->
@credentialsDeviceAuth = new Buffer('cred-uuid:cred-token').toString 'base64'
@meshblu
.post '/authenticate'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.reply 204
describe 'when we get some weird device instead of a credentials device', ->
beforeEach ->
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [
uuid: 'cred-uuid'
banana: 'pudding'
]
describe 'when called with a valid message', ->
beforeEach (done) ->
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 404', ->
expect(@response.statusCode).to.equal 404, JSON.stringify @body
describe 'when we get an invalid credentials device', ->
beforeEach ->
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{@serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [
uuid: 'cred-uuid'
endoSignature: 'John Hancock. Definitely, definitely John Hancock'
endo:
credentialsDeviceUuid: 'cred-uuid'
encrypted: @encrypted
]
describe 'when called with a valid message', ->
beforeEach (done) ->
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 404', ->
expect(@response.statusCode).to.equal 404, JSON.stringify @body
describe 'when we have a real credentials device', ->
beforeEach ->
serviceAuth = new Buffer('peter:i-could-eat').toString 'base64'
@meshblu
.post '/search/devices'
.set 'Authorization', "Basic #{serviceAuth}"
.set 'x-meshblu-as', 'cred-uuid'
.send uuid: 'cred-uuid'
.reply 200, [{
uuid: 'cred-uuid'
endoSignature: 'LebOB6aPRQJC7HuLqVqwBeZOFITW+S+jTExlXKrnhvcbzgn6b82fwyh0Qin8ccMym9y4ymIWcKunfa9bZj2YsA=='
endo:
authorizedKey: 'some-uuid'
credentialsDeviceUuid: 'cred-uuid'
encrypted: @encrypted
}]
describe 'when called with a message without metadata', ->
beforeEach (done) ->
options =
baseUrl: "http://localhost:#{@serverPort}"
json:
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 422', ->
expect(@response.statusCode).to.equal 422, JSON.stringify(@body)
describe 'when called with a valid message', ->
beforeEach (done) ->
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 200
to: { foo: 'bar' }
data:
whatever: 'this is a response'
.reply 201
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: { foo: 'bar' }
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 201', ->
expect(@response.statusCode).to.equal 201, JSON.stringify @body
it 'should respond to the message via meshblu', ->
@responseHandler.done()
it 'should call the hello messageHandler with the message and auth', ->
expect(@messageHandler.onMessage).to.have.been.calledWith sinon.match {
encrypted:
secrets:
credentials:
secret: 'this is secret'
}, {
metadata:
jobType: 'hello'
data:
greeting: 'hola'
}
describe 'when called with a valid message that was forwarded to the service device', ->
beforeEach (done) ->
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 200
to: { foo: 'bar' }
data:
whatever: 'this is a response'
.reply 201
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "peter", "type": "message.received"}
{"from": "peter", "to": "peter", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: { foo: 'bar' }
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 201', ->
expect(@response.statusCode).to.equal 201, JSON.stringify @body
it 'should respond to the message via meshblu', ->
@responseHandler.done()
it 'should call the hello messageHandler with the message and auth', ->
expect(@messageHandler.onMessage).to.have.been.calledWith sinon.match {
encrypted:
secrets:
credentials:
secret: 'this is secret'
}, {
metadata:
jobType: 'hello'
data:
greeting: 'hola'
}
describe 'when called with message that is a result of a user-uuid\'s subscription to itself', ->
beforeEach (done) ->
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "user-uuid", "type": "message.received"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: { foo: 'bar' }
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 422', ->
expect(@response.statusCode).to.equal 422
describe "when called with a valid message that's the result of some other device's subscription to itself", ->
beforeEach (done) ->
@messageHandler.onMessage.yields null, metadata: {code: 200}, data: {whatever: 'this is a response'}
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['whatever-uuid']
metadata:
code: 200
to: { foo: 'bar' }
data:
whatever: 'this is a response'
.reply 201
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "whatever-uuid", "to": "whatever-uuid", "type": "message.received"}
{"from": "whatever-uuid", "to": "flow-uuid", "type": "message.received"}
{"from": "flow-uuid", "to": "user-uuid", "type": "message.received"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
{"from": "cred-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: { foo: 'bar' }
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should return a 201', ->
expect(@response.statusCode).to.equal 201, JSON.stringify @body
describe 'when called with a valid message, but theres an error', ->
beforeEach 'Go back in time to 8am MST 2016-09-28 ', ->
@clock = sinon.useFakeTimers moment('2016-09-28T15:00:00Z').valueOf()
afterEach 'Back to the future', ->
@clock.restore()
beforeEach (done) ->
@messageHandler.onMessage.yields new Error 'Something very bad happened'
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 500
to: 'food'
error:
message: 'Something very bad happened'
.reply 201
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
respondTo: 'food'
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
@getUserDeviceHandler = @meshblu
.get '/v2/devices/user-uuid'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.reply 200, statusDevice: 'status-device-uuid'
@updateStatusDeviceHandler = @meshblu
.put '/v2/devices/status-device-uuid'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
$push:
'status.errors':
$each: [
code: 500
message: 'Something very bad happened'
senderUuid: 'flow-uuid'
date: '2016-09-28T15:00:00Z'
metadata:
to: 'food'
]
$slice: -99
.reply 204
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should call the onMessage messageHandler with the message and auth', ->
expect(@messageHandler.onMessage).to.have.been.calledWith sinon.match {
encrypted:
secrets:
credentials:
secret: 'this is secret'
}, {
metadata:
jobType: 'hello'
data:
greeting: 'hola'
}
it 'should return a 500', ->
expect(@response.statusCode).to.equal 500, JSON.stringify @body
it 'should respond to the message with the error via meshblu', ->
@responseHandler.done()
it 'should update the status device with the error', ->
@updateStatusDeviceHandler.done()
describe 'when called with a valid message, but the the endo is invalid', ->
beforeEach (done) ->
@messageHandler.onMessage.yields new Error 'Something very bad happened'
@responseHandler = @meshblu
.post '/messages'
.set 'Authorization', "Basic #{@credentialsDeviceAuth}"
.set 'x-meshblu-as', 'user-uuid'
.send
devices: ['flow-uuid']
metadata:
code: 500
error:
message: 'Something very bad happened'
.reply 201
options =
baseUrl: "http://localhost:#{@serverPort}"
headers:
'x-meshblu-route': JSON.stringify [
{"from": "flow-uuid", "to": "user-uuid", "type": "message.sent"}
{"from": "user-uuid", "to": "cred-uuid", "type": "message.received"}
]
json:
metadata:
jobType: 'hello'
data:
greeting: 'hola'
auth:
username: 'cred-uuid'
password: 'cred-token'
request.post '/v1/messages', options, (error, @response, @body) =>
done error
it 'should call the hello messageHandler with the message and auth', ->
expect(@messageHandler.onMessage).to.have.been.calledWith sinon.match {
metadata:
jobType: 'hello'
data:
greeting: 'hola'
encrypted:
secrets:
credentials:
secret: 'this is secret'
}
it 'should return a 500', ->
expect(@response.statusCode).to.equal 500, JSON.stringify @body
it 'should respond to the message with the error via meshblu', ->
@responseHandler.done()