mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Flow Engine refactor
Each flow/tab now exists as its own logical object. This is the ground work for allowing flows to be added/removed/updated independently.
This commit is contained in:
891
test/red/nodes/flows/Flow_spec.js
Normal file
891
test/red/nodes/flows/Flow_spec.js
Normal file
@@ -0,0 +1,891 @@
|
||||
/**
|
||||
* Copyright 2015 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 clone = require('clone');
|
||||
var util = require("util");
|
||||
|
||||
// var Flow = require("../../../../red/nodes/Flow");
|
||||
// var flows = require("../../../../red/nodes/flows");
|
||||
// var Node = require("../../../../red/nodes/Node");
|
||||
//
|
||||
// var typeRegistry = require("../../../red/nodes/registry");
|
||||
// var credentials = require("../../../red/nodes/credentials");
|
||||
return;
|
||||
|
||||
describe.skip('Flow', function() {
|
||||
describe('#constructor',function() {
|
||||
it('called with an empty flow',function() {
|
||||
var config = [];
|
||||
var flow = new Flow(config);
|
||||
config.should.eql(flow.getFlow());
|
||||
|
||||
var nodeCount = 0;
|
||||
flow.eachNode(function(node) {
|
||||
nodeCount++;
|
||||
});
|
||||
|
||||
nodeCount.should.equal(0);
|
||||
});
|
||||
|
||||
it('called with a non-empty flow with no missing types', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
// For this test, don't care what the actual type is, just
|
||||
// that this returns a non-false result
|
||||
return {};
|
||||
});
|
||||
try {
|
||||
var config = [{id:"123",type:"test"}];
|
||||
var flow = new Flow(config);
|
||||
config.should.eql(flow.getFlow());
|
||||
|
||||
flow.getMissingTypes().should.have.length(0);
|
||||
} finally {
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('identifies missing types in a flow', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
if (type == "test") {
|
||||
return {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
var config = [{id:"123",type:"test"},{id:"456",type:"test1"},{id:"789",type:"test2"}];
|
||||
var flow = new Flow(config);
|
||||
config.should.eql(flow.getFlow());
|
||||
|
||||
flow.getMissingTypes().should.eql(["test1","test2"]);
|
||||
} finally {
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('extracts node credentials', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
// For this test, don't care what the actual type is, just
|
||||
// that this returns a non-false result
|
||||
return {};
|
||||
});
|
||||
|
||||
try {
|
||||
var config = [{id:"123",type:"test",credentials:{a:1,b:2}}];
|
||||
var resultingConfig = clone(config);
|
||||
delete resultingConfig[0].credentials;
|
||||
var flow = new Flow(config);
|
||||
flow.getFlow().should.eql(resultingConfig);
|
||||
flow.getMissingTypes().should.have.length(0);
|
||||
} finally {
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('missing types',function() {
|
||||
it('prevents a flow with missing types from starting', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
if (type == "test") {
|
||||
return {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
var config = [{id:"123",type:"test"},{id:"456",type:"test1"},{id:"789",type:"test2"}];
|
||||
var flow = new Flow(config);
|
||||
flow.getMissingTypes().should.have.length(2);
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
flow.start();
|
||||
}).should.throw();
|
||||
} finally {
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('removes missing types as they are registered', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
if (type == "test") {
|
||||
return {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
var flowStart;
|
||||
try {
|
||||
var config = [{id:"123",type:"test"},{id:"456",type:"test1"},{id:"789",type:"test2"}];
|
||||
var flow = new Flow(config);
|
||||
|
||||
flowStart = sinon.stub(flow,"start",function() {this.started = true;});
|
||||
config.should.eql(flow.getFlow());
|
||||
|
||||
flow.getMissingTypes().should.eql(["test1","test2"]);
|
||||
|
||||
var resp = flow.typeRegistered("a-random-node");
|
||||
resp.should.be.false;
|
||||
|
||||
resp = flow.typeRegistered("test1");
|
||||
resp.should.be.true;
|
||||
flow.getMissingTypes().should.eql(["test2"]);
|
||||
flowStart.called.should.be.false;
|
||||
|
||||
resp = flow.typeRegistered("test2");
|
||||
resp.should.be.true;
|
||||
flow.getMissingTypes().should.eql([]);
|
||||
flowStart.called.should.be.false;
|
||||
} finally {
|
||||
flowStart.restore();
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('starts flows once all missing types are registered', function() {
|
||||
var getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
if (type == "test") {
|
||||
return {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
var flowStart;
|
||||
try {
|
||||
var config = [{id:"123",type:"test"},{id:"456",type:"test1"},{id:"789",type:"test2"}];
|
||||
var flow = new Flow(config);
|
||||
|
||||
// First call to .start throws err due to missing types
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
flow.start();
|
||||
}).should.throw();
|
||||
|
||||
// Stub .start so when missing types are registered, we don't actually try starting them
|
||||
flowStart = sinon.stub(flow,"start",function() {});
|
||||
config.should.eql(flow.getFlow());
|
||||
|
||||
flow.getMissingTypes().should.eql(["test1","test2"]);
|
||||
|
||||
flow.typeRegistered("test1");
|
||||
flow.typeRegistered("test2");
|
||||
flow.getMissingTypes().should.have.length(0);
|
||||
flowStart.called.should.be.true;
|
||||
} finally {
|
||||
flowStart.restore();
|
||||
getType.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#start',function() {
|
||||
var getType;
|
||||
var getNode;
|
||||
var credentialsClean;
|
||||
|
||||
var stoppedNodes = {};
|
||||
var currentNodes = {};
|
||||
var rewiredNodes = {};
|
||||
var createCount = 0;
|
||||
|
||||
var TestNode = function(n) {
|
||||
Node.call(this,n);
|
||||
createCount++;
|
||||
var node = this;
|
||||
this.handled = 0;
|
||||
this.stopped = false;
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled++;
|
||||
node.send(msg);
|
||||
});
|
||||
this.on('close',function() {
|
||||
node.stopped = true;
|
||||
stoppedNodes[node.id] = node;
|
||||
delete currentNodes[node.id];
|
||||
});
|
||||
this.updateWires = function(newWires) {
|
||||
rewiredNodes[node.id] = node;
|
||||
node.newWires = newWires;
|
||||
};
|
||||
}
|
||||
util.inherits(TestNode,Node);
|
||||
|
||||
before(function() {
|
||||
getNode = sinon.stub(flows,"get",function(id) {
|
||||
return currentNodes[id];
|
||||
});
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return TestNode;
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(config){});
|
||||
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
credentialsClean.restore();
|
||||
getNode.restore();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
currentNodes = {};
|
||||
stoppedNodes = {};
|
||||
rewiredNodes = {};
|
||||
createCount = 0;
|
||||
});
|
||||
|
||||
it("instantiates an initial configuration and stops it",function(done) {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:["2"]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
currentNodes["1"].should.have.a.property("handled",0);
|
||||
currentNodes["2"].should.have.a.property("handled",0);
|
||||
currentNodes["3"].should.have.a.property("handled",0);
|
||||
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",1);
|
||||
|
||||
flow.stop().then(function() {
|
||||
currentNodes.should.not.have.a.property("1");
|
||||
currentNodes.should.not.have.a.property("2");
|
||||
currentNodes.should.not.have.a.property("3");
|
||||
stoppedNodes.should.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.have.a.property("3");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("rewires nodes specified by diff",function(done) {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:["2"]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
|
||||
var flow = new Flow(config);
|
||||
createCount.should.equal(0);
|
||||
flow.start();
|
||||
createCount.should.equal(3);
|
||||
flow.start({rewire:"2"});
|
||||
createCount.should.equal(3);
|
||||
rewiredNodes.should.have.a.property("2");
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('#stop', function() {
|
||||
var getType;
|
||||
var getNode;
|
||||
var credentialsClean;
|
||||
|
||||
var stoppedNodes = {};
|
||||
var currentNodes = {};
|
||||
|
||||
var TestNode = function(n) {
|
||||
Node.call(this,n);
|
||||
var node = this;
|
||||
this.handled = 0;
|
||||
this.stopped = false;
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled++;
|
||||
node.send(msg);
|
||||
});
|
||||
this.on('close',function() {
|
||||
node.stopped = true;
|
||||
stoppedNodes[node.id] = node;
|
||||
delete currentNodes[node.id];
|
||||
});
|
||||
}
|
||||
util.inherits(TestNode,Node);
|
||||
|
||||
var TestAsyncNode = function(n) {
|
||||
Node.call(this,n);
|
||||
var node = this;
|
||||
this.handled = 0;
|
||||
this.stopped = false;
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled++;
|
||||
node.send(msg);
|
||||
});
|
||||
this.on('close',function(done) {
|
||||
setTimeout(function() {
|
||||
node.stopped = true;
|
||||
stoppedNodes[node.id] = node;
|
||||
delete currentNodes[node.id];
|
||||
done();
|
||||
},500);
|
||||
});
|
||||
}
|
||||
util.inherits(TestAsyncNode,Node);
|
||||
|
||||
before(function() {
|
||||
getNode = sinon.stub(flows,"get",function(id) {
|
||||
return currentNodes[id];
|
||||
});
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
if (type=="test") {
|
||||
return TestNode;
|
||||
} else {
|
||||
return TestAsyncNode;
|
||||
}
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(config){});
|
||||
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
credentialsClean.restore();
|
||||
getNode.restore();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
currentNodes = {};
|
||||
stoppedNodes = {};
|
||||
});
|
||||
|
||||
it("stops all nodes",function(done) {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:["2"]},{id:"2",type:"asyncTest",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
flow.stop().then(function() {
|
||||
currentNodes.should.not.have.a.property("1");
|
||||
currentNodes.should.not.have.a.property("2");
|
||||
currentNodes.should.not.have.a.property("3");
|
||||
stoppedNodes.should.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.have.a.property("3");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stops nodes specified by diff",function(done) {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:["2"]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
flow.stop({stop:["2"]}).then(function() {
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.not.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
stoppedNodes.should.not.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.not.have.a.property("3");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('#diffConfig',function() {
|
||||
var getType;
|
||||
var getNode;
|
||||
var credentialsClean;
|
||||
|
||||
var stoppedNodes = {};
|
||||
var currentNodes = {};
|
||||
|
||||
var TestNode = function(n) {
|
||||
Node.call(this,n);
|
||||
var node = this;
|
||||
this.handled = 0;
|
||||
this.stopped = false;
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled++;
|
||||
node.send(msg);
|
||||
});
|
||||
this.on('close',function() {
|
||||
node.stopped = true;
|
||||
stoppedNodes[node.id] = node;
|
||||
delete currentNodes[node.id];
|
||||
});
|
||||
}
|
||||
util.inherits(TestNode,Node);
|
||||
|
||||
before(function() {
|
||||
getNode = sinon.stub(flows,"get",function(id) {
|
||||
return currentNodes[id];
|
||||
});
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return TestNode;
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(config){});
|
||||
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
credentialsClean.restore();
|
||||
getNode.restore();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
currentNodes = {};
|
||||
stoppedNodes = {};
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("#diffConfig",function() {
|
||||
return;
|
||||
var getType;
|
||||
var getNode;
|
||||
var credentialsClean;
|
||||
|
||||
var stoppedNodes = {};
|
||||
var currentNodes = {};
|
||||
|
||||
var TestNode = function(n) {
|
||||
Node.call(this,n);
|
||||
var node = this;
|
||||
this.handled = 0;
|
||||
this.stopped = false;
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled++;
|
||||
node.send(msg);
|
||||
});
|
||||
this.on('close',function() {
|
||||
node.stopped = true;
|
||||
stoppedNodes[node.id] = node;
|
||||
delete currentNodes[node.id];
|
||||
});
|
||||
}
|
||||
util.inherits(TestNode,Node);
|
||||
|
||||
before(function() {
|
||||
getNode = sinon.stub(flows,"get",function(id) {
|
||||
return currentNodes[id];
|
||||
});
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return TestNode;
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(config){});
|
||||
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
credentialsClean.restore();
|
||||
getNode.restore();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
currentNodes = {};
|
||||
stoppedNodes = {};
|
||||
});
|
||||
|
||||
it('handles an identical configuration', function() {
|
||||
var config = [{id:"123",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = [{id:"123",type:"test",foo:"a",wires:[]}];
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
var diffResult = flow.diffConfig(newConfig,"nodes");
|
||||
|
||||
diffResult.should.have.property("type","nodes");
|
||||
diffResult.should.have.property("stop",[]);
|
||||
diffResult.should.have.property("rewire",[]);
|
||||
diffResult.should.have.property("config",newConfig);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe("#dead",function() {
|
||||
return;
|
||||
it("stops all nodes on new full deploy",function(done) {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:["2"]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = [{id:"4",type:"test",foo:"a",wires:["5"]},{id:"5",type:"test",bar:"b",wires:[["6"]]},{id:"6",type:"test",foo:"a",wires:[]}];
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
flow.applyConfig(newConfig).then(function() {
|
||||
currentNodes.should.not.have.a.property("1");
|
||||
currentNodes.should.not.have.a.property("2");
|
||||
currentNodes.should.not.have.a.property("3");
|
||||
stoppedNodes.should.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.have.a.property("3");
|
||||
|
||||
currentNodes.should.have.a.property("4");
|
||||
currentNodes.should.have.a.property("5");
|
||||
currentNodes.should.have.a.property("6");
|
||||
done();
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
it("stops only modified nodes on 'nodes' deploy",function(done) {
|
||||
var config = [{id:"1",type:"test",name:"a",wires:["2"]},{id:"2",type:"test",name:"b",wires:[["3"]]},{id:"3",type:"test",name:"c",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].name = "B";
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
currentNodes["2"].should.have.a.property("name","b");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",1);
|
||||
|
||||
|
||||
flow.applyConfig(newConfig,"nodes").then(function() {
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
currentNodes["2"].should.have.a.property("name","B");
|
||||
|
||||
stoppedNodes.should.not.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.not.have.a.property("3");
|
||||
stoppedNodes["2"].should.have.a.property("name","b");
|
||||
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",2);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",2);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stops only modified flows on 'flows' deploy",function(done) {
|
||||
var config = [{id:"1",type:"test",name:"a",wires:["2"]},{id:"2",type:"test",name:"b",wires:[[]]},{id:"3",type:"test",name:"c",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].name = "B";
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
currentNodes["2"].should.have.a.property("name","b");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",0);
|
||||
|
||||
currentNodes["3"].receive({payload:"test"});
|
||||
currentNodes["3"].should.have.a.property("handled",1);
|
||||
|
||||
flow.applyConfig(newConfig,"flows").then(function() {
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
currentNodes["2"].should.have.a.property("name","B");
|
||||
|
||||
stoppedNodes.should.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.not.have.a.property("3");
|
||||
|
||||
stoppedNodes["2"].should.have.a.property("name","b");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
|
||||
currentNodes["3"].receive({payload:"test"});
|
||||
currentNodes["3"].should.have.a.property("handled",2);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("rewires otherwise unmodified nodes on 'nodes' deploy",function(done) {
|
||||
var config = [{id:"1",type:"test",name:"a",wires:["2"]},{id:"2",type:"test",name:"b",wires:[[]]},{id:"3",type:"test",name:"c",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].wires[0].push("3");
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",0);
|
||||
|
||||
flow.applyConfig(newConfig,"nodes").then(function() {
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
stoppedNodes.should.not.have.a.property("1");
|
||||
stoppedNodes.should.not.have.a.property("2");
|
||||
stoppedNodes.should.not.have.a.property("3");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",2);
|
||||
currentNodes["2"].should.have.a.property("handled",2);
|
||||
currentNodes["3"].should.have.a.property("handled",1);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stops rewired but otherwise unmodified nodes on 'flows' deploy",function(done) {
|
||||
var config = [{id:"1",type:"test",name:"a",wires:["2"]},{id:"2",type:"test",name:"b",wires:[[]]},{id:"3",type:"test",name:"c",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].wires[0].push("3");
|
||||
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",0);
|
||||
|
||||
flow.applyConfig(newConfig,"flows").then(function() {
|
||||
currentNodes.should.have.a.property("1");
|
||||
currentNodes.should.have.a.property("2");
|
||||
currentNodes.should.have.a.property("3");
|
||||
|
||||
stoppedNodes.should.have.a.property("1");
|
||||
stoppedNodes.should.have.a.property("2");
|
||||
stoppedNodes.should.have.a.property("3");
|
||||
|
||||
currentNodes["1"].receive({payload:"test"});
|
||||
currentNodes["1"].should.have.a.property("handled",1);
|
||||
currentNodes["2"].should.have.a.property("handled",1);
|
||||
currentNodes["3"].should.have.a.property("handled",1);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleError',function() {
|
||||
var getType;
|
||||
var getNode;
|
||||
var credentialsClean;
|
||||
|
||||
var currentNodes = {};
|
||||
|
||||
var TestNode = function(n) {
|
||||
Node.call(this,n);
|
||||
var node = this;
|
||||
this.handled = [];
|
||||
currentNodes[node.id] = node;
|
||||
this.on('input',function(msg) {
|
||||
node.handled.push(msg);
|
||||
node.send(msg);
|
||||
});
|
||||
}
|
||||
util.inherits(TestNode,Node);
|
||||
|
||||
before(function() {
|
||||
getNode = sinon.stub(flows,"get",function(id) {
|
||||
return currentNodes[id];
|
||||
});
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return TestNode;
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(config){});
|
||||
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
credentialsClean.restore();
|
||||
getNode.restore();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
currentNodes = {};
|
||||
});
|
||||
|
||||
it("reports error to catch nodes on same z",function(done) {
|
||||
var config = [
|
||||
{id:"1",type:"test",z:"tab1",name:"a",wires:["2"]},
|
||||
{id:"2",type:"catch",z:"tab1",wires:[[]]},
|
||||
{id:"3",type:"catch",z:"tab2",wires:[[]]}
|
||||
];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
var msg = {a:1};
|
||||
flow.handleError(getNode(1),"test error",msg);
|
||||
var n2 = getNode(2);
|
||||
n2.handled.should.have.lengthOf(1);
|
||||
n2.handled[0].should.have.property("a",1);
|
||||
n2.handled[0].should.have.property("error");
|
||||
n2.handled[0].error.should.have.property("message","test error");
|
||||
n2.handled[0].error.should.have.property("source");
|
||||
n2.handled[0].error.source.should.have.property("id","1");
|
||||
n2.handled[0].error.source.should.have.property("type","test");
|
||||
getNode(3).handled.should.have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
|
||||
it("reports error with Error object",function(done) {
|
||||
var config = [
|
||||
{id:"1",type:"test",z:"tab1",name:"a",wires:["2"]},
|
||||
{id:"2",type:"catch",z:"tab1",wires:[[]]},
|
||||
{id:"3",type:"catch",z:"tab2",wires:[[]]}
|
||||
];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
var msg = {a:1,error:"existing"};
|
||||
flow.handleError(getNode(1),"test error",msg);
|
||||
var n2 = getNode(2);
|
||||
n2.handled.should.have.lengthOf(1);
|
||||
n2.handled[0].should.have.property("error");
|
||||
n2.handled[0].should.have.property("_error","existing");
|
||||
n2.handled[0].error.should.have.property("message","test error");
|
||||
n2.handled[0].error.should.have.property("source");
|
||||
n2.handled[0].error.source.should.have.property("id","1");
|
||||
n2.handled[0].error.source.should.have.property("type","test");
|
||||
getNode(3).handled.should.have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
|
||||
it("reports error with Error object",function(done) {
|
||||
var config = [
|
||||
{id:"1",type:"test",z:"tab1",name:"a",wires:["2"]},
|
||||
{id:"2",type:"catch",z:"tab1",wires:[[]]},
|
||||
{id:"3",type:"catch",z:"tab2",wires:[[]]}
|
||||
];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
flow.handleError(getNode(1),new Error("test error"));
|
||||
var n2 = getNode(2);
|
||||
n2.handled.should.have.lengthOf(1);
|
||||
n2.handled[0].should.have.property("error");
|
||||
n2.handled[0].error.should.have.property("message","Error: test error");
|
||||
n2.handled[0].error.should.have.property("source");
|
||||
n2.handled[0].error.source.should.have.property("id","1");
|
||||
n2.handled[0].error.source.should.have.property("type","test");
|
||||
getNode(3).handled.should.have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
|
||||
it('reports error in subflow to a local handler', function(done) {
|
||||
var config = [
|
||||
{id:"1",type:"test",z:"tab1",wires:[[]]},
|
||||
{id:"2",type:"subflow:sf1",z:"tab1",wires:[[]]},
|
||||
{id:"3",type:"catch",z:"tab1",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [],"out": []},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-catch",type:"catch",z:"sf1",wires:[]}
|
||||
];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
var instanceNode;
|
||||
var instanceCatch;
|
||||
for (var id in currentNodes) {
|
||||
if (currentNodes.hasOwnProperty(id)) {
|
||||
if (currentNodes[id].z == '2') {
|
||||
if (currentNodes[id].type == "catch") {
|
||||
instanceCatch = currentNodes[id];
|
||||
} else {
|
||||
instanceNode = currentNodes[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flow.handleError(instanceNode,new Error("test error"));
|
||||
var n3 = instanceCatch;
|
||||
n3.handled.should.have.lengthOf(1);
|
||||
n3.handled[0].should.have.property("error");
|
||||
n3.handled[0].error.should.have.property("message","Error: test error");
|
||||
n3.handled[0].error.should.have.property("source");
|
||||
n3.handled[0].error.source.should.have.property("id",instanceNode.id);
|
||||
n3.handled[0].error.source.should.have.property("type","test");
|
||||
getNode(3).handled.should.have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
it('reports error in subflow to a parent handler', function(done) {
|
||||
var config = [
|
||||
{id:"1",type:"test",z:"tab1",wires:[[]]},
|
||||
{id:"2",type:"subflow:sf1",z:"tab1",wires:[[]]},
|
||||
{id:"3",type:"catch",z:"tab1",wires:[]},
|
||||
{id:"4",type:"catch",z:"tab2",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [],"out": []},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
var flow = new Flow(config);
|
||||
flow.start();
|
||||
var instanceNode;
|
||||
|
||||
for (var id in currentNodes) {
|
||||
if (currentNodes.hasOwnProperty(id)) {
|
||||
if (currentNodes[id].z == '2') {
|
||||
instanceNode = currentNodes[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
flow.handleError(instanceNode,new Error("test error"));
|
||||
var n3 = getNode(3);
|
||||
n3.handled.should.have.lengthOf(1);
|
||||
n3.handled[0].should.have.property("error");
|
||||
n3.handled[0].error.should.have.property("message","Error: test error");
|
||||
n3.handled[0].error.should.have.property("source");
|
||||
n3.handled[0].error.source.should.have.property("id",instanceNode.id);
|
||||
n3.handled[0].error.source.should.have.property("type","test");
|
||||
getNode(4).handled.should.have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
415
test/red/nodes/flows/index_spec.js
Normal file
415
test/red/nodes/flows/index_spec.js
Normal file
@@ -0,0 +1,415 @@
|
||||
/**
|
||||
* Copyright 2014, 2015 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 when = require("when");
|
||||
var clone = require("clone");
|
||||
var flows = require("../../../../red/nodes/flows");
|
||||
var RedNode = require("../../../../red/nodes/Node");
|
||||
var RED = require("../../../../red/nodes");
|
||||
var events = require("../../../../red/events");
|
||||
var credentials = require("../../../../red/nodes/credentials");
|
||||
var typeRegistry = require("../../../../red/nodes/registry");
|
||||
var Flow = require("../../../../red/nodes/flows/Flow");
|
||||
|
||||
var settings = {
|
||||
available: function() { return false; }
|
||||
}
|
||||
|
||||
function loadFlows(testFlows, cb) {
|
||||
var storage = {
|
||||
getFlows: function() {
|
||||
return when.resolve(testFlows);
|
||||
},
|
||||
getCredentials: function() {
|
||||
return when.resolve({});
|
||||
}
|
||||
};
|
||||
RED.init(settings, storage);
|
||||
flows.load().then(function() {
|
||||
should.deepEqual(testFlows, flows.getFlows());
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
describe('flows/index', function() {
|
||||
|
||||
var eventsOn;
|
||||
var storage;
|
||||
var credentialsExtract;
|
||||
var credentialsSave;
|
||||
var credentialsClean;
|
||||
var credentialsLoad;
|
||||
|
||||
var flowCreate;
|
||||
var getType;
|
||||
|
||||
before(function() {
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return type!=='missing';
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
});
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
eventsOn = sinon.stub(events,"on",function(evt,handler) {});
|
||||
credentialsExtract = sinon.stub(credentials,"extract",function(conf) {
|
||||
delete conf.credentials;
|
||||
});
|
||||
credentialsSave = sinon.stub(credentials,"save",function() {
|
||||
return when.resolve();
|
||||
});
|
||||
credentialsClean = sinon.stub(credentials,"clean",function(conf) {
|
||||
return when.resolve();
|
||||
});
|
||||
credentialsLoad = sinon.stub(credentials,"load",function() {
|
||||
return when.resolve();
|
||||
});
|
||||
flowCreate = sinon.stub(Flow,"create",function(global, flow) {
|
||||
var id;
|
||||
if (typeof flow === 'undefined') {
|
||||
flow = global;
|
||||
id = '_GLOBAL_';
|
||||
} else {
|
||||
id = flow.id;
|
||||
}
|
||||
flowCreate.flows[id] = {
|
||||
start: sinon.spy(),
|
||||
update: sinon.spy(),
|
||||
stop: sinon.spy()
|
||||
}
|
||||
return flowCreate.flows[id];
|
||||
});
|
||||
flowCreate.flows = {};
|
||||
|
||||
storage = {
|
||||
saveFlows: function(conf) {
|
||||
storage.conf = conf;
|
||||
return when.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
eventsOn.restore();
|
||||
credentialsExtract.restore();
|
||||
credentialsSave.restore();
|
||||
credentialsClean.restore();
|
||||
credentialsLoad.restore();
|
||||
flowCreate.restore();
|
||||
});
|
||||
describe('#init',function() {
|
||||
it('registers the type-registered handler', function() {
|
||||
flows.init({},{});
|
||||
eventsOn.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setFlows', function() {
|
||||
it('sets the full flow', function(done) {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
flows.init({},storage);
|
||||
flows.setFlows(originalConfig).then(function() {
|
||||
credentialsExtract.called.should.be.false;
|
||||
credentialsClean.called.should.be.true;
|
||||
storage.hasOwnProperty('conf').should.be.true;
|
||||
flows.getFlows().should.eql(originalConfig);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
it('sets the full flow for type load', function(done) {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
flows.init({},storage);
|
||||
flows.setFlows(originalConfig,"load").then(function() {
|
||||
credentialsExtract.called.should.be.false;
|
||||
credentialsClean.called.should.be.true;
|
||||
// 'load' type does not trigger a save
|
||||
storage.hasOwnProperty('conf').should.be.false;
|
||||
flows.getFlows().should.eql(originalConfig);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('extracts credentials from the full flow', function(done) {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[],credentials:{}},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
flows.init({},storage);
|
||||
flows.setFlows(originalConfig).then(function() {
|
||||
credentialsExtract.called.should.be.true;
|
||||
credentialsClean.called.should.be.true;
|
||||
storage.hasOwnProperty('conf').should.be.true;
|
||||
var cleanedFlows = flows.getFlows();
|
||||
cleanedFlows.should.not.eql(originalConfig);
|
||||
cleanedFlows[0].credentials = {};
|
||||
cleanedFlows.should.eql(originalConfig);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#load', function() {
|
||||
it('loads the flow config', function(done) {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
storage.getFlows = function() {
|
||||
return when.resolve(originalConfig);
|
||||
}
|
||||
flows.init({},storage);
|
||||
flows.load().then(function() {
|
||||
credentialsExtract.called.should.be.false;
|
||||
credentialsLoad.called.should.be.true;
|
||||
credentialsClean.called.should.be.true;
|
||||
// 'load' type does not trigger a save
|
||||
storage.hasOwnProperty('conf').should.be.false;
|
||||
flows.getFlows().should.eql(originalConfig);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('#startFlows', function() {
|
||||
it.skip('starts the loaded config', function(done) {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
storage.getFlows = function() {
|
||||
return when.resolve(originalConfig);
|
||||
}
|
||||
flows.init({},storage);
|
||||
flows.load().then(function() {
|
||||
flows.startFlows();
|
||||
// TODO: PICK IT UP FROM HERE
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#get',function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#eachNode', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#stopFlows', function() {
|
||||
|
||||
});
|
||||
describe('#handleError', function() {
|
||||
|
||||
});
|
||||
describe('#handleStatus', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// afterEach(function(done) {
|
||||
// flows.stopFlows().then(function() {
|
||||
// loadFlows([],done);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('#load',function() {
|
||||
//
|
||||
// it('should load nothing when storage is empty',function(done) {
|
||||
// loadFlows([], done);
|
||||
// });
|
||||
//
|
||||
// it.skip('should load and start an empty tab flow',function(done) {
|
||||
// events.once('nodes-started', function() { done(); });
|
||||
// loadFlows([{"type":"tab","id":"tab1","label":"Sheet 1"}], function() {});
|
||||
// });
|
||||
//
|
||||
// it.skip('should load and start a registered node type', function(done) {
|
||||
// RED.registerType('debug', function() {});
|
||||
// var typeRegistryGet = sinon.stub(typeRegistry,"get",function(nt) {
|
||||
// return RedNode;
|
||||
// });
|
||||
// loadFlows([{"id":"n1","type":"debug"}], function() { });
|
||||
// events.once('nodes-started', function() {
|
||||
// typeRegistryGet.restore();
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it.skip('should load and start when node type is registered', function(done) {
|
||||
// var typeRegistryGet = sinon.stub(typeRegistry,"get");
|
||||
// typeRegistryGet.onCall(0).returns(null);
|
||||
// typeRegistryGet.returns(RedNode);
|
||||
// loadFlows([{"id":"n2","type":"inject"}], function() {
|
||||
// events.emit('type-registered','inject');
|
||||
// });
|
||||
// events.once('nodes-started', function() {
|
||||
// typeRegistryGet.restore();
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it.skip('should not instantiate nodes of an unused subflow', function(done) {
|
||||
// RED.registerType('abc', function() {});
|
||||
// var typeRegistryGet = sinon.stub(typeRegistry,"get",function(nt) {
|
||||
// return RedNode;
|
||||
// });
|
||||
// loadFlows([{"id":"n1","type":"subflow",inputs:[],outputs:[],wires:[]},
|
||||
// {"id":"n2","type":"abc","z":"n1",wires:[]}
|
||||
// ],function() { });
|
||||
// events.once('nodes-started', function() {
|
||||
// (flows.get("n2") == null).should.be.true;
|
||||
// var ncount = 0
|
||||
// flows.eachNode(function(n) {
|
||||
// ncount++;
|
||||
// });
|
||||
// ncount.should.equal(0);
|
||||
// typeRegistryGet.restore();
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// it.skip('should instantiate nodes of an used subflow with new IDs', function(done) {
|
||||
// RED.registerType('abc', function() {});
|
||||
// var typeRegistryGet = sinon.stub(typeRegistry,"get",function(nt) {
|
||||
// return RedNode;
|
||||
// });
|
||||
// loadFlows([{"id":"n1","type":"subflow",inputs:[],outputs:[]},
|
||||
// {"id":"n2","type":"abc","z":"n1","name":"def",wires:[]},
|
||||
// {"id":"n3","type":"subflow:n1"}
|
||||
// ], function() { });
|
||||
// events.once('nodes-started', function() {
|
||||
// // n2 should not get instantiated with that id
|
||||
// (flows.get("n2") == null).should.be.true;
|
||||
// var ncount = 0
|
||||
// var nodes = [];
|
||||
// flows.eachNode(function(n) {
|
||||
// nodes.push(n);
|
||||
// });
|
||||
// nodes.should.have.lengthOf(2);
|
||||
//
|
||||
// // Assume the nodes are instantiated in this order - not
|
||||
// // a requirement, but makes the test easier to write.
|
||||
// nodes[0].should.have.property("id","n3");
|
||||
// nodes[0].should.have.property("type","subflow:n1");
|
||||
// nodes[1].should.not.have.property("id","n2");
|
||||
// nodes[1].should.have.property("name","def");
|
||||
//
|
||||
// // TODO: verify instance wiring is correct
|
||||
// typeRegistryGet.restore();
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// describe.skip('#setFlows',function() {
|
||||
// var credentialsExtact;
|
||||
// var credentialsSave;
|
||||
// var stopFlows;
|
||||
// var startFlows;
|
||||
// var credentialsExtractNode;
|
||||
// beforeEach(function() {
|
||||
// credentialsExtact = sinon.stub(credentials,"extract",function(node) {credentialsExtractNode = clone(node);delete node.credentials;});
|
||||
// credentialsSave = sinon.stub(credentials,"save",function() { return when.resolve();});
|
||||
// stopFlows = sinon.stub(flows,"stopFlows",function() {return when.resolve();});
|
||||
// startFlows = sinon.stub(flows,"startFlows",function() {});
|
||||
// });
|
||||
// afterEach(function() {
|
||||
// credentialsExtact.restore();
|
||||
// credentialsSave.restore();
|
||||
// startFlows.restore();
|
||||
// stopFlows.restore();
|
||||
// });
|
||||
//
|
||||
// it('should extract credentials from nodes', function(done) {
|
||||
// var testFlow = [{"type":"testNode","credentials":{"a":1}},{"type":"testNode2"}];
|
||||
// var resultFlow = clone(testFlow);
|
||||
// var storage = { saveFlows: sinon.spy() };
|
||||
// flows.init({},storage);
|
||||
// flows.setFlows(testFlow,"full").then(function() {
|
||||
// try {
|
||||
// credentialsExtact.calledOnce.should.be.true;
|
||||
// // credential property stripped
|
||||
// testFlow.should.not.have.property("credentials");
|
||||
// credentialsExtractNode.should.eql(resultFlow[0]);
|
||||
// credentialsExtractNode.should.not.equal(resultFlow[0]);
|
||||
//
|
||||
// credentialsSave.calledOnce.should.be.true;
|
||||
//
|
||||
// storage.saveFlows.calledOnce.should.be.true;
|
||||
// storage.saveFlows.args[0][0].should.eql(testFlow);
|
||||
//
|
||||
// stopFlows.calledOnce.should.be.true;
|
||||
// startFlows.calledOnce.should.be.true;
|
||||
//
|
||||
// done();
|
||||
// } catch(err) {
|
||||
// done(err);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it('should apply diff on partial deployment', function(done) {
|
||||
// var testFlow = [{"type":"testNode"},{"type":"testNode2"}];
|
||||
// var testFlow2 = [{"type":"testNode3"},{"type":"testNode4"}];
|
||||
// var storage = { saveFlows: sinon.spy() };
|
||||
// flows.init({},storage);
|
||||
//
|
||||
// flows.setFlows(testFlow,"full").then(function() {
|
||||
// flows.setFlows(testFlow2,"nodes").then(function() {
|
||||
// try {
|
||||
// credentialsExtact.called.should.be.false;
|
||||
//
|
||||
// storage.saveFlows.calledTwice.should.be.true;
|
||||
// storage.saveFlows.args[1][0].should.eql(testFlow2);
|
||||
//
|
||||
// stopFlows.calledTwice.should.be.true;
|
||||
// startFlows.calledTwice.should.be.true;
|
||||
//
|
||||
// var configDiff = {
|
||||
// type: 'nodes',
|
||||
// stop: [],
|
||||
// rewire: [],
|
||||
// config: testFlow2
|
||||
// }
|
||||
// stopFlows.args[1][0].should.eql(configDiff);
|
||||
// startFlows.args[1][0].should.eql(configDiff);
|
||||
//
|
||||
// done();
|
||||
// } catch(err) {
|
||||
// done(err);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
//
|
||||
// });
|
594
test/red/nodes/flows/util_spec.js
Normal file
594
test/red/nodes/flows/util_spec.js
Normal file
@@ -0,0 +1,594 @@
|
||||
/**
|
||||
* Copyright 2015 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 when = require("when");
|
||||
var clone = require("clone");
|
||||
var flowUtil = require("../../../../red/nodes/flows/util");
|
||||
var typeRegistry = require("../../../../red/nodes/registry");
|
||||
var redUtil = require("../../../../red/util");
|
||||
|
||||
describe('flows/util', function() {
|
||||
var getType;
|
||||
|
||||
before(function() {
|
||||
getType = sinon.stub(typeRegistry,"get",function(type) {
|
||||
return type!=='missing';
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
getType.restore();
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('#diffNodes',function() {
|
||||
it('handles a null old node', function() {
|
||||
flowUtil.diffNodes(null,{}).should.be.true;
|
||||
});
|
||||
it('ignores x/y changes', function() {
|
||||
flowUtil.diffNodes({x:10,y:10},{x:20,y:10}).should.be.false;
|
||||
flowUtil.diffNodes({x:10,y:10},{x:10,y:20}).should.be.false;
|
||||
});
|
||||
it('ignores wiring changes', function() {
|
||||
flowUtil.diffNodes({wires:[]},{wires:[1,2,3]}).should.be.false;
|
||||
});
|
||||
it('spots existing property change - string', function() {
|
||||
flowUtil.diffNodes({a:"foo"},{a:"bar"}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - number', function() {
|
||||
flowUtil.diffNodes({a:0},{a:1}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - boolean', function() {
|
||||
flowUtil.diffNodes({a:true},{a:false}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - truthy', function() {
|
||||
flowUtil.diffNodes({a:true},{a:1}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - falsey', function() {
|
||||
flowUtil.diffNodes({a:false},{a:0}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - array', function() {
|
||||
flowUtil.diffNodes({a:[0,1,2]},{a:[0,2,3]}).should.be.true;
|
||||
});
|
||||
it('spots existing property change - object', function() {
|
||||
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{a:[0,2,3]}}).should.be.true;
|
||||
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{b:[0,1,2]}}).should.be.true;
|
||||
});
|
||||
it('spots added property', function() {
|
||||
flowUtil.diffNodes({a:"foo"},{a:"foo",b:"bar"}).should.be.true;
|
||||
});
|
||||
it('spots removed property', function() {
|
||||
flowUtil.diffNodes({a:"foo",b:"bar"},{a:"foo"}).should.be.true;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('#parseConfig',function() {
|
||||
|
||||
it('parses a single-tab flow', function() {
|
||||
var originalConfig = [
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t1",type:"tab"}
|
||||
];
|
||||
var parsedConfig = flowUtil.parseConfig(originalConfig);
|
||||
var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"missingTypes":[]};
|
||||
|
||||
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
|
||||
|
||||
});
|
||||
|
||||
it('parses a multi-tab flow', function() {
|
||||
var originalConfig = [
|
||||
{id:"t1",type:"tab"},
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||
{id:"t2",type:"tab"},
|
||||
{id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}
|
||||
];
|
||||
var parsedConfig = flowUtil.parseConfig(originalConfig);
|
||||
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"missingTypes":[]};
|
||||
|
||||
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
|
||||
});
|
||||
|
||||
it('parses a subflow flow', function() {
|
||||
var originalConfig = [
|
||||
{id:"t1",type:"tab"},
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"subflow:sf1",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
var parsedConfig = flowUtil.parseConfig(originalConfig);
|
||||
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"missingTypes":[]};
|
||||
|
||||
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
|
||||
});
|
||||
|
||||
it('parses a flow with a missing type', function() {
|
||||
var originalConfig = [
|
||||
{id:"t1",type:"tab"},
|
||||
{id:"t1-1",x:10,y:10,z:"t1",type:"sf1",wires:[]},
|
||||
{id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]},
|
||||
];
|
||||
var parsedConfig = flowUtil.parseConfig(originalConfig);
|
||||
parsedConfig.missingTypes.should.eql(['missing']);
|
||||
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]}}}},"missingTypes":["missing"]};
|
||||
|
||||
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('#diffConfigs', function() {
|
||||
|
||||
it('handles an identical configuration', function() {
|
||||
var config = [{id:"123",type:"test",foo:"a",wires:[]}];
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(clone(config));
|
||||
var changedConfig = flowUtil.parseConfig(clone(config));
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.should.have.length(0);
|
||||
});
|
||||
|
||||
it('identifies nodes with changed properties, including downstream linked', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[[1]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[0].foo = "b";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.eql(["1"]);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.should.eql(["2"]);
|
||||
|
||||
});
|
||||
it('identifies nodes with changed properties, including upstream linked', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].bar = "c";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.eql(["2"]);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.should.eql(["1"]);
|
||||
});
|
||||
|
||||
it('identifies nodes with changed credentials, including downstream linked', function() {
|
||||
var config = [{id:"1",type:"test",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[0].credentials = {};
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.eql(["1"]);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.should.eql(["2"]);
|
||||
});
|
||||
|
||||
it('identifies nodes with changed wiring', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].wires[0][0] = "3";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.eql(["2"]);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('identifies nodes with changed wiring - second connection added', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].wires[0].push("1");
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.eql(["2"]);
|
||||
diffResult.linked.sort().should.eql(["1"]);
|
||||
});
|
||||
|
||||
it('identifies nodes with changed wiring - node connected', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[[]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig[1].wires.push("3");
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.eql(["2"]);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('identifies new nodes', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig.push({id:"2",type:"test",bar:"b",wires:[["1"]]});
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.eql(["2"]);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1"]);
|
||||
});
|
||||
|
||||
it('identifies deleted nodes', function() {
|
||||
var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}];
|
||||
var newConfig = clone(config);
|
||||
newConfig.splice(1,1);
|
||||
newConfig[0].wires = [];
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.should.have.length(0);
|
||||
diffResult.removed.should.eql(["2"]);
|
||||
diffResult.rewired.should.eql(["1"]);
|
||||
diffResult.linked.sort().should.eql(["3"]);
|
||||
});
|
||||
|
||||
it('identifies config nodes changes', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",foo:"configNode",wires:[["2"]]},
|
||||
{id:"2",type:"test",bar:"b",wires:[["3"]]},
|
||||
{id:"3",type:"test",foo:"a",wires:[]},
|
||||
{id:"configNode",type:"testConfig"}
|
||||
];
|
||||
var newConfig = clone(config);
|
||||
newConfig[3].foo = "bar";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(["1","configNode"]);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["2","3"]);
|
||||
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal property change', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",type:"test",foo:"a",wires:[]},
|
||||
{id:"4",type:"subflow:sf1",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[4].foo = "b";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', '4', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal wiring change', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[4].wires = [["sf1-2"]];
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal node add', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig.push({id:"sf1-3",z:"sf1",type:"test",wires:[]});
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal node delete', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig.splice(5,1);
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal subflow wiring change - input removed', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[3].in[0].wires = [];
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal subflow wiring change - input added', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[3].in[0].wires.push({"id":"sf1-2"});
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal subflow wiring change - output added', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[3].out[0].wires.push({"id":"sf1-2","port":0});
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal subflow wiring change - output removed', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[3].out[0].wires = [];
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for a global config node change', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",prop:"configNode",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"test",wires:[]},
|
||||
{id:"configNode",a:"foo",type:"test",wires:[]}
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[6].a = "bar";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', "configNode", 'sf1']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
|
||||
it('marks a parent subflow as changed for an internal subflow instance change', function() {
|
||||
var config = [
|
||||
{id:"1",type:"test",wires:[["2"]]},
|
||||
{id:"2",type:"subflow:sf1",wires:[["3"]]},
|
||||
{id:"3",type:"test",wires:[]},
|
||||
{id:"sf1",type:"subflow"},
|
||||
{id:"sf2",type:"subflow"},
|
||||
{id:"sf1-1",z:"sf1",type:"test",wires:[]},
|
||||
{id:"sf1-2",z:"sf1",type:"subflow:sf2",wires:[]},
|
||||
{id:"sf2-1",z:"sf2",type:"test",wires:[]},
|
||||
{id:"sf2-2",z:"sf2",type:"test",wires:[]},
|
||||
];
|
||||
|
||||
var newConfig = clone(config);
|
||||
newConfig[8].a = "bar";
|
||||
|
||||
var originalConfig = flowUtil.parseConfig(config);
|
||||
var changedConfig = flowUtil.parseConfig(newConfig);
|
||||
|
||||
originalConfig.missingTypes.should.have.length(0);
|
||||
|
||||
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
|
||||
diffResult.added.should.have.length(0);
|
||||
diffResult.changed.sort().should.eql(['2', 'sf1', 'sf2']);
|
||||
diffResult.removed.should.have.length(0);
|
||||
diffResult.rewired.should.have.length(0);
|
||||
diffResult.linked.sort().should.eql(["1","3"]);
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user