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:
Nick O'Leary
2015-10-11 20:37:11 +01:00
parent 20b321f928
commit ec25191c98
14 changed files with 2275 additions and 2003 deletions

View 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);
// }
// });
// });
// });
//
//
// });

View 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"]);
});
});
});

View File

@@ -1,230 +0,0 @@
/**
* 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 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/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', 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('should load and start an empty tab flow',function(done) {
loadFlows([{"type":"tab","id":"tab1","label":"Sheet 1"}], function() {});
events.once('nodes-started', function() { done(); });
});
it('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('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('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('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('#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);
}
});
});
});
});
});

View File

@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var should = require("should");
var deprecated = require("../../../red/nodes/deprecated.js");
var deprecated = require("../../../../red/nodes/registry/deprecated.js");
describe('deprecated', function() {
it('should return info on a node',function() {

View File

@@ -30,11 +30,11 @@ var log = require("../../red/log");
describe("red/server", function() {
var commsMessages = [];
var commsPublish;
beforeEach(function() {
commsMessages = [];
});
before(function() {
commsPublish = sinon.stub(comms,"publish", function(topic,msg,retained) {
commsMessages.push({topic:topic,msg:msg,retained:retained});
@@ -43,22 +43,22 @@ describe("red/server", function() {
after(function() {
commsPublish.restore();
});
it("initialises components", function() {
var commsInit = sinon.stub(comms,"init",function() {});
var dummyServer = {};
server.init(dummyServer,{testSettings: true, httpAdminRoot:"/", load:function() { return when.resolve();}});
commsInit.called.should.be.true;
should.exist(server.app);
should.exist(server.nodeApp);
server.server.should.equal(dummyServer);
commsInit.restore();
});
describe("start",function() {
var commsInit;
var storageInit;
@@ -73,8 +73,9 @@ describe("red/server", function() {
var redNodesCleanModuleList;
var redNodesGetNodeList;
var redNodesLoadFlows;
var redNodesStartFlows;
var commsStart;
beforeEach(function() {
commsInit = sinon.stub(comms,"init",function() {});
storageInit = sinon.stub(storage,"init",function(settings) {return when.resolve();});
@@ -86,7 +87,8 @@ describe("red/server", function() {
redNodesInit = sinon.stub(redNodes,"init", function() {});
redNodesLoad = sinon.stub(redNodes,"load", function() {return when.resolve()});
redNodesCleanModuleList = sinon.stub(redNodes,"cleanModuleList",function(){});
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {});
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {return when.resolve()});
redNodesStartFlows = sinon.stub(redNodes,"startFlows",function() {});
commsStart = sinon.stub(comms,"start",function(){});
});
afterEach(function() {
@@ -99,9 +101,10 @@ describe("red/server", function() {
logLog.restore();
redNodesInit.restore();
redNodesLoad.restore();
redNodesGetNodeList.restore();
redNodesGetNodeList.restore();
redNodesCleanModuleList.restore();
redNodesLoadFlows.restore();
redNodesStartFlows.restore();
commsStart.restore();
});
it("reports errored/missing modules",function(done) {
@@ -120,7 +123,7 @@ describe("red/server", function() {
redNodesLoad.calledOnce.should.be.true;
commsStart.calledOnce.should.be.true;
redNodesLoadFlows.calledOnce.should.be.true;
logWarn.calledWithMatch("Failed to register 1 node type");
logWarn.calledWithMatch("Missing node modules");
logWarn.calledWithMatch(" - module: typeA, typeB");
@@ -168,7 +171,7 @@ describe("red/server", function() {
});
server.init({},{testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return when.resolve();}});
server.start().then(function() {
try {
apiInit.calledOnce.should.be.true;
logWarn.neverCalledWithMatch("Failed to register 1 node type");
@@ -179,7 +182,7 @@ describe("red/server", function() {
}
});
});
it("reports runtime metrics",function(done) {
var commsStop = sinon.stub(comms,"stop",function() {} );
var stopFlows = sinon.stub(redNodes,"stopFlows",function() {} );
@@ -208,8 +211,8 @@ describe("red/server", function() {
}
},500);
});
});
});
it("doesn't init api if httpAdminRoot set to false",function(done) {
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function() {return []});
server.init({},{testSettings: true, httpAdminRoot:false, load:function() { return when.resolve();}});
@@ -225,20 +228,20 @@ describe("red/server", function() {
});
});
});
it("stops components", function() {
var commsStop = sinon.stub(comms,"stop",function() {} );
var stopFlows = sinon.stub(redNodes,"stopFlows",function() {} );
server.stop();
commsStop.called.should.be.true;
stopFlows.called.should.be.true;
commsStop.restore();
stopFlows.restore();
});
it("reports added modules", function() {
var nodes = {nodes:[
{types:["a"]},
@@ -246,13 +249,13 @@ describe("red/server", function() {
{types:["c"],err:"error"}
]};
var result = server.reportAddedModules(nodes);
result.should.equal(nodes);
commsMessages.should.have.length(1);
commsMessages[0].topic.should.equal("node/added");
commsMessages[0].msg.should.eql(nodes.nodes);
});
it("reports removed modules", function() {
var nodes = [
{types:["a"]},
@@ -260,13 +263,13 @@ describe("red/server", function() {
{types:["c"],err:"error"}
];
var result = server.reportRemovedModules(nodes);
result.should.equal(nodes);
commsMessages.should.have.length(1);
commsMessages[0].topic.should.equal("node/removed");
commsMessages[0].msg.should.eql(nodes);
});
describe("installs module", function() {
it("rejects invalid module names", function(done) {
var promises = [];
@@ -278,12 +281,12 @@ describe("red/server", function() {
done();
});
});
it("rejects when npm returns a 404", function(done) {
var exec = sinon.stub(child_process,"exec",function(cmd,opt,cb) {
cb(new Error(),""," 404 this_wont_exist");
});
server.installModule("this_wont_exist").otherwise(function(err) {
err.code.should.be.eql(404);
done();
@@ -295,7 +298,7 @@ describe("red/server", function() {
var exec = sinon.stub(child_process,"exec",function(cmd,opt,cb) {
cb(new Error("test_error"),"","");
});
server.installModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).otherwise(function(err) {
@@ -312,7 +315,7 @@ describe("red/server", function() {
var addModule = sinon.stub(redNodes,"addModule",function(md) {
return when.resolve(nodeInfo);
});
server.installModule("this_wont_exist").then(function(info) {
info.should.eql(nodeInfo);
commsMessages.should.have.length(1);
@@ -347,7 +350,7 @@ describe("red/server", function() {
var exec = sinon.stub(child_process,"exec",function(cmd,opt,cb) {
cb(new Error("test_error"),"","");
});
server.uninstallModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).otherwise(function(err) {
@@ -366,7 +369,7 @@ describe("red/server", function() {
cb(null,"","");
});
var exists = sinon.stub(fs,"existsSync", function(fn) { return true; });
server.uninstallModule("this_wont_exist").then(function(info) {
info.should.eql(nodeInfo);
commsMessages.should.have.length(1);
@@ -382,5 +385,5 @@ describe("red/server", function() {
});
});
});
});