msgflo
Version:
Polyglot FBP runtime based on message queues
133 lines (116 loc) • 4.66 kB
text/coffeescript
chai = require 'chai'
EventEmitter = require('events').EventEmitter
websocket = require 'websocket'
fbp = require 'fbp'
participants = require './fixtures/participants'
Runtime = require('../src/runtime').Runtime
class MockUi extends EventEmitter
constructor: ->
= null
= new websocket.client()
.on 'connect', (connection) =>
= connection
.on 'error', (error) =>
throw error
.on 'close', (error) =>
'disconnected'
.on 'message', (message) =>
message
'connected', connection
connect: (port) ->
.connect "ws://localhost:#{port}/", "noflo"
disconnect: ->
.close() if
'disconnected'
send: (protocol, command, payload) ->
msg =
protocol: protocol
command: command
payload: payload || {}
msg
sendMsg: (msg) ->
.sendUTF JSON.stringify msg
handleMessage: (message) ->
if not message.type == 'utf8'
throw new Error "Received non-UTF8 message: " + message
d = JSON.parse message.utf8Data
'message', d, d.protocol, d.command, d.payload
describe 'FBP runtime protocol', () ->
runtime = null
ui = new MockUi
options =
broker: 'direct://broker111'
port: 3333
host: 'localhost'
before (done) ->
runtime = new Runtime options
runtime.start (err, url) ->
chai.expect(err).to.be.a 'null'
ui.once 'connected', () ->
done()
ui.connect options.port
after (done) ->
ui.once 'disconnected', () ->
runtime.stop () ->
runtime = null
done()
ui.disconnect()
describe 'runtime info', ->
info = null
it 'should be returned on getruntime', (done) ->
ui.send "runtime", "getruntime"
ui.once 'message', (d, protocol, command, payload) ->
info = payload
chai.expect(info).to.be.an 'object'
done()
it 'type should be "msgflo"', ->
chai.expect(info.type).to.equal "msgflo"
it 'protocol version should be "0.4"', ->
chai.expect(info.version).to.be.a "string"
chai.expect(info.version).to.equal "0.4"
describe 'capabilities"', ->
it 'should be an array', ->
chai.expect(info.capabilities).to.be.an "array"
it 'should include "protocol:component"', ->
chai.expect(info.capabilities).to.include "protocol:component"
it 'should include "protocol:graph"', ->
chai.expect(info.capabilities).to.include "protocol:graph"
it 'should include "protocol:network"', ->
chai.expect(info.capabilities).to.include "protocol:network"
it 'should include "component:getsource"', ->
chai.expect(info.capabilities).to.include "component:getsource"
describe 'participant queues already connected', ->
# TODO: move IIP sending into Participant class?
sendGraphIIPs = (part, graph) ->
processIsParticipant = (name) ->
process = graph.processes[name]
return name == part.definition.id
processes = Object.keys(graph.processes).filter processIsParticipant
iips = graph.connections.filter (c) -> return c.data? and c.tgt.process in processes
for iip in iips
part.send iip.tgt.port, iip.data
it 'should show as connected edges', (done) ->
graph = fbp.parse " 'world' -> NAME say(Hello) OUT -> DROP sink(DevNullSink) "
source = participants.Hello options.broker, 'say'
sink = participants.DevNullSink options.broker, 'sink'
source.connectGraphEdges graph
sink.connectGraphEdges graph
sink.start (err) ->
chai.expect(err).to.be.a 'null'
source.start (err) ->
chai.expect(err).to.be.a 'null'
ui.send 'component', 'getsource', { name: 'default/main' }
ui.on 'message', (d, protocol, command, payload) ->
chai.expect(payload).to.be.an 'object'
chai.expect(payload).to.include.keys ['name', 'code', 'language']
chai.expect(payload.language).to.equal 'json'
graph = JSON.parse payload.code
chai.expect(graph).to.include.keys ['connections', 'processes']
chai.expect(graph.connections).to.have.length 1
conn = graph.connections[0]
chai.expect(conn.src.process).to.contain 'say'
chai.expect(conn.src.port).to.equal 'out'
chai.expect(conn.tgt.process).to.contain 'sink'
chai.expect(conn.tgt.port).to.equal 'drop'
done()
# TODO: automatically represent multiple participants of same class as subgraph