summaryrefslogtreecommitdiffstats
path: root/dgbuilder/test/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'dgbuilder/test/nodes')
-rw-r--r--dgbuilder/test/nodes/core/core/20-inject_spec.js118
-rw-r--r--dgbuilder/test/nodes/core/core/58-debug_spec.js298
-rw-r--r--dgbuilder/test/nodes/core/core/80-function_spec.js149
-rw-r--r--dgbuilder/test/nodes/core/core/80-template_spec.js46
-rw-r--r--dgbuilder/test/nodes/core/core/89-delay_spec.js420
-rw-r--r--dgbuilder/test/nodes/core/core/90-comment_spec.js36
-rw-r--r--dgbuilder/test/nodes/core/logic/10-switch_spec.js367
-rw-r--r--dgbuilder/test/nodes/core/logic/15-change_spec.js194
-rw-r--r--dgbuilder/test/nodes/core/logic/16-range_spec.js131
-rw-r--r--dgbuilder/test/nodes/core/parsers/70-HTML_spec.js211
-rw-r--r--dgbuilder/test/nodes/core/parsers/70-JSON_spec.js104
-rw-r--r--dgbuilder/test/nodes/core/parsers/70-XML_spec.js107
-rw-r--r--dgbuilder/test/nodes/core/storage/28-tail_spec.js165
-rw-r--r--dgbuilder/test/nodes/helper.js128
14 files changed, 2474 insertions, 0 deletions
diff --git a/dgbuilder/test/nodes/core/core/20-inject_spec.js b/dgbuilder/test/nodes/core/core/20-inject_spec.js
new file mode 100644
index 00000000..aeeac9dd
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/20-inject_spec.js
@@ -0,0 +1,118 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var injectNode = require("../../../../nodes/core/core/20-inject.js");
+var helper = require("../../helper.js");
+
+describe('inject node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should inject once', function(done) {
+
+ helper.load(injectNode, [{id:"n1", type:"inject",
+ payload:"payload", topic: "t1",
+ once: true, wires:[["n2"]] },
+ {id:"n2", type:"helper"}],
+ function() {
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 't1');
+ msg.should.have.property('payload', 'payload');
+ done();
+ });
+ });
+ });
+
+ it('should inject repeatedly', function(done) {
+
+ helper.load(injectNode, [{id:"n1", type:"inject",
+ payload:"payload", topic: "t2",
+ repeat: 0.2, wires:[["n2"]] },
+ {id:"n2", type:"helper"}],
+ function() {
+ var n2 = helper.getNode("n2");
+ var count = 0;
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 't2');
+ msg.should.have.property('payload', 'payload');
+ count += 1;
+ if (count > 2) {
+ helper.clearFlows().then(function() {
+ done();
+ });
+ }
+ });
+ });
+ });
+
+ it('should inject with cron', function(done) {
+ helper.load(injectNode, [{id:"n1", type:"inject",
+ payloadType:"date", topic: "t3",
+ crontab: "* * * * * *", wires:[["n3"]] },
+ {id:"n3", type:"helper"}],
+ function() {
+ var n3 = helper.getNode("n3");
+ n3.on("input", function(msg) {
+ msg.should.have.property('topic', 't3');
+ msg.should.have.property('payload').be.a.Number;
+ helper.clearFlows().then(function() {
+ done();
+ });
+ });
+ });
+ });
+
+ describe('post', function() {
+ it('should inject message', function(done) {
+ helper.load(injectNode,
+ [{id:"n1", type:"inject",
+ payloadType:"some type", topic: "t4",
+ wires:[["n4"]] },
+ { id:"n4", type:"helper"}], function() {
+ var n4 = helper.getNode("n4");
+ n4.on("input", function(msg) {
+ msg.should.have.property('topic', 't4');
+ msg.should.have.property('payload', '');
+ helper.clearFlows().then(function() {
+ done();
+ });
+ });
+ helper.request()
+ .post('/inject/n1')
+ .expect(200).end(function(err) {
+ if (err) {
+ return helper.clearFlows()
+ .then(function () {
+ done(err);
+ });
+ }
+ });
+ });
+ });
+
+ it('should fail for invalid node', function(done) {
+ helper.request().post('/inject/invalid').expect(404).end(done);
+ });
+ });
+});
diff --git a/dgbuilder/test/nodes/core/core/58-debug_spec.js b/dgbuilder/test/nodes/core/core/58-debug_spec.js
new file mode 100644
index 00000000..f83fc2ca
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/58-debug_spec.js
@@ -0,0 +1,298 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var debugNode = require("../../../../nodes/core/core/58-debug.js");
+var helper = require("../../helper.js");
+var WebSocket = require('ws');
+
+describe('debug node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"n1", type:"debug", name: "Debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ n1.should.have.property('name', 'Debug');
+ done();
+ });
+ });
+
+ it('should publish on input', function(done) {
+ var flow = [{id:"n1", type:"debug", name: "Debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload:"test"});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",name:"Debug",msg:"test"}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish to console', function(done) {
+ var flow = [{id:"n1", type:"debug", console: "true" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var count = 0;
+ n1.on('log', function(msg) {
+ msg.should.eql({level:'log',id:'n1',type:'debug',msg:'test'});
+ count++;
+ if (count == 2) {
+ done();
+ }
+ });
+ websocket_test(function() {
+ n1.emit("input", {payload:"test"});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",msg:"test"}
+ });
+ count++;
+ }, function() {
+ if (count == 2) {
+ done();
+ }
+ });
+ });
+ });
+
+ it('should publish complete message', function(done) {
+ var flow = [{id:"n1", type:"debug", complete: "true" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload:"test"});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{id:"n1",msg:'(Object) {\n "payload": "test"\n}'}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish an Error', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: new Error("oops")});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",msg:"Error: oops"}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish a boolean', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: true});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",msg: '(boolean) true'}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish with no payload', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",msg: '(undefined)'}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish an object', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: {type:'foo'}});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{id:"n1",msg:'(Object) {\n "type": "foo"\n}'}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish an array', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: [0,1,2,3]});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{id:"n1",msg: '(Array) [\n 0,\n 1,\n 2,\n 3\n]'}
+ });
+ }, done);
+ });
+ });
+
+ it('should publish an object with circular references', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ var o = { name: 'bar' };
+ o.o = o;
+ n1.emit("input", {payload: o});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{
+ id:"n1",
+ msg:'(Object) {\n "name": "bar",\n "o": "[circular]"\n}'
+ }
+ });
+ }, done);
+ });
+ });
+
+ it('should truncated a long message', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: Array(1002).join("X")});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{
+ id:"n1",
+ msg: Array(1001).join("X")+' ....'
+ }
+ });
+ }, done);
+ });
+ });
+
+ it('should convert Buffer to hex', function(done) {
+ var flow = [{id:"n1", type:"debug" }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload: new Buffer('HELLO', 'utf8')});
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",
+ data:{
+ id:"n1",
+ msg: '(Buffer) 48454c4c4f',
+ }
+ });
+ }, done);
+ });
+ });
+
+ it('should publish when active', function(done) {
+ var flow = [{id:"n1", type:"debug", active: false }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function() {
+ n1.emit("input", {payload:"message 1"});
+ helper.request()
+ .post('/debug/n1/enable')
+ .expect(200).end(function(err) {
+ if (err) { return done(err); }
+ n1.emit("input", {payload:"message 2"});
+ });
+ }, function(msg) {
+ JSON.parse(msg).should.eql({
+ topic:"debug",data:{id:"n1",msg:"message 2"}
+ });
+ }, done);
+ });
+ });
+
+ it('should not publish when inactive', function(done) {
+ var flow = [{id:"n1", type:"debug", active: true }];
+ helper.load(debugNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ websocket_test(function(close) {
+ helper.request()
+ .post('/debug/n1/disable')
+ .expect(201).end(function(err) {
+ if (err) {
+ close();
+ return done(err);
+ }
+ n1.emit("input", {payload:"message"});
+ setTimeout(function() {
+ close();
+ done();
+ }, 200);
+ });
+ }, function(msg) {
+ should.fail(null,null,"unexpected message");
+ }, function() {});
+ });
+ });
+
+ describe('post', function() {
+ it('should return 404 on invalid state', function(done) {
+ var flow = [{id:"n1", type:"debug", active: true }];
+ helper.load(debugNode, flow, function() {
+ helper.request()
+ .post('/debug/n1/foobar')
+ .expect(404).end(done);
+ });
+ });
+
+ it('should return 404 on invalid node', function(done) {
+ helper.request()
+ .post('/debug/n99/enable')
+ .expect(404).end(done);
+ });
+ });
+
+});
+
+function websocket_test(open_callback, message_callback, done_callback) {
+ var ws = new WebSocket(helper.url() + "/comms");
+ var close_callback = function() { ws.close(); };
+ ws.on('open', function() { open_callback(close_callback); });
+ ws.on('message', function(msg) {
+ message_callback(msg, close_callback);
+ ws.close();
+ done_callback();
+ });
+}
diff --git a/dgbuilder/test/nodes/core/core/80-function_spec.js b/dgbuilder/test/nodes/core/core/80-function_spec.js
new file mode 100644
index 00000000..9efd93aa
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/80-function_spec.js
@@ -0,0 +1,149 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var functionNode = require("../../../../nodes/core/core/80-function.js");
+var helper = require("../../helper.js");
+
+describe('function node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"n1", type:"function", name: "function" }];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ n1.should.have.property('name', 'function');
+ done();
+ });
+ });
+
+ it('should send returned message', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.should.have.property('payload', 'foo');
+ done();
+ });
+ n1.receive({payload:"foo",topic: "bar"});
+ });
+ });
+
+ it('should pass through _topic', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.should.have.property('payload', 'foo');
+ msg.should.have.property('_topic', 'baz');
+ done();
+ });
+ n1.receive({payload:"foo",topic: "bar", _topic: "baz"});
+ });
+ });
+
+ it('should send to multiple outputs', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"],["n3"]],
+ func:"return [{payload: '1'},{payload: '2'}];"},
+ {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ var n3 = helper.getNode("n3");
+ var count = 0;
+ n2.on("input", function(msg) {
+ should(msg).have.property('payload', '1');
+ count++;
+ if (count == 2) {
+ done();
+ }
+ });
+ n3.on("input", function(msg) {
+ should(msg).have.property('payload', '2');
+ count++;
+ if (count == 2) {
+ done();
+ }
+ });
+ n1.receive({payload:"foo",topic: "bar"});
+ });
+ });
+
+ it('should send to multiple messages', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],
+ func:"return [[{payload: 1},{payload: 2}]];"},
+ {id:"n2", type:"helper"} ];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ var count = 0;
+ n2.on("input", function(msg) {
+ count++;
+ should(msg).have.property('payload', count);
+ should(msg).have.property('_topic', 'baz');
+ if (count == 2) {
+ done();
+ }
+ });
+ n1.receive({payload:"foo", topic: "bar", _topic:"baz"});
+ });
+ });
+
+ it('should allow input to be discarded by returning null', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return null"},
+ {id:"n2", type:"helper"}];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ setTimeout(function() {
+ done();
+ }, 200);
+ n2.on("input", function(msg) {
+ should.fail(null,null,"unexpected message");
+ });
+ n1.receive({payload:"foo",topic: "bar"});
+ });
+ });
+
+ it('should handle and log script error', function(done) {
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"retunr"}];
+ helper.load(functionNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ n1.on("log", function(msg) {
+ msg.should.have.property('level', 'error');
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'ReferenceError: retunr is not defined');
+ done();
+ });
+ n1.receive({payload:"foo",topic: "bar"});
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/core/80-template_spec.js b/dgbuilder/test/nodes/core/core/80-template_spec.js
new file mode 100644
index 00000000..a89afa37
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/80-template_spec.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var templateNode = require("../../../../nodes/core/core/80-template.js");
+var helper = require("../../helper.js");
+
+describe('template node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+
+ it('should modify payload', function(done) {
+ var flow = [{id:"n1", type:"template", field: "payload", template: "payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}];
+ helper.load(templateNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.should.have.property('payload', 'payload=foo');
+ done();
+ });
+ n1.receive({payload:"foo",topic: "bar"});
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/core/89-delay_spec.js b/dgbuilder/test/nodes/core/core/89-delay_spec.js
new file mode 100644
index 00000000..22c3173a
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/89-delay_spec.js
@@ -0,0 +1,420 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+
+var delayNode = require("../../../../nodes/core/core/89-delay.js");
+var helper = require("../../helper.js");
+
+var GRACE_PERCENTAGE=10;
+
+var nanosToSeconds = 1000000000;
+var millisToSeconds = 1000;
+
+var secondsToMinutes = 60;
+var secondsToHours = 3600;
+var secondsToDays = 86400;
+
+
+describe('delayNode', function() {
+
+ beforeEach(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function(done) {
+ helper.unload();
+ helper.stopServer(done);
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ delayNode1.should.have.property('name', 'delayNode');
+ done();
+ });
+ });
+
+ var TimeUnitEnum = {
+ MILLIS : "milliseconds",
+ SECONDS : "seconds",
+ MINUTES : "minutes",
+ HOURS : "hours",
+ DAYS : "days"
+ }
+
+ /**
+ * Tells whether two numeric values are close enough to each other
+ * @param actualValue - the value we're testing
+ * @param expectedValue - the value we're matching the test value against
+ * @param tolerancePercent - the percentage of tolerated deviation (0 means equals)
+ */
+ function closeEnough(actualValue, expectedValue, tolerancePercent) {
+ var toReturn;
+ var toleranceFraction = expectedValue * (tolerancePercent/100);
+ var minExpected = expectedValue - toleranceFraction;
+ var maxExpected = expectedValue + toleranceFraction;
+
+ if(actualValue >= minExpected && actualValue <= maxExpected) {
+ toReturn = true;
+ } else {
+ toReturn = false;
+ }
+ return toReturn;
+ }
+
+ /**
+ * Runs a delay test
+ * @param aTimeout - the timeout quantity
+ * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days
+ */
+ function genericDelayTest(aTimeout, aTimeoutUnit, done) {
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":aTimeout,"timeoutUnits":aTimeoutUnit,"rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ var endTime = process.hrtime(startTime);
+ var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] );
+ var runtimeSeconds = runtimeNanos / nanosToSeconds;
+ var aTimeoutUnifiedToSeconds;
+
+ // calculating the timeout in seconds
+ if(aTimeoutUnit == TimeUnitEnum.MILLIS) {
+ aTimeoutUnifiedToSeconds = aTimeout / millisToSeconds;
+ } else if(aTimeoutUnit == TimeUnitEnum.SECONDS) {
+ aTimeoutUnifiedToSeconds = aTimeout;
+ } else if(aTimeoutUnit == TimeUnitEnum.MINUTES) {
+ aTimeoutUnifiedToSeconds = aTimeout * secondsToMinutes;
+ } else if(aTimeoutUnit == TimeUnitEnum.HOURS) {
+ aTimeoutUnifiedToSeconds = aTimeout * secondsToHours;
+ } else if(aTimeoutUnit == TimeUnitEnum.DAYS) {
+ aTimeoutUnifiedToSeconds = aTimeout * secondsToDays;
+ }
+
+ if(closeEnough(runtimeSeconds, aTimeoutUnifiedToSeconds, GRACE_PERCENTAGE)) {
+ done();
+ } else {
+ try {
+ should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not close enough to exlected timeout seconds: " + aTimeoutUnifiedToSeconds);
+ } catch (err) {
+ done(err);
+ }
+ }
+ } catch(err) {
+ done(err);
+ }
+ });
+ var startTime = process.hrtime();
+ delayNode1.receive({payload:"delayMe"});
+ });
+ }
+
+ /**
+ * We send a message, take a timestamp then when the message is received by the helper node, we take another timestamp.
+ * Then check if the message has been delayed by the expected amount.
+ */
+ it('delays the message in seconds', function(done) {
+ genericDelayTest(0.5, "seconds", done);
+ });
+
+ it('delays the message in milliseconds', function(done) {
+ genericDelayTest(500, "milliseconds", done);
+ });
+
+ it('delays the message in minutes', function(done) { // this is also 0.5 seconds
+ genericDelayTest(0.00833, "minutes", done);
+ });
+
+ it('delays the message in hours', function(done) { // this is also 0.5 seconds
+ genericDelayTest(0.0001388, "hours", done);
+ });
+
+ it('delays the message in days', function(done) { // this is also 0.5 seconds
+ genericDelayTest(0.000005787, "days", done);
+ });
+
+ /**
+ * Runs a rate limit test - only testing seconds!
+ * @param aLimit - the message limit count
+ * @param runtimeInMillis - when to terminate run and count messages received
+ */
+ function genericRateLimitSECONDSTest(aLimit, runtimeInMillis, done) {
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var receivedMessagesStack = [];
+ var rate = 1000/aLimit;
+
+ var receiveTimestamp;
+
+ helperNode1.on("input", function(msg) {
+ if(receiveTimestamp) {
+ var elapse = process.hrtime(receiveTimestamp);
+ var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000);
+ receiveInterval.should.be.above(rate * 0.9);
+ }
+ receiveTimestamp = process.hrtime();
+ receivedMessagesStack.push(msg);
+ });
+
+ var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst
+
+ var i = 0;
+ for(; i < possibleMaxMessageCount + 1; i++) {
+ delayNode1.receive({payload:i});
+ }
+
+ setTimeout(function() {
+ try {
+ receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount);
+ for(var j = 0; j < receivedMessagesStack.length; j++) {
+ if(receivedMessagesStack[j].payload === j) {
+ if(j === (receivedMessagesStack.length -1)) { // last message, all matched so far
+ done();
+ }
+ } else {
+ should.fail(null, null, "Received messages were not received in order. Message was " + receivedMessagesStack[i].payload + " on count " + i);
+ }
+ }
+ } catch (err) {
+ done(err);
+ }
+ }, runtimeInMillis);
+ });
+ }
+
+ it('limits the message rate to 1 per second', function(done) {
+ genericRateLimitSECONDSTest(1, 1500, done);
+ });
+
+ it('limits the message rate to 2 per second, 2 seconds', function(done) {
+ this.timeout(6000);
+ genericRateLimitSECONDSTest(2, 2100, done);
+ });
+
+ /**
+ * Runs a rate limit test with drop support - only testing seconds!
+ * @param aLimit - the message limit count
+ * @param runtimeInMillis - when to terminate run and count messages received
+ */
+ function dropRateLimitSECONDSTest(aLimit, runtimeInMillis, done) {
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var receivedMessagesStack = [];
+
+ var rate = 1000/aLimit;
+
+ var receiveTimestamp;
+
+ helperNode1.on("input", function(msg) {
+ if(receiveTimestamp) {
+ var elapse = process.hrtime(receiveTimestamp);
+ var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000);
+ receiveInterval.should.be.above(rate * 0.9);
+ }
+ receiveTimestamp = process.hrtime();
+ receivedMessagesStack.push(msg);
+ });
+
+ var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst
+
+ var i = 0;
+ delayNode1.receive({payload:i});
+ i++;
+ for(; i < possibleMaxMessageCount + 1; i++) {
+ setTimeout(function() {
+ delayNode1.receive({payload:i});
+ }, 2 * ((rate * i) / possibleMaxMessageCount) );
+ }
+
+ //we need to send a message delayed so that it doesn't get dropped
+ setTimeout(function() {
+ delayNode1.receive({payload:++i});
+ }, runtimeInMillis - 300); // should give enough time to squeeze another message in
+
+ setTimeout(function() {
+ try {
+ receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount + 1);
+ receivedMessagesStack.length.should.be.greaterThan(2); // ensure that we receive more than 1st and last message
+ receivedMessagesStack[0].payload.should.be.exactly(0); // means we received the last message injected just before test termination
+ var foundAtLeastOneDrop = false;
+ for(var i = 0; i < receivedMessagesStack.length; i++) {
+ if(i > 0) {
+ if(receivedMessagesStack[i].payload - receivedMessagesStack[i - 1].payload > 1) {
+ foundAtLeastOneDrop = true;
+ }
+ }
+ }
+ foundAtLeastOneDrop.should.be.true;
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }, runtimeInMillis);
+ });
+ }
+
+ it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) {
+ this.timeout(6000);
+ dropRateLimitSECONDSTest(1, 4000, done);
+ });
+
+ it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) {
+ this.timeout(6000);
+ dropRateLimitSECONDSTest(2, 5000, done);
+ });
+
+ /**
+ * Returns true if the actualTimeout is gracefully in between the timeoutFrom and timeoutTo
+ * values. Gracefully means that inBetween could actually mean smaller/greater values
+ * than the timeout range so long as it's within an actual grace percentage.
+ * @param timeoutFrom - The expected timeout range (low number)
+ * @param timeoutTo - The expected timeout range (high number)
+ * @param actualTimeout - The actual measured timeout value of test
+ * @param allowedGracePercent - The percentage of grace allowed
+ */
+ function inBetweenDelays(timeoutFrom, timeoutTo, actualTimeout, allowedGracePercent) {
+ if(closeEnough(actualTimeout, timeoutFrom, allowedGracePercent)) {
+ return true;
+ } else if(closeEnough(actualTimeout, timeoutTo, allowedGracePercent)) {
+ return true;
+ } else if(timeoutFrom < actualTimeout && timeoutTo > actualTimeout) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Runs a RANDOM DELAY test, checks if the delay is in between the given timeout values
+ * @param aTimeoutFrom - the timeout quantity which is the minimal acceptable wait period
+ * @param aTimeoutTo - the timeout quantity which is the maximum acceptable wait period
+ * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days
+ */
+ function randomDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, done) {
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"random","timeout":5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ var endTime = process.hrtime(startTime);
+ var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] );
+ var runtimeSeconds = runtimeNanos / nanosToSeconds;
+ var aTimeoutFromUnifiedToSeconds;
+ var aTimeoutToUnifiedToSeconds;
+
+ // calculating the timeout in seconds
+ if(aTimeoutUnit == TimeUnitEnum.MILLIS) {
+ aTimeoutFromUnifiedToSeconds = aTimeoutFrom / millisToSeconds;
+ aTimeoutToUnifiedToSeconds = aTimeoutTo / millisToSeconds;
+ } else if(aTimeoutUnit == TimeUnitEnum.SECONDS) {
+ aTimeoutFromUnifiedToSeconds = aTimeoutFrom;
+ aTimeoutToUnifiedToSeconds = aTimeoutTo;
+ } else if(aTimeoutUnit == TimeUnitEnum.MINUTES) {
+ aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToMinutes;
+ aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToMinutes;
+ } else if(aTimeoutUnit == TimeUnitEnum.HOURS) {
+ aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToHours;
+ aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToHours;
+ } else if(aTimeoutUnit == TimeUnitEnum.DAYS) {
+ aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToDays;
+ aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToDays;
+ }
+
+ if(inBetweenDelays(aTimeoutFromUnifiedToSeconds, aTimeoutToUnifiedToSeconds, runtimeSeconds, GRACE_PERCENTAGE)) {
+ done();
+ } else {
+ try {
+ should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not \"in between enough\" enough to expected values of: " + aTimeoutFromUnifiedToSeconds + " and " + aTimeoutToUnifiedToSeconds);
+ } catch (err) {
+ done(err);
+ }
+ }
+ } catch(err) {
+ done(err);
+ }
+ });
+ var startTime = process.hrtime();
+ delayNode1.receive({payload:"delayMe"});
+ });
+ }
+
+ it('randomly delays the message in seconds', function(done) {
+ randomDelayTest(0.4, 0.8, "seconds", done);
+ });
+
+ it(' randomly delays the message in milliseconds', function(done) {
+ randomDelayTest(400, 800, "milliseconds", done);
+ });
+
+ it('randomly delays the message in minutes', function(done) {
+ randomDelayTest(0.0066, 0.0133, "minutes", done);
+ });
+
+ it('delays the message in hours', function(done) {
+ randomDelayTest(0.000111111, 0.000222222, "hours", done);
+ });
+
+ it('delays the message in days', function(done) {
+ randomDelayTest(0.0000046296, 0.0000092593, "days", done);
+ });
+
+ it('handles bursts using a buffer', function(done) {
+ this.timeout(6000);
+
+ var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":1000,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(delayNode, flow, function() {
+ var delayNode1 = helper.getNode("delayNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+
+ var sinon = require('sinon');
+
+ var receivedWarning = false;
+ var messageBurstSize = 1500;
+
+ // we ensure that we note that a warning is received for buffer growth
+ sinon.stub(delayNode1, 'warn', function(warning){
+ if(warning.indexOf("buffer exceeded 1000 messages" > -1)) {
+ receivedWarning = true;
+ }
+ });
+
+ // we ensure that the warning is received for buffer size and that we get the last message
+ helperNode1.on("input", function(msg) {
+ if(msg.payload === (messageBurstSize - 1) && receivedWarning === true) {
+ done(); // it will timeout if we don't receive the last message
+ }
+ });
+ // send 1500 messages as quickly as possible
+ for(var i = 0; i < messageBurstSize; i++) {
+ delayNode1.receive({payload:i});
+ }
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/core/90-comment_spec.js b/dgbuilder/test/nodes/core/core/90-comment_spec.js
new file mode 100644
index 00000000..54563e9e
--- /dev/null
+++ b/dgbuilder/test/nodes/core/core/90-comment_spec.js
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var commentNode = require("../../../../nodes/core/core/90-comment.js");
+var helper = require("../../helper.js");
+
+describe('comment node', function() {
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"n1", type:"comment", name: "comment" }];
+ helper.load(commentNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ n1.should.have.property('name', 'comment');
+ done();
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/logic/10-switch_spec.js b/dgbuilder/test/nodes/core/logic/10-switch_spec.js
new file mode 100644
index 00000000..d5a42805
--- /dev/null
+++ b/dgbuilder/test/nodes/core/logic/10-switch_spec.js
@@ -0,0 +1,367 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+
+var switchNode = require("../../../../nodes/core/logic/10-switch.js");
+var helper = require("../../helper.js");
+
+describe('SwitchNode', function() {
+
+ beforeEach(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function(done) {
+ helper.unload();
+ helper.stopServer(done);
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{"id":"switchNode1","type":"switch","name":"switchNode","property":"payload","rules":[{"t":"eq","v":""}],"checkall":"true","outputs":1,"wires":[[]]}];
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ switchNode1.should.have.property('name', 'switchNode');
+ done();
+ });
+ });
+
+ /**
+ * Test a switch node where one argument is consumed by the rule (such as greater than).
+ * @param rule - the switch rule (see 10-switc.js) string we're using
+ * @param ruleWith - whatever the rule should be executed with (say greater than 5)
+ * @param aCheckall - whether the switch flow should have the checkall flag set to true/false
+ * @param shouldReceive - whether the helper node should receive a payload
+ * @param sendPayload - the payload message we're sending
+ * @param done - callback when done
+ */
+ function genericSwitchTest(rule, ruleWith, aCheckall, shouldReceive, sendPayload, done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ customFlowSwitchTest(flow, shouldReceive, sendPayload, done);
+ }
+
+ /**
+ * Test a switch node where NO arguments are consumed by the rule (such as TRUE/FALSE)
+ * @param rule - the switch rule (see 10-switc.js) string we're using
+ * @param aCheckall - whether the switch flow should have the checkall flag set to true/false
+ * @param shouldReceive - whether the helper node should receive a payload
+ * @param sendPayload - the payload message we're sending
+ * @param done - callback when done
+ */
+ function singularSwitchTest(rule, aCheckall, shouldReceive, sendPayload, done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ customFlowSwitchTest(flow, shouldReceive, sendPayload, done);
+ }
+
+ /**
+ * Test a switch node where two arguments are consumed by the rule (such as between).
+ * @param rule - the switch rule (see 10-switc.js) string we're using
+ * @param ruleWith - whatever the rule should be executed with (say between 5...)
+ * @param ruleWith2 - whatever the rule should be executed with (say ...and 5)
+ * @param aCheckall - whether the switch flow should have the checkall flag set to true/false
+ * @param shouldReceive - whether the helper node should receive a payload
+ * @param sendPayload - the payload message we're sending
+ * @param done - callback when done
+ */
+ function twoFieldSwitchTest(rule, ruleWith, ruleWith2, aCheckall, shouldReceive, sendPayload, done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith,"v2":ruleWith2}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ customFlowSwitchTest(flow, shouldReceive, sendPayload, done);
+ }
+
+ /**
+ * Execute a switch test. Can specify whether the should node is expected to send a payload onwards to the helper node.
+ * The flow and the payload can be customised
+ * @param flow - the custom flow to be tested => must contain a switch node (switchNode1) wiring a helper node (helperNode1)
+ * @param shouldReceive - whether the helper node should receive a payload
+ * @param sendPayload - the payload message we're sending
+ * @param done - callback when done
+ */
+ function customFlowSwitchTest(flow, shouldReceive, sendPayload, done) {
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ if(shouldReceive === true) {
+ msg.payload.should.equal(sendPayload);
+ done();
+ } else {
+ should.fail(null, null, "We should never get an input!");
+ }
+ } catch(err) {
+ done(err);
+ }
+ });
+ switchNode1.receive({payload:sendPayload});
+ if(shouldReceive === false) {
+ setTimeout(function() {
+ done();
+ }, 200);
+ }
+ });
+ }
+
+ it('should check if payload equals given value', function(done) {
+ genericSwitchTest("eq", "Hello", true, true, "Hello", done);
+ });
+
+ it('should return nothing when the payload doesn\'t equal to desired string', function(done) {
+ genericSwitchTest("eq", "Hello", true, false, "Hello!", done);
+ });
+
+ it('should check if payload NOT equals given value', function(done) {
+ genericSwitchTest("neq", "Hello", true, true, "HEllO", done);
+ });
+
+ it('should return nothing when the payload does equal to desired string', function(done) {
+ genericSwitchTest("neq", "Hello", true, false, "Hello", done);
+ });
+
+ it('should check if payload equals given numeric value', function(done) {
+ genericSwitchTest("eq", 3, true, true, 3, done);
+ });
+
+ it('should return nothing when the payload doesn\'t equal to desired numeric value', function(done) {
+ genericSwitchTest("eq", 2, true, false, 4, done);
+ });
+
+ it('should check if payload NOT equals given numeric value', function(done) {
+ genericSwitchTest("neq", 55667744, true, true, -1234, done);
+ });
+
+ it('should return nothing when the payload does equal to desired numeric value', function(done) {
+ genericSwitchTest("neq", 10, true, false, 10, done);
+ });
+
+ it('should check if payload is less than given value', function(done) {
+ genericSwitchTest("lt", 3, true, true, 2, done);
+ });
+
+ it('should return nothing when the payload is not less than desired string', function(done) {
+ genericSwitchTest("lt", 3, true, false, 4, done);
+ });
+
+ it('should check if payload less than equals given value', function(done) {
+ genericSwitchTest("lte", 3, true, true, 3, done);
+ });
+
+ it('should check if payload is greater than given value', function(done) {
+ genericSwitchTest("gt", 3, true, true, 6, done);
+ });
+
+ it('should return nothing when the payload is not greater than desired string', function(done) {
+ genericSwitchTest("gt", 3, true, false, -1, done);
+ });
+
+ it('should check if payload is greater than/equals given value', function(done) {
+ genericSwitchTest("gte", 3, true, true, 3, done);
+ });
+
+ it('should return nothing when the payload is not greater than desired string', function(done) {
+ genericSwitchTest("gt", 3, true, false, -1, done);
+ });
+
+ it('should check if payload is greater than/equals given value', function(done) {
+ genericSwitchTest("gte", 3, true, true, 3, done);
+ });
+
+ it('should check if payload is between given values', function(done) {
+ twoFieldSwitchTest("btwn", 3, 5, true, true, 4, done);
+ });
+
+ it('should check if payload is not between given values', function(done) {
+ twoFieldSwitchTest("btwn", 3, 5, true, false, 12, done);
+ });
+
+ it('should check if payload contains given value', function(done) {
+ genericSwitchTest("cont", "Hello", true, true, "Hello World!", done);
+ });
+
+ it('should return nothing when the payload doesn\'t contain desired string', function(done) {
+ genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done);
+ });
+
+ it('should match regex', function(done) {
+ genericSwitchTest("regex", "[abc]+", true, true, "abbabac", done);
+ });
+
+ it('should return nothing when the payload doesn\'t match regex', function(done) {
+ genericSwitchTest("regex", "\\d+", true, false, "This is not a digit", done);
+ });
+
+ it('should return nothing when the payload doesn\'t contain desired string', function(done) {
+ genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done);
+ });
+
+ it('should check if input is true', function(done) {
+ singularSwitchTest(true, true, true, true, done);
+ });
+
+ it('sends nothing when input is false and checking for true', function(done) {
+ singularSwitchTest(true, true, false, false, done);
+ });
+
+ it('should check if input is indeed false', function(done) {
+ singularSwitchTest(false, true, true, false, done);
+ });
+
+ it('sends nothing when input is false and checking for true', function(done) {
+ singularSwitchTest(false, true, false, true, done);
+ });
+
+ it('should check if input is indeed null', function(done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"null"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+
+
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ if(msg.payload) {
+ try {
+ should.fail(null, null, "msg.payload should be undefined!");
+ } catch (err) {
+ done(err);
+ }
+ } else {
+ done();
+ }
+ });
+ switchNode1.receive({payload:undefined});
+ });
+ });
+
+ it('should check if input is indeed not null', function(done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"nnull"}],checkall:false,outputs:1,wires:[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+
+
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ if(msg.payload) {
+ done();
+ } else {
+ try {
+ msg.payload.should.equal("Anything here");
+ } catch (err) {
+ done(err);
+ }
+ }
+ });
+ switchNode1.receive({payload:"Anything here"});
+ });
+ });
+
+ it('sends a message when the "else/otherwise" statement is selected' , function(done) {
+ singularSwitchTest("else", true, true, 123456, done);
+ });
+
+ it('handles more than one switch statement' , function(done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:true,outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]},
+ {id:"helperNode1", type:"helper", wires:[]},
+ {id:"helperNode2", type:"helper", wires:[]},
+ {id:"helperNode3", type:"helper", wires:[]}];
+
+
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var helperNode2 = helper.getNode("helperNode2");
+ var helperNode3 = helper.getNode("helperNode3");
+
+ var nodeHitCount = 0;
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Hello");
+ nodeHitCount++;
+ } catch (err) {
+ done(err);
+ }
+ });
+ helperNode2.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Hello");
+ nodeHitCount++;
+ if(nodeHitCount == 2) {
+ done();
+ } else {
+ try {
+ should.fail(null, null, "Both statements should be triggered!");
+ } catch (err) {
+ done(err);
+ }
+ }
+ } catch (err) {
+ done(err);
+ }
+ });
+ helperNode3.on("input", function(msg) {
+ try {
+ should.fail(null, null, "The otherwise/else statement should not be triggered here!");
+ } catch (err) {
+ done(err);
+ }
+ });
+ switchNode1.receive({payload:"Hello"});
+ });
+ });
+
+ it('stops after first statement' , function(done) {
+ var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:"false",outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]},
+ {id:"helperNode1", type:"helper", wires:[]},
+ {id:"helperNode2", type:"helper", wires:[]},
+ {id:"helperNode3", type:"helper", wires:[]}];
+
+
+ helper.load(switchNode, flow, function() {
+ var switchNode1 = helper.getNode("switchNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var helperNode2 = helper.getNode("helperNode2");
+ var helperNode3 = helper.getNode("helperNode3");
+
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Hello");
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ helperNode2.on("input", function(msg) {
+ try {
+ should.fail(null, null, "The otherwise/else statement should not be triggered here!");
+ } catch (err) {
+ done(err);
+ }
+ });
+ helperNode3.on("input", function(msg) {
+ try {
+ should.fail(null, null, "The otherwise/else statement should not be triggered here!");
+ } catch (err) {
+ done(err);
+ }
+ });
+ switchNode1.receive({payload:"Hello"});
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/logic/15-change_spec.js b/dgbuilder/test/nodes/core/logic/15-change_spec.js
new file mode 100644
index 00000000..5072dfce
--- /dev/null
+++ b/dgbuilder/test/nodes/core/logic/15-change_spec.js
@@ -0,0 +1,194 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+
+var changeNode = require("../../../../nodes/core/logic/15-change.js");
+var helper = require("../../helper.js");
+
+describe('ChangeNode', function() {
+
+ beforeEach(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function(done) {
+ helper.unload();
+ helper.stopServer(done);
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"","reg":false,"name":"changeNode","wires":[[]]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ changeNode1.should.have.property('name', 'changeNode');
+ done();
+ });
+ });
+
+ it('sets the value of the message property', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("changed");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"changeMe"});
+ });
+ });
+
+ it('changes the value of the message property', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Goodbye World!");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"Hello World!"});
+ });
+ });
+
+ it('changes the value of the message property based on a regex', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\d+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Replace all numbers NUMBER and NUMBER");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"Replace all numbers 12 and 14"});
+ });
+ });
+
+ it('supports regex groups', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"(Hello)","to":"$1-$1-$1","reg":true,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal("Hello-Hello-Hello World");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"Hello World"});
+ });
+ });
+
+ it('Reports invalid regex', function(done) {
+
+ var sinon = require('sinon');
+
+ var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\+**+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+
+ sinon.stub(changeNode1, 'error', function(error) {
+ if(error.indexOf("regular expression" > -1)) {
+ done();
+ } else {
+ try {
+ should.fail(null, null, "An error should be reported for an invalid regex");
+ } catch (err) {
+ done(err);
+ }
+ }
+ });
+ changeNode1.receive({payload:"This is irrelevant"});
+ });
+ });
+
+ it('deletes the value of the message property', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"payload","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.should.not.have.property('payload');
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"This won't get through!"});
+ });
+ });
+
+// TODO confirm the behaviour of the change node later,apparently calling eval such that makeNew( msg, node.property.split("."), eval(node.to) ); is incorrect
+// it('changes the property name of the message object', function(done) {
+// var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.otherProp=10","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
+// {id:"helperNode1", type:"helper", wires:[]}];
+// helper.load(changeNode, flow, function() {
+// var changeNode1 = helper.getNode("changeNode1");
+// var helperNode1 = helper.getNode("helperNode1");
+// helperNode1.on("input", function(msg) {
+// try {
+// msg.otherProp.should.equal(10);
+// done();
+// } catch(err) {
+// done(err);
+// }
+// });
+// changeNode1.receive({payload:"changeMe"});
+// });
+// });
+
+ it('splits dot delimited properties into objects', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"pay.load","from":"","to":"10","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.pay.load.should.equal("10");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({"pay.load":"changeMe"});
+ });
+ });
+});
+
diff --git a/dgbuilder/test/nodes/core/logic/16-range_spec.js b/dgbuilder/test/nodes/core/logic/16-range_spec.js
new file mode 100644
index 00000000..abb6c646
--- /dev/null
+++ b/dgbuilder/test/nodes/core/logic/16-range_spec.js
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+
+var rangeNode = require("../../../../nodes/core/logic/16-range.js");
+var helper = require("../../helper.js");
+
+describe('RangeNode', function() {
+
+ beforeEach(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function(done) {
+ helper.unload();
+ helper.stopServer(done);
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{"id":"rangeNode1","type":"range","minin":"0","maxin":"99","minout":"100","maxout":"199","action":"range","round":true,"name":"rangeNode","wires":[[]]}];
+ helper.load(rangeNode, flow, function() {
+ var rangeNode1 = helper.getNode("rangeNode1");
+ rangeNode1.should.have.property('name', 'rangeNode');
+ done();
+ });
+ });
+
+ /**
+ * Run a generic range test
+ * @param action - scale/clamp (range limit)/roll (modulo): what action to choose
+ * @param minin - map from minimum value
+ * @param maxin - map from maximum value
+ * @param minout - map to minimum value
+ * @param maxout - map to maximum value
+ * @param round - whether to round the result to the nearest integer
+ * @param aPayload - what payload to send to the range node
+ * @param expectedResult - what result we're expecting
+ * @param done - the callback to call when test done
+ */
+ function genericRangeTest(action, minin, maxin, minout, maxout, round, aPayload, expectedResult, done) {
+ var flow = [{"id":"rangeNode1","type":"range","minin":minin,"maxin":maxin,"minout":minout,"maxout":maxout,"action":action,"round":round,"name":"rangeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(rangeNode, flow, function() {
+ var rangeNode1 = helper.getNode("rangeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.payload.should.equal(expectedResult);
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ rangeNode1.receive({payload:aPayload});
+ });
+ }
+
+ it('ranges numbers up tenfold', function(done) {
+ genericRangeTest("scale", 0, 100, 0, 1000, false, 50, 500, done);
+ });
+
+ it('ranges numbers down such as centimetres to metres', function(done) {
+ genericRangeTest("scale", 0, 100, 0, 1, false, 55, 0.55, done);
+ });
+
+ it('wraps numbers down say for degree/rotation reading 1/2', function(done) {
+ genericRangeTest("roll", 0, 10, 0, 360, true, 15, 180, done); // 1/2 around wrap => "one and a half turns"
+ });
+
+ it('wraps numbers around say for degree/rotation reading 1/3', function(done) {
+ genericRangeTest("roll", 0, 10, 0, 360, true, 13.3333, 120, done); // 1/3 around wrap => "one and a third turns"
+ });
+
+ it('wraps numbers around say for degree/rotation reading 1/4', function(done) {
+ genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns"
+ });
+
+ it('wraps numbers around say for degree/rotation reading 1/4', function(done) {
+ genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns"
+ });
+
+ it('wraps numbers down say for degree/rotation reading 1/4', function(done) {
+ genericRangeTest("roll", 0, 10, 0, 360, true, -12.5, 270, done); // 1/4 backwards wrap => "one and a quarter turns backwards"
+ });
+
+ it('clamps numbers within a range - over max', function(done) {
+ genericRangeTest("clamp", 0, 10, 0, 1000, false, 111, 1000, done);
+ });
+
+ it('clamps numbers within a range - below min', function(done) {
+ genericRangeTest("clamp", 0, 10, 0, 1000, false, -1, 0, done);
+ });
+
+ it('reports if input is not a number', function(done) {
+ var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":0,"minout":0,"maxout":0,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(rangeNode, flow, function() {
+ var rangeNode1 = helper.getNode("rangeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+
+ var sinon = require('sinon');
+ sinon.stub(rangeNode1, 'log', function(log) {
+ if(log.indexOf("Not a number") > -1) {
+ done();
+ } else {
+ try {
+ should.fail(null, null, "Non-number inputs should be reported!");
+ } catch (err) {
+ done(err);
+ }
+ }
+ });
+
+ rangeNode1.receive({payload:"NOT A NUMBER"});
+ });
+ });
+});
diff --git a/dgbuilder/test/nodes/core/parsers/70-HTML_spec.js b/dgbuilder/test/nodes/core/parsers/70-HTML_spec.js
new file mode 100644
index 00000000..e7e4cbb5
--- /dev/null
+++ b/dgbuilder/test/nodes/core/parsers/70-HTML_spec.js
@@ -0,0 +1,211 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var sinon = require("sinon");
+var path = require("path");
+var fs = require('fs-extra');
+
+var htmlNode = require("../../../../nodes/core/parsers/70-HTML.js");
+var helper = require("../../helper.js");
+
+describe('html node', function() {
+
+ var resourcesDir = __dirname+ path.sep + ".." + path.sep + ".." + path.sep + ".." + path.sep + "resources" + path.sep;
+ var file = path.join(resourcesDir, "70-HTML-test-file.html");
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ beforeEach(function() {
+ fs.existsSync(file).should.be.true;
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"htmlNode1", type:"html", name: "htmlNode" }];
+ helper.load(htmlNode, flow, function() {
+ var htmlNode1 = helper.getNode("htmlNode1");
+ htmlNode1.should.have.property('name', 'htmlNode');
+ done();
+ });
+ });
+
+ it('should retrieve header contents as default', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ should.equal(msg.payload, 'This is a test page for node 70-HTML');
+ done();
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ it('should retrieve paragraph contents when specified', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"text",tag:"p"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ should.equal(msg.payload, 'There\'s nothing to read here.');
+ done();
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ it('should retrieve list contents as an array of html as default', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.payload[0].indexOf("<li>Blue</li>").should.be.above(-1);
+ msg.payload[0].indexOf("<li>Red</li>").should.be.above(-1);
+ done();
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ it('should retrieve list contents as an array of text', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol",ret:"text"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.payload.should.be.instanceof(Array).and.have.lengthOf(1);
+ msg.payload[0].indexOf("Blue").should.be.above(-1);
+ msg.payload[0].indexOf("Red").should.be.above(-1);
+ done();
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ it('should log on error', function(done) {
+ fs.readFile(file,function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"p"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n1.on("log", function(msg) {
+ msg.should.have.property('msg');
+ msg.msg.indexOf("Error:").should.be.above(-1);
+ msg.msg.should.startWith("Error:");
+ done();
+ });
+ n1.receive({payload:null,topic: "bar"});
+ });
+ });
+ });
+
+ describe('multiple messages', function(){
+ var cnt = 0;
+
+ afterEach(function() {
+ cnt.should.be.exactly(2);
+ cnt = 0;
+ });
+
+ it('should retrieve list contents as html as default with output as multiple msgs ', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",as:"multi"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ cnt++;
+ msg.should.have.property('topic', 'bar');
+ if (cnt !== 1 && cnt !== 2) {
+ return false;
+ }
+ if (cnt === 1) {
+ msg.payload.indexOf("<li>Apple</li>").should.be.above(-1);
+ msg.payload.indexOf("<li>Pear</li>").should.be.above(-1);
+ } else if (cnt === 2) {
+ msg.payload.indexOf("<li>Potato</li>").should.be.above(-1);
+ msg.payload.indexOf("<li>Parsnip</li>").should.be.above(-1);
+ done();
+ }
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ it('should retrieve list contents as text with output as multiple msgs ', function(done) {
+ fs.readFile(file, 'utf8', function(err, data) {
+ var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",ret:"text",as:"multi"},
+ {id:"n2", type:"helper"}];
+
+ helper.load(htmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ cnt++;
+ msg.should.have.property('topic', 'bar');
+ if (cnt !== 1 && cnt !== 2) {
+ return false;
+ }
+ if (cnt === 1) {
+ msg.payload.indexOf("Apple").should.be.above(-1);
+ msg.payload.indexOf("Pear").should.be.above(-1);
+ } else if (cnt === 2) {
+ msg.payload.indexOf("Potato").should.be.above(-1);
+ msg.payload.indexOf("Parsnip").should.be.above(-1);
+ done();
+ }
+ });
+ n1.receive({payload:data,topic: "bar"});
+ });
+ });
+ });
+
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/parsers/70-JSON_spec.js b/dgbuilder/test/nodes/core/parsers/70-JSON_spec.js
new file mode 100644
index 00000000..b2ebe719
--- /dev/null
+++ b/dgbuilder/test/nodes/core/parsers/70-JSON_spec.js
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var jsonNode = require("../../../../nodes/core/parsers/70-JSON.js");
+var helper = require("../../helper.js");
+
+describe('JSON node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"jsonNode1", type:"json", name: "jsonNode" }];
+ helper.load(jsonNode, flow, function() {
+ var jsonNode1 = helper.getNode("jsonNode1");
+ jsonNode1.should.have.property('name', 'jsonNode');
+ done();
+ });
+ });
+
+ it('should convert a valid json string to a javascript object', function(done) {
+ var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
+ {id:"jn2", type:"helper"}];
+ helper.load(jsonNode, flow, function() {
+ var jn1 = helper.getNode("jn1");
+ var jn2 = helper.getNode("jn2");
+ jn2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.payload.should.have.property('employees');
+ msg.payload.employees[0].should.have.property('firstName', 'John');
+ msg.payload.employees[0].should.have.property('lastName', 'Smith');
+ done();
+ });
+ var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}';
+ jn1.receive({payload:jsonString,topic: "bar"});
+ });
+ });
+
+ it('should convert a javascript object to a json string', function(done) {
+ var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
+ {id:"jn2", type:"helper"}];
+ helper.load(jsonNode, flow, function() {
+ var jn1 = helper.getNode("jn1");
+ var jn2 = helper.getNode("jn2");
+ jn2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}');
+ done();
+ });
+ var obj = {employees:[{firstName:"John", lastName:"Smith"}]};
+ jn1.receive({payload:obj,topic: "bar"});
+ });
+ });
+
+ it('should log an error if asked to parse an invalid json string', function(done) {
+ var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
+ {id:"jn2", type:"helper"}];
+ helper.load(jsonNode, flow, function() {
+ var jn1 = helper.getNode("jn1");
+ var jn2 = helper.getNode("jn2");
+ jn1.on("log", function(msg) {
+ msg.should.have.property('msg');
+ should.deepEqual("SyntaxError: Unexpected token o"+ "\nfoo", msg.msg);
+ done();
+ });
+ jn1.receive({payload:'foo',topic: "bar"});
+ });
+ });
+
+ it('should log an error if asked to parse something thats not json or js', function(done) {
+ var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
+ {id:"jn2", type:"helper"}];
+ helper.load(jsonNode, flow, function() {
+ var jn1 = helper.getNode("jn1");
+ var jn2 = helper.getNode("jn2");
+ jn1.on("log", function(msg) {
+ msg.should.have.property('msg');
+ should.deepEqual("dropped: 1", msg.msg);
+ done();
+ });
+ jn1.receive({payload:1,topic: "bar"});
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/parsers/70-XML_spec.js b/dgbuilder/test/nodes/core/parsers/70-XML_spec.js
new file mode 100644
index 00000000..8bf269d1
--- /dev/null
+++ b/dgbuilder/test/nodes/core/parsers/70-XML_spec.js
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var xmlNode = require("../../../../nodes/core/parsers/70-XML.js");
+var helper = require("../../helper.js");
+
+describe('XML node', function() {
+
+ before(function(done) {
+ helper.startServer(done);
+ });
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"xmlNode1", type:"xml", name: "xmlNode" }];
+ helper.load(xmlNode, flow, function() {
+ var xmlNode1 = helper.getNode("xmlNode1");
+ xmlNode1.should.have.property('name', 'xmlNode');
+ done();
+ });
+ });
+
+ it('should convert a valid xml string to a javascript object', function(done) {
+ var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(xmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ msg.payload.should.have.property('employees');
+ msg.payload.employees.should.have.property('firstName');
+ should.equal(msg.payload.employees.firstName[0], 'John');
+ msg.payload.employees.should.have.property('lastName');
+ should.equal(msg.payload.employees.lastName[0], 'Smith');
+ done();
+ });
+ var string = '<employees><firstName>John</firstName><lastName>Smith</lastName></employees>';
+ n1.receive({payload:string,topic: "bar"});
+ });
+ });
+
+ it('should convert a javascript object to an xml string', function(done) {
+ var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(xmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property('topic', 'bar');
+ console.log(msg.payload);
+ var index = msg.payload.indexOf('<employees><firstName>John</firstName><lastName>Smith</lastName></employees>');
+ index.should.be.above(-1);
+ done();
+ });
+ var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }};
+ n1.receive({payload:obj,topic: "bar"});
+ });
+ });
+
+ it('should log an error if asked to parse an invalid xml string', function(done) {
+ var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(xmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n1.on("log", function(msg) {
+ should.deepEqual("error", msg.level);
+ done();
+ });
+ n1.receive({payload:'<not valid xml>',topic: "bar"});
+ });
+ });
+
+ it('should log an error if asked to parse something thats not xml or js', function(done) {
+ var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"},
+ {id:"n2", type:"helper"}];
+ helper.load(xmlNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n1.on("log", function(msg) {
+ msg.should.have.property('msg');
+ should.deepEqual("This node only handles xml strings or js objects.", msg.msg);
+ done();
+ });
+ n1.receive({payload:1,topic: "bar"});
+ });
+ });
+
+});
diff --git a/dgbuilder/test/nodes/core/storage/28-tail_spec.js b/dgbuilder/test/nodes/core/storage/28-tail_spec.js
new file mode 100644
index 00000000..06124576
--- /dev/null
+++ b/dgbuilder/test/nodes/core/storage/28-tail_spec.js
@@ -0,0 +1,165 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var path = require('path');
+var fs = require('fs-extra');
+var mkdirp = require('mkdirp');
+
+var tailNode = require("../../../../nodes/core/storage/28-tail.js");
+var helper = require("../../helper.js");
+
+describe('TailNode', function() {
+
+ var resourcesDir = path.join(__dirname,"..","..","..","resources");
+ var fileToTail = path.join(resourcesDir,"28-tail-test-file.txt");
+
+ beforeEach(function(done) {
+ fs.writeFileSync(fileToTail, "Tail message line 1\nTail message line 2\n");
+ helper.startServer(done);
+ });
+
+ afterEach(function(done) {
+ helper.unload().then(function() {
+ fs.unlinkSync(fileToTail);
+ helper.stopServer(done);
+ });
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"tailNode1", type:"tail", name: "tailNode", "split":true, "filename":fileToTail}];
+ helper.load(tailNode, flow, function() {
+ var tailNode1 = helper.getNode("tailNode1");
+ tailNode1.should.have.property('name', 'tailNode');
+ done();
+ });
+ });
+
+ it('tail should tail a file', function(done) {
+ var flow = [{id:"tailNode1", type:"tail", name: "tailNode", "split":true, "filename":fileToTail, "wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(tailNode, flow, function() {
+ var tailNode1 = helper.getNode("tailNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var inputCounter = 0;
+ helperNode1.on("input", function(msg) {
+ //console.log(msg);
+ msg.should.have.property('topic', fileToTail);
+ msg.payload.should.equal("Tail message line " + (++inputCounter + 2));
+ if (inputCounter === 2) {
+ done();
+ }
+ });
+ setTimeout( function() {
+ fs.appendFileSync(fileToTail, "Tail message line 3\n");
+ fs.appendFileSync(fileToTail, "Tail message line 4\n");
+ },100);
+ });
+ });
+
+ it('tail should work in non-split mode', function(done) {
+ var flow = [{id:"tailNode1", type:"tail", name: "tailNode", "split":false, "filename":fileToTail, "wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(tailNode, flow, function() {
+ var tailNode1 = helper.getNode("tailNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ //console.log(msg);
+ msg.should.have.property('topic', fileToTail);
+ msg.payload.should.equal("Tail message line 5\nTail message line 6\n");
+ done();
+ });
+ setTimeout( function() {
+ fs.appendFileSync(fileToTail, "Tail message line 5\nTail message line 6\n");
+ },150);
+ });
+ });
+
+ it('tail should handle a non-existent file', function(done) {
+ fs.unlinkSync(fileToTail);
+ var flow = [{id:"tailNode1", type:"tail", name: "tailNode", "split":true, "filename":fileToTail, "wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(tailNode, flow, function() {
+ var tailNode1 = helper.getNode("tailNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ msg.should.have.property('topic', fileToTail);
+ msg.payload.should.equal("Tail message line");
+ done();
+ });
+ setTimeout( function() {
+ fs.writeFileSync(fileToTail, "Tail message line\n");
+ },150);
+ });
+ });
+ /*
+ it('tail should handle file truncation', function(done) {
+ var flow = [{id:"tailNode1", type:"tail", name: "tailNode", "split":true, "filename":fileToTail, "wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(tailNode, flow, function() {
+ var tailNode1 = helper.getNode("tailNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ var inputCounter = 0;
+ var warned = false;
+ tailNode1.on("log", function(msg) {
+ if (msg.level == "warn") { warned = true; }
+ });
+ helperNode1.on("input", function(msg) {
+ console.log("inputCounter =",inputCounter);
+ console.log(msg);
+ msg.should.have.property('topic', fileToTail);
+ inputCounter++;
+ if (inputCounter === 1) {
+ warned.should.be.false;
+ msg.payload.should.equal("Tail message line append");
+ } else if (inputCounter === 2) {
+ msg.payload.should.equal("Tail message line truncate");
+ } else {
+ msg.payload.should.equal("Tail message line append "+inputCounter);
+ }
+
+ if (inputCounter === 5) {
+ setTimeout(function() {
+ warned.should.be.true;
+ done();
+ },100);
+ }
+ });
+ var actions = [
+ function() { fs.appendFileSync(fileToTail, "Tail message line append\n");},
+ function() { fs.writeFileSync(fileToTail, "Tail message line truncate\n");},
+ function() { fs.appendFileSync(fileToTail, "Tail message line append 3\n");},
+ function() { fs.appendFileSync(fileToTail, "Tail message line append 4\n");},
+ function() { fs.appendFileSync(fileToTail, "Tail message line append 5\n");}
+ ];
+
+ function processAction() {
+ var action = actions.shift();
+ action();
+ if (actions.length > 0) {
+ setTimeout(function() {
+ processAction();
+ },250);
+ }
+ }
+ setTimeout( function() {
+ processAction();
+ },150);
+ });
+ });
+ */
+
+});
diff --git a/dgbuilder/test/nodes/helper.js b/dgbuilder/test/nodes/helper.js
new file mode 100644
index 00000000..9b371ca4
--- /dev/null
+++ b/dgbuilder/test/nodes/helper.js
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var should = require("should");
+var when = require("when");
+var request = require('supertest');
+var nock;
+if (!process.version.match(/^v0\.[0-9]\./)) {
+ // only set nock for node >= 0.10
+ try {
+ nock = require('nock');
+ } catch (err) {
+ // nevermind, will skip nock tests
+ nock = null;
+ }
+}
+var RED = require("../../red/red.js");
+var redNodes = require("../../red/nodes");
+var flows = require("../../red/nodes/flows");
+var credentials = require("../../red/nodes/credentials");
+var comms = require("../../red/comms.js");
+
+var http = require('http');
+var express = require('express');
+var app = express();
+
+var address = '127.0.0.1';
+var listenPort = 0; // use ephemeral port
+var port;
+var url;
+
+var server;
+
+function helperNode(n) {
+ RED.nodes.createNode(this, n);
+}
+
+module.exports = {
+ load: function(testNode, testFlows, testCredentials, cb) {
+ if (typeof testCredentials === 'function') {
+ cb = testCredentials;
+ testCredentials = {};
+ }
+
+ var storage = {
+ getFlows: function() {
+ var defer = when.defer();
+ defer.resolve(testFlows);
+ return defer.promise;
+ },
+ getCredentials: function() {
+ var defer = when.defer();
+ defer.resolve(testCredentials);
+ return defer.promise;
+ },
+ saveCredentials: function() {
+ // do nothing
+ },
+ };
+ var settings = {
+ available: function() { return false; }
+ };
+
+ redNodes.init(settings, storage);
+ credentials.init(storage);
+ RED.nodes.registerType("helper", helperNode);
+ testNode(RED);
+ flows.load().then(function() {
+ should.deepEqual(testFlows, flows.getFlows());
+ cb();
+ });
+ },
+ unload: function() {
+ // TODO: any other state to remove between tests?
+ redNodes.clearRegistry();
+ return flows.stopFlows();
+ },
+
+ getNode: function(id) {
+ return flows.get(id);
+ },
+
+ credentials: credentials,
+
+ clearFlows: function() {
+ return flows.clear();
+ },
+
+ request: function() {
+ return request(RED.httpAdmin);
+ },
+
+ startServer: function(done) {
+ server = http.createServer(function(req,res){app(req,res);});
+ RED.init(server, {});
+ server.listen(listenPort, address);
+ server.on('listening', function() {
+ port = server.address().port;
+ url = 'http://' + address + ':' + port;
+ comms.start();
+ done();
+ });
+ },
+ //TODO consider saving TCP handshake/server reinit on start/stop/start sequences
+ stopServer: function(done) {
+ if(server) {
+ server.close(done);
+ }
+ },
+
+ url: function() { return url; },
+
+ nock: nock,
+
+};