UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

1,645 lines (1,642 loc) 66.7 kB
var Substream, chai, component, helpers, socket; if (typeof process !== 'undefined' && process.execPath && process.execPath.match(/node|iojs/)) { if (!chai) { chai = require('chai'); } helpers = require('../src/lib/Helpers'); component = require('../src/lib/Component'); socket = require('../src/lib/InternalSocket'); Substream = require('../src/lib/Streams').Substream; } else { helpers = require('noflo/src/lib/Helpers'); component = require('noflo/src/lib/Component'); socket = require('noflo/src/lib/InternalSocket'); Substream = require('noflo/src/lib/Streams').Substream; } describe('Component traits', function() { describe('MapComponent', function() { var c; c = null; it('should pass data to the callback', function() { var s; c = new component.Component; c.inPorts.add('in'); c.outPorts.add('out', { required: false }); helpers.MapComponent(c, function(data) { return chai.expect(data).to.equal(1); }); s = new socket.createSocket(); c.inPorts["in"].attach(s); return s.send(1); }); it('should pass groups to the callback', function() { var s; c = new component.Component; c.inPorts.add('in'); c.outPorts.add('out', { required: false }); helpers.MapComponent(c, function(data, groups) { chai.expect(groups).to.eql(['one', 'two']); return chai.expect(data).to.equal(1); }); s = new socket.createSocket(); c.inPorts["in"].attach(s); s.beginGroup('one'); s.beginGroup('two'); return s.send(1); }); return it('should send groups and disconnect through', function(done) { var groups, s, s2; c = new component.Component; c.inPorts.add('in'); c.outPorts.add('out', { required: false }); helpers.MapComponent(c, function(data, groups, out) { return out.send(data * 2); }); s = new socket.createSocket(); c.inPorts["in"].attach(s); s2 = new socket.createSocket(); c.outPorts.out.attach(s2); groups = []; s2.on('begingroup', function(group) { return groups.push(group); }); s2.on('data', function(data) { chai.expect(groups.length).to.equal(2); return chai.expect(data).to.equal(6); }); s2.on('endgroup', function() { return groups.pop(); }); s2.on('disconnect', function() { chai.expect(groups.length).to.equal(0); return done(); }); s.beginGroup('one'); s.beginGroup('two'); s.send(3); s.endGroup(); s.endGroup(); return s.disconnect(); }); }); describe('WirePattern', function() { describe('when grouping by packet groups', function() { var c, p, x, y, z; c = new component.Component; c.inPorts.add('x', { required: true, datatype: 'int' }).add('y', { required: true, datatype: 'int' }).add('z', { required: true, datatype: 'int' }); c.outPorts.add('point'); x = new socket.createSocket(); y = new socket.createSocket(); z = new socket.createSocket(); p = new socket.createSocket(); c.inPorts.x.attach(x); c.inPorts.y.attach(y); c.inPorts.z.attach(z); c.outPorts.point.attach(p); it('should pass data and groups to the callback', function(done) { var count, groups, grp, key, src, _results; src = { 111: { x: 1, y: 2, z: 3 }, 222: { x: 4, y: 5, z: 6 }, 333: { x: 7, y: 8, z: 9 } }; helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point', group: true, forwardGroups: true }, function(data, groups, out) { chai.expect(groups.length).to.be.above(0); chai.expect(data).to.deep.equal(src[groups[0]]); return out.send(data); }); groups = []; count = 0; p.on('begingroup', function(grp) { return groups.push(grp); }); p.on('endgroup', function() { return groups.pop(); }); p.on('data', function(data) { return count++; }); p.on('disconnect', function() { if (count === 3 && groups.length === 0) { return done(); } }); _results = []; for (key in src) { grp = src[key]; x.beginGroup(key); y.beginGroup(key); z.beginGroup(key); x.send(grp.x); y.send(grp.y); z.send(grp.z); x.endGroup(); y.endGroup(); z.endGroup(); x.disconnect(); y.disconnect(); _results.push(z.disconnect()); } return _results; }); it('should work without a group provided', function(done) { p.removeAllListeners(); helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point' }, function(data, groups, out) { chai.expect(groups.length).to.equal(0); return out.send({ x: data.x, y: data.y, z: data.z }); }); p.once('data', function(data) { chai.expect(data).to.deep.equal({ x: 123, y: 456, z: 789 }); return done(); }); x.send(123); x.disconnect(); y.send(456); y.disconnect(); z.send(789); return z.disconnect(); }); it('should process inputs for different groups independently with group: true', function(done) { var groups, inOrder, input, outOrder, src, tuple, _i, _len, _results; src = { 1: { x: 1, y: 2, z: 3 }, 2: { x: 4, y: 5, z: 6 }, 3: { x: 7, y: 8, z: 9 } }; inOrder = [[1, 'x'], [3, 'z'], [2, 'y'], [2, 'x'], [1, 'z'], [2, 'z'], [3, 'x'], [1, 'y'], [3, 'y']]; outOrder = [2, 1, 3]; helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point', group: true, forwardGroups: true }, function(data, groups, out) { return out.send({ x: data.x, y: data.y, z: data.z }); }); groups = []; p.on('begingroup', function(grp) { return groups.push(grp); }); p.on('endgroup', function(grp) { return groups.pop(); }); p.on('data', function(data) { chai.expect(groups.length).to.equal(1); chai.expect(groups[0]).to.equal(outOrder[0]); chai.expect(data).to.deep.equal(src[outOrder[0]]); outOrder.shift(); if (!outOrder.length) { return done(); } }); _results = []; for (_i = 0, _len = inOrder.length; _i < _len; _i++) { tuple = inOrder[_i]; input = null; switch (tuple[1]) { case 'x': input = x; break; case 'y': input = y; break; case 'z': input = z; } input.beginGroup(tuple[0]); input.send(src[tuple[0]][tuple[1]]); input.endGroup(); _results.push(input.disconnect()); } return _results; }); it('should support asynchronous handlers', function(done) { var counter, hadData, point; point = { x: 123, y: 456, z: 789 }; helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point', async: true, group: true, forwardGroups: true }, function(data, groups, out, callback) { return setTimeout(function() { out.send({ x: data.x, y: data.y, z: data.z }); return callback(); }, 100); }); p.removeAllListeners(); counter = 0; hadData = false; p.on('begingroup', function(grp) { return counter++; }); p.on('endgroup', function() { return counter--; }); p.once('data', function(data) { chai.expect(data).to.deep.equal(point); return hadData = true; }); p.once('disconnect', function() { chai.expect(counter).to.equal(0); chai.expect(hadData).to.be["true"]; return done(); }); x.beginGroup('async'); y.beginGroup('async'); z.beginGroup('async'); x.send(point.x); y.send(point.y); z.send(point.z); x.endGroup(); y.endGroup(); z.endGroup(); x.disconnect(); y.disconnect(); return z.disconnect(); }); it('should not forward groups if forwarding is off', function(done) { var counter, hadData, point; point = { x: 123, y: 456 }; helpers.WirePattern(c, { "in": ['x', 'y'], out: 'point' }, function(data, groups, out) { return out.send({ x: data.x, y: data.y }); }); p.removeAllListeners(); counter = 0; hadData = false; p.on('begingroup', function(grp) { return counter++; }); p.on('data', function(data) { chai.expect(data).to.deep.equal(point); return hadData = true; }); p.once('disconnect', function() { chai.expect(counter).to.equal(0); chai.expect(hadData).to.be["true"]; return done(); }); x.beginGroup('doNotForwardMe'); y.beginGroup('doNotForwardMe'); x.send(point.x); y.send(point.y); x.endGroup(); y.endGroup(); x.disconnect(); return y.disconnect(); }); it('should forward groups from a specific port only', function(done) { var groups, point, refGroups; point = { x: 123, y: 456, z: 789 }; refGroups = ['boo']; helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point', forwardGroups: 'y' }, function(data, groups, out) { return out.send({ x: data.x, y: data.y, z: data.z }); }); p.removeAllListeners(); groups = []; p.on('begingroup', function(grp) { return groups.push(grp); }); p.on('data', function(data) { return chai.expect(data).to.deep.equal(point); }); p.once('disconnect', function() { chai.expect(groups).to.deep.equal(refGroups); return done(); }); x.beginGroup('foo'); y.beginGroup('boo'); z.beginGroup('bar'); x.send(point.x); y.send(point.y); z.send(point.z); x.endGroup(); y.endGroup(); z.endGroup(); x.disconnect(); y.disconnect(); return z.disconnect(); }); return it('should forward groups from selected ports only', function(done) { var groups, point, refGroups; point = { x: 123, y: 456, z: 789 }; refGroups = ['foo', 'bar']; helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point', forwardGroups: ['x', 'z'] }, function(data, groups, out) { return out.send({ x: data.x, y: data.y, z: data.z }); }); p.removeAllListeners(); groups = []; p.on('begingroup', function(grp) { return groups.push(grp); }); p.on('data', function(data) { return chai.expect(data).to.deep.equal(point); }); p.once('disconnect', function() { chai.expect(groups).to.deep.equal(refGroups); return done(); }); x.beginGroup('foo'); y.beginGroup('boo'); z.beginGroup('bar'); x.send(point.x); y.send(point.y); z.send(point.z); x.endGroup(); y.endGroup(); z.endGroup(); x.disconnect(); y.disconnect(); return z.disconnect(); }); }); describe('when `this` context is important', function() { var c, p, x, y, z; c = new component.Component; c.inPorts.add('x', { required: true, datatype: 'int' }).add('y', { required: true, datatype: 'int' }).add('z', { required: true, datatype: 'int' }); c.outPorts.add('point'); x = new socket.createSocket(); y = new socket.createSocket(); z = new socket.createSocket(); p = new socket.createSocket(); c.inPorts.x.attach(x); c.inPorts.y.attach(y); c.inPorts.z.attach(z); c.outPorts.point.attach(p); it('should correctly bind component to `this` context', function(done) { p.removeAllListeners(); helpers.WirePattern(c, { "in": ['x', 'y', 'z'], out: 'point' }, function(data, groups, out) { chai.expect(this).to.deep.equal(c); return out.send({ x: data.x, y: data.y, z: data.z }); }); p.once('data', function(data) { chai.expect(data).to.deep.equal({ x: 123, y: 456, z: 789 }); return done(); }); x.send(123); x.disconnect(); y.send(456); y.disconnect(); z.send(789); return z.disconnect(); }); return it('should correctly bind component to `this` context in async mode', function(done) { p.removeAllListeners(); helpers.WirePattern(c, { "in": ['x', 'y', 'z'], async: true, out: 'point' }, function(data, groups, out, callback) { chai.expect(this).to.deep.equal(c); out.send({ x: data.x, y: data.y, z: data.z }); return callback(); }); p.once('data', function(data) { return done(); }); x.send(123); x.disconnect(); y.send(456); y.disconnect(); z.send(789); return z.disconnect(); }); }); describe('when in async mode and packet order matters', function() { var c, delay, load, msg, out; c = new component.Component; c.inPorts.add('delay', { datatype: 'int' }).add('msg', { datatype: 'string' }); c.outPorts.add('out', { datatype: 'object' }).add('load', { datatype: 'int' }); delay = new socket.createSocket(); msg = new socket.createSocket(); out = new socket.createSocket(); load = new socket.createSocket(); c.inPorts.delay.attach(delay); c.inPorts.msg.attach(msg); c.outPorts.out.attach(out); c.outPorts.load.attach(load); it('should preserve input order at the output', function(done) { var expected, idx, ip, sample, _i, _len, _results; helpers.WirePattern(c, { "in": ['delay', 'msg'], async: true, ordered: true, group: false }, function(data, groups, res, callback) { return setTimeout(function() { res.send({ delay: data.delay, msg: data.msg }); return callback(); }, data.delay); }); sample = [ { delay: 30, msg: "one" }, { delay: 0, msg: "two" }, { delay: 20, msg: "three" }, { delay: 10, msg: "four" } ]; out.on('data', function(data) { return chai.expect(data).to.deep.equal(sample.shift()); }); out.on('disconnect', function() { if (sample.length === 0) { return done(); } }); expected = [1, 2, 3, 4, 3, 2, 1, 0]; load.on('data', function(data) { return chai.expect(data).to.equal(expected.shift()); }); idx = 0; _results = []; for (_i = 0, _len = sample.length; _i < _len; _i++) { ip = sample[_i]; delay.beginGroup(idx); delay.send(ip.delay); delay.endGroup(); msg.beginGroup(idx); msg.send(ip.msg); msg.endGroup(); delay.disconnect(); msg.disconnect(); _results.push(idx++); } return _results; }); return it('should support complex substreams', function(done) { var expected, i, sample, _i, _results; out.removeAllListeners(); load.removeAllListeners(); c.cntr = 0; helpers.WirePattern(c, { "in": ['delay', 'msg'], async: true, ordered: true, group: false, receiveStreams: ['delay', 'msg'] }, function(data, groups, res, callback) { var delayData, delayObj, index0, index1, msgData, msgObj, subDelay, subMsg; chai.expect(data.delay instanceof Substream).to.be["true"]; chai.expect(data.msg instanceof Substream).to.be["true"]; delayObj = data.delay.toObject(); msgObj = data.msg.toObject(); index0 = this.cntr.toString(); chai.expect(Object.keys(delayObj)[0]).to.equal(index0); chai.expect(Object.keys(msgObj)[0]).to.equal(index0); subDelay = delayObj[index0]; subMsg = msgObj[index0]; index1 = (10 + this.cntr).toString(); chai.expect(Object.keys(subDelay)[0]).to.equal(index1); chai.expect(Object.keys(subMsg)[0]).to.equal(index1); delayData = subDelay[index1]; msgData = subMsg[index1]; chai.expect(delayData).to.equal(sample[c.cntr].delay); chai.expect(msgData).to.equal(sample[c.cntr].msg); this.cntr++; return setTimeout(function() { var k0, k1, v0, v1; for (k0 in msgObj) { v0 = msgObj[k0]; res.beginGroup(k0); res.send(k0); for (k1 in v0) { v1 = v0[k1]; res.beginGroup(k1); res.send({ delay: delayObj[k0][k1], msg: msgObj[k0][k1] }); res.endGroup(); res.send(k1); } res.endGroup(); } return callback(); }, data.delay); }); sample = [ { delay: 30, msg: "one" }, { delay: 0, msg: "two" }, { delay: 20, msg: "three" }, { delay: 10, msg: "four" } ]; expected = ['0', '0', '10', sample[0], '10', '1', '1', '11', sample[1], '11', '2', '2', '12', sample[2], '12', '3', '3', '13', sample[3], '13']; out.on('begingroup', function(grp) { return chai.expect(grp).to.equal(expected.shift()); }); out.on('data', function(data) { return chai.expect(data).to.deep.equal(expected.shift()); }); out.on('disconnect', function() { if (expected.length === 0) { return done(); } }); _results = []; for (i = _i = 0; _i <= 3; i = ++_i) { delay.beginGroup(i); delay.beginGroup(10 + i); delay.send(sample[i].delay); delay.endGroup(); delay.endGroup(); msg.beginGroup(i); msg.beginGroup(10 + i); msg.send(sample[i].msg); msg.endGroup(); msg.endGroup(); delay.disconnect(); _results.push(msg.disconnect()); } return _results; }); }); describe('when grouping by field', function() { var c, msg, umsg, usr; c = new component.Component; c.inPorts.add('user', { datatype: 'object' }).add('message', { datatype: 'object' }); c.outPorts.add('signedmessage'); usr = new socket.createSocket(); msg = new socket.createSocket(); umsg = new socket.createSocket(); c.inPorts.user.attach(usr); c.inPorts.message.attach(msg); c.outPorts.signedmessage.attach(umsg); return it('should match objects by specific field', function(done) { var counter, mesg, messages, req, user, users, _fn, _results; helpers.WirePattern(c, { "in": ['user', 'message'], out: 'signedmessage', async: true, field: 'request' }, function(data, groups, out, callback) { return setTimeout(function() { out.send({ request: data.request, user: data.user.name, text: data.message.text }); return callback(); }, 10); }); users = { 14: { request: 14, id: 21, name: 'Josh' }, 12: { request: 12, id: 25, name: 'Leo' }, 34: { request: 34, id: 84, name: 'Anica' } }; messages = { 34: { request: 34, id: 234, text: 'Hello world' }, 12: { request: 12, id: 82, text: 'Aloha amigos' }, 14: { request: 14, id: 249, text: 'Node.js ftw' } }; counter = 0; umsg.on('data', function(data) { chai.expect(data).to.be.an('object'); chai.expect(data.request).to.be.ok; chai.expect(data.user).to.equal(users[data.request].name); chai.expect(data.text).to.equal(messages[data.request].text); counter++; if (counter === 3) { return done(); } }); _fn = function(req, user) { return setTimeout(function() { usr.send(user); return usr.disconnect(); }, req); }; for (req in users) { user = users[req]; _fn(req, user); } _results = []; for (req in messages) { mesg = messages[req]; _results.push((function(req, mesg) { return setTimeout(function() { msg.send(mesg); return msg.disconnect(); }, req); })(req, mesg)); } return _results; }); }); describe('when there are multiple output routes', function() { var c, even, num, odd, str; c = new component.Component; c.inPorts.add('num', { datatype: 'int' }).add('str', { datatype: 'string' }); c.outPorts.add('odd', { datatype: 'object' }).add('even', { datatype: 'object' }); num = new socket.createSocket(); str = new socket.createSocket(); odd = new socket.createSocket(); even = new socket.createSocket(); c.inPorts.num.attach(num); c.inPorts.str.attach(str); c.outPorts.odd.attach(odd); c.outPorts.even.attach(even); return it('should send output to one or more of them', function(done) { var dataCounter, grpCounter, i, numbers, _i, _results; numbers = ['cero', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve']; helpers.WirePattern(c, { "in": ['num', 'str'], out: ['odd', 'even'], async: true, ordered: true }, function(data, groups, outs, callback) { return setTimeout(function() { var grp, _i, _j, _k, _l, _len, _len1, _len2, _len3; if (data.num % 2 === 1) { for (_i = 0, _len = groups.length; _i < _len; _i++) { grp = groups[_i]; outs.odd.beginGroup(grp); } outs.odd.send(data); for (_j = 0, _len1 = groups.length; _j < _len1; _j++) { grp = groups[_j]; outs.odd.endGroup(); } } else { for (_k = 0, _len2 = groups.length; _k < _len2; _k++) { grp = groups[_k]; outs.even.beginGroup(grp); } outs.even.send(data); for (_l = 0, _len3 = groups.length; _l < _len3; _l++) { grp = groups[_l]; outs.even.endGroup(); } } return callback(); }, 0); }); grpCounter = 0; dataCounter = 0; odd.on('begingroup', function(grp) { return grpCounter++; }); odd.on('data', function(data) { chai.expect(data.num % 2).to.equal(1); chai.expect(data.str).to.equal(numbers[data.num]); return dataCounter++; }); odd.on('disconnect', function() { if (dataCounter === 10 && grpCounter === 10) { return done(); } }); even.on('begingroup', function(grp) { return grpCounter++; }); even.on('data', function(data) { chai.expect(data.num % 2).to.equal(0); chai.expect(data.str).to.equal(numbers[data.num]); return dataCounter++; }); even.on('disconnect', function() { if (dataCounter === 10 && grpCounter === 10) { return done(); } }); _results = []; for (i = _i = 0; _i < 10; i = ++_i) { num.beginGroup(i); num.send(i); num.endGroup(i); num.disconnect(); str.beginGroup(i); str.send(numbers[i]); str.endGroup(i); _results.push(str.disconnect()); } return _results; }); }); describe('when there are parameter ports', function() { var c, d1, d2, err, out, p1, p2, p3; c = null; p1 = p2 = p3 = d1 = d2 = out = err = 0; beforeEach(function() { c = new component.Component; c.inPorts.add('param1', { datatype: 'string', required: true }).add('param2', { datatype: 'int', required: false }).add('param3', { datatype: 'int', required: true, "default": 0 }).add('data1', { datatype: 'string' }).add('data2', { datatype: 'int' }); c.outPorts.add('out', { datatype: 'object' }).add('error', { datatype: 'object' }); p1 = new socket.createSocket(); p2 = new socket.createSocket(); p3 = new socket.createSocket(); d1 = new socket.createSocket(); d2 = new socket.createSocket(); out = new socket.createSocket(); err = new socket.createSocket(); c.inPorts.param1.attach(p1); c.inPorts.param2.attach(p2); c.inPorts.param3.attach(p3); c.inPorts.data1.attach(d1); c.inPorts.data2.attach(d2); c.outPorts.out.attach(out); return c.outPorts.error.attach(err); }); it('should wait for required params without default value', function(done) { helpers.WirePattern(c, { "in": ['data1', 'data2'], out: 'out', params: ['param1', 'param2', 'param3'] }, function(input, groups, out) { var res; res = { p1: c.params.param1, p2: c.params.param2, p3: c.params.param3, d1: input.data1, d2: input.data2 }; return out.send(res); }); out.once('data', function(data) { chai.expect(data).to.be.an('object'); chai.expect(data.p1).to.equal('req'); chai.expect(data.p2).to.be.undefined; chai.expect(data.p3).to.equal(0); chai.expect(data.d1).to.equal('foo'); chai.expect(data.d2).to.equal(123); return out.once('data', function(data) { chai.expect(data).to.be.an('object'); chai.expect(data.p1).to.equal('req'); chai.expect(data.p2).to.equal(568); chai.expect(data.p3).to.equal(800); chai.expect(data.d1).to.equal('bar'); chai.expect(data.d2).to.equal(456); return done(); }); }); d1.send('foo'); d1.disconnect(); d2.send(123); d2.disconnect(); c.sendDefaults(); p1.send('req'); p1.disconnect(); return setTimeout(function() { p2.send(568); p2.disconnect(); p3.send(800); p3.disconnect(); d1.send('bar'); d1.disconnect(); d2.send(456); return d2.disconnect(); }, 10); }); it('should work for async procs too', function(done) { helpers.WirePattern(c, { "in": ['data1', 'data2'], out: 'out', params: ['param1', 'param2', 'param3'] }, function(input, groups, out) { var delay; delay = c.params.param2 ? c.params.param2 : 10; return setTimeout(function() { var res; res = { p1: c.params.param1, p2: c.params.param2, p3: c.params.param3, d1: input.data1, d2: input.data2 }; return out.send(res); }, delay); }); out.once('data', function(data) { chai.expect(data).to.be.an('object'); chai.expect(data.p1).to.equal('req'); chai.expect(data.p2).to.equal(56); chai.expect(data.p3).to.equal(0); chai.expect(data.d1).to.equal('foo'); chai.expect(data.d2).to.equal(123); return done(); }); p2.send(56); p2.disconnect(); d1.send('foo'); d1.disconnect(); d2.send(123); d2.disconnect(); c.sendDefaults(); p1.send('req'); return p1.disconnect(); }); it('should reset state if shutdown() is called', function(done) { helpers.WirePattern(c, { "in": ['data1', 'data2'], out: 'out', params: ['param1', 'param2', 'param3'] }, function(input, groups, out) { return out.send({ p1: c.params.param1, p2: c.params.param2, p3: c.params.param3, d1: input.data1, d2: input.data2 }); }); d1.send('boo'); d1.disconnect(); p2.send(73); p2.disconnect(); chai.expect(Object.keys(c.groupedData)).to.have.length.above(0); chai.expect(Object.keys(c.params)).to.have.length.above(0); c.shutdown(); chai.expect(c.groupedData).to.deep.equal({}); chai.expect(c.params).to.deep.equal({}); chai.expect(c.taskQ).to.deep.equal([]); return done(); }); return it('should drop premature data if configured to do so', function(done) { helpers.WirePattern(c, { "in": ['data1', 'data2'], out: 'out', params: ['param1', 'param2', 'param3'], dropInput: true }, function(input, groups, out) { var res; res = { p1: c.params.param1, p2: c.params.param2, p3: c.params.param3, d1: input.data1, d2: input.data2 }; return out.send(res); }); out.once('data', function(data) { chai.expect(data).to.be.an('object'); chai.expect(data.p1).to.equal('req'); chai.expect(data.p2).to.equal(568); chai.expect(data.p3).to.equal(800); chai.expect(data.d1).to.equal('bar'); chai.expect(data.d2).to.equal(456); return done(); }); c.sendDefaults(); p2.send(568); p2.disconnect(); p3.send(800); p3.disconnect(); d1.send('foo'); d1.disconnect(); d2.send(123); d2.disconnect(); return setTimeout(function() { p1.send('req'); p1.disconnect(); d1.send('bar'); d1.disconnect(); d2.send(456); return d2.disconnect(); }, 10); }); }); describe('without output ports', function() { var c, foo, sig; c = new component.Component; c.inPorts.add('foo'); foo = socket.createSocket(); sig = socket.createSocket(); c.inPorts.foo.attach(foo); helpers.WirePattern(c, { "in": 'foo', out: [], async: true }, function(foo, grp, out, callback) { return setTimeout(function() { sig.send(foo); return callback(); }, 20); }); return it('should be fine still', function(done) { sig.on('data', function(data) { chai.expect(data).to.equal('foo'); return done(); }); foo.send('foo'); return foo.disconnect(); }); }); describe('when data processing is not possible at the moment', function() { var c, err, line, res, rpt, tryAgain, whn; c = new component.Component; c.inPorts.add('line', { datatype: 'string' }).add('repeat', { datatype: 'int' }).add('when', { datatype: 'string', "default": 'later' }); c.outPorts.add('res', { datatype: 'string' }).add('error', { datatype: 'object' }); line = socket.createSocket(); rpt = socket.createSocket(); whn = socket.createSocket(); res = socket.createSocket(); err = socket.createSocket(); c.inPorts.line.attach(line); c.inPorts.repeat.attach(rpt); c.inPorts.when.attach(whn); c.outPorts.res.attach(res); c.outPorts.error.attach(err); c.invCount = 0; tryAgain = null; helpers.WirePattern(c, { "in": ['line', 'repeat'], params: 'when', out: 'res', async: true }, function(input, groups, out, completed, postpone, resume) { var i, repeated, _i, _ref; this.invCount++; if (this.invCount > 100) { return; } switch (this.params.when) { case 'now': repeated = ''; for (i = _i = 0, _ref = input.repeat; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { repeated += input.line; } out.send(repeated); return completed(); case 'later': return postpone(); case 'afterTimeout': postpone(false); this.params.when = 'now'; return setTimeout(function() { return resume(); }, 10); case 'whenItell': postpone(false); this.params.when = 'now'; return tryAgain = resume; } }); it('should be able to postpone it until next tuple of data', function(done) { res.once('data', function(data) { chai.expect(data).to.equal('opopopopopopopopopop'); chai.expect(c.invCount).to.equal(2); return res.once('data', function(data) { chai.expect(data).to.equal('gogogo'); chai.expect(c.invCount).to.equal(3); return done(); }); }); c.sendDefaults(); line.send('op'); rpt.send(10); line.disconnect(); rpt.disconnect(); whn.send('now'); whn.disconnect(); line.send('go'); rpt.send(3); line.disconnect(); return rpt.disconnect(); }); it('should be able to postpone and retry after timeout', function(done) { c.invCount = 0; res.once('data', function(data) { chai.expect(data).to.equal('dododo'); chai.expect(c.invCount).to.equal(2); return done(); }); whn.send('afterTimeout'); whn.disconnect(); line.send('do'); rpt.send(3); line.disconnect(); return rpt.disconnect(); }); return it('should be able to postpone it and resume when needed', function(done) { c.invCount = 0; res.once('data', function(data) { chai.expect(data).to.equal('yoyo'); chai.expect(c.invCount).to.equal(2); return done(); }); whn.send('whenItell'); whn.disconnect(); line.send('yo'); rpt.send(2); line.disconnect(); rpt.disconnect(); return setTimeout(function() { return tryAgain(); }, 30); }); }); describe('with many inputs and groups', function() { var c, err, ins, msg, out, pth, rep, tkn; c = new component.Component; c.token = null; c.inPorts.add('in', { datatype: 'string' }).add('message', { datatype: 'string' }).add('repository', { datatype: 'string' }).add('path', { datatype: 'string' }).add('token', { datatype: 'string' }, function(event, payload) { if (event === 'data') { return c.token = payload; } }); c.outPorts.add('out', { datatype: 'string' }).add('error', { datatype: 'object' }); helpers.WirePattern(c, { "in": ['in', 'message', 'repository', 'path'], out: 'out', async: true, forwardGroups: true }, function(data, groups, out, callback) { return setTimeout(function() { out.beginGroup(data.path); out.send(data.message); out.endGroup(); return callback(); }, 300); }); ins = socket.createSocket(); msg = socket.createSocket(); rep = socket.createSocket(); pth = socket.createSocket(); tkn = socket.createSocket(); out = socket.createSocket(); err = socket.createSocket(); c.inPorts["in"].attach(ins); c.inPorts.message.attach(msg); c.inPorts.repository.attach(rep); c.inPorts.path.attach(pth); c.inPorts.token.attach(tkn); c.outPorts.out.attach(out); c.outPorts.error.attach(err); return it('should handle mixed flow well', function(done) { var ends, groups, packets, refData, refGroups; groups = []; refGroups = ['foo', 'http://techcrunch.com/2013/03/26/embedly-now/', 'path data']; ends = 0; packets = []; refData = ['message data']; out.on('begingroup', function(grp) { return groups.push(grp); }); out.on('endgroup', function() { return ends++; }); out.on('data', function(data) { return packets.push(data); }); out.on('disconnect', function() { chai.expect(groups).to.deep.equal(refGroups); chai.expect(ends).to.equal(3); chai.expect(packets).to.deep.equal(refData); return done(); }); err.on('data', function(data) { return done(data); }); rep.beginGroup('foo'); rep.beginGroup('http://techcrunch.com/2013/03/26/embedly-now/'); rep.send('repo data'); rep.endGroup(); rep.endGroup(); ins.beginGroup('foo'); ins.beginGroup('http://techcrunch.com/2013/03/26/embedly-now/'); ins.send('ins data'); msg.beginGroup('foo'); msg.beginGroup('http://techcrunch.com/2013/03/26/embedly-now/'); msg.send('message data'); msg.endGroup(); msg.endGroup(); ins.endGroup(); ins.endGroup(); ins.disconnect(); msg.disconnect(); pth.beginGroup('foo'); pth.beginGroup('http://techcrunch.com/2013/03/26/embedly-now/'); pth.send('path data'); pth.endGroup(); pth.endGroup(); pth.disconnect(); return rep.disconnect(); }); }); describe('for batch processing', function() { var addr, addr2sum, cntA, cntB, dblA, dblA2add, dblB, dblB2add, gen2dblA, gen2dblB, genA, genB, newAdder, newDoubler, newGenerator, newSeqsum, sum, sumr; newGenerator = function(name) { var generator; generator = new component.Component; generator.inPorts.add('count', { datatype: 'int' }); generator.outPorts.add('seq', { datatype: 'int' }); return helpers.WirePattern(generator, { "in": 'count', out: 'seq', async: true, forwardGroups: true, ordered: true }, function(count, groups, seq, callback) { var i, sentCount, _i, _results; sentCount = 0; _results = []; for (i = _i = 1; 1 <= count ? _i <= count : _i >= count; i = 1 <= count ? ++_i : --_i) { _results.push((function(i) { var delay; delay = i > 10 ? i % 10 : i; return setTimeout(function() { seq.send(i); sentCount++; if (sentCount === count) { return callback(); } }, delay); })(i)); } return _results; }); }; newDoubler = function(name) { var doubler; doubler = new component.Component; doubler.inPorts.add('num', { datatype: 'int' }); doubler.outPorts.add('out', { datatype: 'int' }); return helpers.WirePattern(doubler, { "in": 'num', out: 'out', forwardGroups: true }, function(num, groups, out) { var dbl; dbl = 2 * num; return out.send(dbl); }); }; newAdder = function() { var adder; adder = new component.Component; adder.inPorts.add('num1', { datatype: 'int' }); adder.inPorts.add('num2', { datatype: 'int' }); adder.outPorts.add('sum', { datatype: 'int' }); return helpers.WirePattern(adder, { "in": ['num1', 'num2'], out: 'sum', forwardGroups: true, async: true, ordered: true }, function(args, groups, out, callback) { var sum; sum = args.num1 + args.num2; return setTimeout(function() { out.send(sum); return callback(); }, sum % 10); }); }; newSeqsum = function() { var seqsum; seqsum = new component.Component; seqsum.sum = 0; seqsum.inPorts.add('seq', { datatype: 'int' }, function(event, payload) { switch (event) { case 'data': return seqsum.sum += payload; case 'disconnect': seqsum.outPorts.sum.send(seqsum.sum); seqsum.sum = 0; return seqsum.outPorts.sum.disconnect(); } }); seqsum.outPorts.add('sum', { datatype: 'int' }); return seqsum; }; genA = newGenerator('A'); genB = newGenerator('B'); dblA = newDoubler('A'); dblB = newDoubler('B'); addr = newAdder(); sumr = newSeqsum(); cntA = socket.createSocket(); cntB = socket.createSocket(); gen2dblA = socket.createSocket(); gen2dblB = socket.createSocket(); dblA2add = socket.createSocket(); dblB2add = socket.createSocket(); addr2sum = socket.createSocket(); sum = socket.createSocket(); genA.inPorts.count.attach(cntA); genB.inPorts.count.attach(cntB); genA.outPorts.seq.attach(gen2dblA); genB.outPorts.seq.attach(gen2dblB); dblA.inPorts.num.attach(gen2dblA); dblB.inPorts.num.attach(gen2dblB); dblA.outPorts.out.attach(dblA2add); dblB.outPorts.out.attach(dblB2add); addr.inPorts.num1.attach(dblA2add); addr.inPorts.num2.attach(dblB2add); addr.outPorts.sum.attach(addr2sum); sumr.inPorts.seq.attach(addr2sum); sumr.outPorts.sum.attach(sum); return it('should process sequences of packets separated by disconnects', function(done) { var actual, expected; expected = [24, 40]; actual = []; sum.on('data', function(data) { return actual.push(data); }); sum.on('disconnect', function() { var act, exp; chai.expect(actual).to.have.length.above(0); chai.expect(expected).to.have.length.above(0); act = actual.shift(); exp = expected.shift(); chai.expect(act).to.equal(exp); if (expected.length === 0) { return done(); } }); cntA.send(3); cntA.disconnect(); cntB.send(3); cntB.disconnect(); cntA.send(4); cntB.send(4); cntA.disconnect(); return cntB.disconnect(); }); }); describe('for batch processing with groups', function() { var c1, c1c2, c2, cnt, out; c1 = new component.Component; c1.inPorts.add('count', { datatype: 'int' }); c1.outPorts.add('seq', { datatype: 'int' }); c2 = new component.Component; c2.inPorts.add('num', { datatype: 'int' }); c2.outPorts.add('out', { datatype: 'int' }); cnt = socket.createSocket(); c1c2 = socket.createSocket(); out = socket.createSocket(); c1.inPorts.count.attach(cnt); c1.outPorts.seq.attach(c1c2); c2.inPorts.num.attach(c1c2); c2.outPorts.out.attach(out); return it('should wrap entire sequence with groups', function(done) { var actual, expected; helpers.WirePattern(c1, { "in": 'count', out: 'seq', async: true, forwardGroups: true }, function(count, groups, out, callback) { var i, _fn, _i; _fn = function(i) { return setTimeout(function() { return out.send(i); }, 0); }; for (i = _i = 0; 0 <= count ? _i < count : _i > count; i = 0 <= count ? ++_i : --_i) { _fn(i); } return setTimeout(function() { return callback(); }, 3); }); helpers.WirePattern(c2, { "in": 'num', out: 'out', forwardGroups: true }, function(num, groups, out) { chai.expect(groups).to.deep.equal(['foo', 'bar']); return out.send(num); }); expected = ['<foo>', '<bar>', 0, 1, 2, '</bar>', '</foo>']; actual = []; out.on('begingroup', function(grp) { return actual.push("<" + grp + ">"); }); out.on('endgroup', function(grp) { return actual.push("</" + grp + ">"); }); out.on('data', function(data) { return actual.push(data); }); out.on('disconnect', function() { chai.expect(actual).to.deep.equal(expected); return done(); }); cnt.beginGroup('foo'); cnt.beginGroup('bar'); cnt.send(3); cnt.endGroup(); cnt.endGroup(); return cnt.disconnect(); }); }); describe('with addressable ports', function() { var c, d11, d12, d13, d2, err, out, p11, p12, p13; c = new component.Component; c.inPorts.add('p1', { datatype: 'int', addressable: true, required: true }).add('d1', { datatype: 'int', addressable: true }).add('d2', { datatype: 'string' }); c.outPorts.add('out', { datatype: 'object' }).add('error', { datatype: 'object' }); p11 = socket.createSocket(); p12 = socket.createSocket(); p13 = socket.createSocket(); d11 = socket.createSocket(); d12 = socket.createSocket(); d13 = socket.createSocket(); d2 = socket.createSocket(); out = socket.createSocket(); err = socket.createSocket(); c.inPorts.p1.attach(p11); c.inPorts.p1.attach(p12); c.inPorts.p1.attach(p13); c.inPorts.d1.attach(d11); c.inPorts.d1.attach(d12); c.inPorts.d1.attach(d13); c.inPorts.d2.attach(d2); c.outPorts.out.attach(out); c.outPorts.error.attach(err); it('should wait for all param and any data port values (default)', function(done) { helpers.WirePattern(c, { "in": ['d1', 'd2'], params: 'p1', out: 'out', arrayPolicy: { "in": 'any', params: 'all' } }, function(input, groups, out) { chai.expect(c.params.p1).to.deep.equal({ 0: 1, 1: 2, 2: 3 }); chai.expect(input.d1).to.deep.equal({ 0: 1 }); chai.expect(input.d2).to.equal('foo'); return done(); }); d2.send('foo'); d2.disconnect(); d11.send(1);