diff options
author | Chinthakayala, Sheshashailavas (sc2914) <sc2914@us.att.com> | 2017-08-28 05:25:46 -0900 |
---|---|---|
committer | Chinthakayala, Sheshashailavas (sc2914) <sc2914@att.com> | 2017-08-28 05:36:52 -0900 |
commit | d1569975bb18f4359fac18aa98f55b69c248a3ad (patch) | |
tree | c8681eeac12dca8673ccf841705daac88bf01ca6 /dgbuilder/test/nodes | |
parent | a016ea661ff5767a3539734c4c07ef974a6e4614 (diff) |
[CCSDK-28] populated the seed code for dgbuilder
updated the code to point to the new package name for sli
Change-Id: I3b5a1d05dc5193664fd4a667afdcd0b2354010a4
Issue-ID:{CCSDK-28}
Signed-off-by: Chinthakayala, Sheshashailavas (sc2914) <sc2914@att.com>
Signed-off-by: Chinthakayala, Sheshashailavas (sc2914) <sc2914@att.com>
Diffstat (limited to 'dgbuilder/test/nodes')
-rw-r--r-- | dgbuilder/test/nodes/core/core/20-inject_spec.js | 118 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/core/58-debug_spec.js | 298 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/core/80-function_spec.js | 149 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/core/80-template_spec.js | 46 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/core/89-delay_spec.js | 420 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/core/90-comment_spec.js | 36 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/logic/10-switch_spec.js | 367 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/logic/15-change_spec.js | 194 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/logic/16-range_spec.js | 131 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/parsers/70-HTML_spec.js | 211 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/parsers/70-JSON_spec.js | 104 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/parsers/70-XML_spec.js | 107 | ||||
-rw-r--r-- | dgbuilder/test/nodes/core/storage/28-tail_spec.js | 165 | ||||
-rw-r--r-- | dgbuilder/test/nodes/helper.js | 128 |
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, + +}; |