From b30fc917200ed5b3a8c019ef8b31f7e873c522c7 Mon Sep 17 00:00:00 2001 From: aakashsharma7 Date: Mon, 20 May 2024 17:20:32 +0530 Subject: [PATCH] improvement in flow API --- .../@node-red/runtime/lib/flows/index_spec.js | 1499 ++++++++++++----- 1 file changed, 1124 insertions(+), 375 deletions(-) diff --git a/test/unit/@node-red/runtime/lib/flows/index_spec.js b/test/unit/@node-red/runtime/lib/flows/index_spec.js index a07ab7b42..82018d58a 100644 --- a/test/unit/@node-red/runtime/lib/flows/index_spec.js +++ b/test/unit/@node-red/runtime/lib/flows/index_spec.js @@ -1,18 +1,965 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * 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. - **/ +// /** +// * Copyright JS Foundation and other contributors, http://js.foundation +// * +// * 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 NR_TEST_UTILS = require("nr-test-utils"); + +// var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); +// var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); +// var RED = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes"); +// var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); +// var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); +// var typeRegistry = NR_TEST_UTILS.require("@node-red/registry") +// var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); + +// describe('flows/index', function() { + +// var storage; +// var eventsOn; +// var credentialsClean; +// var credentialsLoad; +// var credentialsAdd; + +// var flowCreate; +// var getType; +// var checkFlowDependencies; + +// var mockLog = { +// log: sinon.stub(), +// debug: sinon.stub(), +// trace: sinon.stub(), +// warn: sinon.stub(), +// info: sinon.stub(), +// metric: sinon.stub(), +// _: function() { return "abc"} +// } + + +// before(function() { +// getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { +// return type.indexOf('missing') === -1; +// }); +// checkFlowDependencies = sinon.stub(typeRegistry, "checkFlowDependencies").callsFake(async function(flow) { +// if (flow[0].id === "node-with-missing-modules") { +// throw new Error("Missing module"); +// } +// }); +// }); + +// after(function() { +// getType.restore(); +// checkFlowDependencies.restore(); +// }); + + +// beforeEach(function() { +// eventsOn = sinon.spy(events,"on"); +// credentialsClean = sinon.stub(credentials,"clean").callsFake(function(conf) { +// conf.forEach(function(n) { +// delete n.credentials; +// }); +// return Promise.resolve(); +// }); +// credentialsLoad = sinon.stub(credentials,"load").callsFake(function(creds) { +// if (creds && creds.hasOwnProperty("$") && creds['$'] === "fail") { +// return Promise.reject("creds error"); +// } +// return Promise.resolve(); +// }); +// credentialsAdd = sinon.stub(credentials,"add").callsFake(async function(id, conf){}) +// flowCreate = sinon.stub(Flow,"create").callsFake(function(parent, global, flow) { +// var id; +// if (typeof flow === 'undefined') { +// flow = global; +// id = '_GLOBAL_'; +// } else { +// id = flow.id; +// } +// flowCreate.flows[id] = { +// flow: flow, +// global: global, +// start: sinon.spy(async() => {}), +// update: sinon.spy(), +// stop: sinon.spy(), +// getActiveNodes: function() { +// return flow.nodes||{}; +// }, +// handleError: sinon.spy(), +// handleStatus: sinon.spy() + +// } +// return flowCreate.flows[id]; +// }); +// flowCreate.flows = {}; + +// storage = { +// saveFlows: function(conf) { +// storage.conf = conf; +// return Promise.resolve(); +// } +// } +// }); + +// afterEach(function(done) { +// eventsOn.restore(); +// credentialsClean.restore(); +// credentialsLoad.restore(); +// credentialsAdd.restore(); +// flowCreate.restore(); + +// flows.stopFlows().then(done); + +// }); +// // 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({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig).then(function() { +// credentialsClean.called.should.be.true(); +// storage.hasOwnProperty('conf').should.be.true(); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }); + +// }); +// it('loads 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"} +// ]; +// var loadStorage = { +// saveFlows: function(conf) { +// loadStorage.conf = conf; +// return Promise.resolve(456); +// }, +// getFlows: function() { +// return Promise.resolve({flows:originalConfig,rev:123}) +// } +// } +// flows.init({log:mockLog, settings:{},storage:loadStorage}); +// flows.setFlows(originalConfig,"load").then(function() { +// credentialsClean.called.should.be.false(); +// // 'load' type does not trigger a save +// loadStorage.hasOwnProperty('conf').should.be.false(); +// flows.getFlows().flows.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:{"a":1}}, +// {id:"t1",type:"tab"} +// ]; +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig).then(function() { +// credentialsClean.called.should.be.true(); +// storage.hasOwnProperty('conf').should.be.true(); +// var cleanedFlows = flows.getFlows(); +// storage.conf.flows.should.eql(cleanedFlows.flows); +// cleanedFlows.flows.should.not.eql(originalConfig); +// cleanedFlows.flows[0].credentials = {"a":1}; +// cleanedFlows.flows.should.eql(originalConfig); +// done(); +// }); +// }); + +// it('sets the full flow including credentials', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// var credentials = {"t1-1":{"a":1}}; + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig,credentials).then(function() { +// credentialsClean.called.should.be.true(); +// credentialsAdd.called.should.be.true(); +// credentialsAdd.lastCall.args[0].should.eql("t1-1"); +// credentialsAdd.lastCall.args[1].should.eql({"a":1}); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }); +// }); + +// it('updates existing flows with partial deployment - nodes', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// var newConfig = clone(originalConfig); +// newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); +// newConfig.push({id:"t2",type:"tab"}); +// newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// events.once('flows:started',function() { +// events.once('flows:started', function() { +// try { +// flows.getFlows().flows.should.eql(newConfig); +// flowCreate.flows['t1'].update.called.should.be.true(); +// flowCreate.flows['t2'].start.called.should.be.true(); +// flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); +// done(); +// } catch(err) { +// done(err) +// } +// }) +// flows.setFlows(newConfig,"nodes") +// }); + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// flows.startFlows(); +// }); +// }); + +// it('updates existing flows with partial deployment - flows', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// var newConfig = clone(originalConfig); +// newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); +// newConfig.push({id:"t2",type:"tab"}); +// newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } + +// events.once('flows:started',function() { +// events.once('flows:started',function() { +// flows.getFlows().flows.should.eql(newConfig); +// flowCreate.flows['t1'].update.called.should.be.true(); +// flowCreate.flows['t2'].start.called.should.be.true(); +// flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); +// flows.stopFlows().then(done); +// }) +// flows.setFlows(newConfig,"nodes") +// }); + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// flows.startFlows(); +// }); +// }); + +// it('returns error if it cannot decrypt credentials', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// var credentials = {"$":"fail"}; + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig,credentials).then(function() { +// done("Unexpected success when credentials couldn't be decrypted") +// }).catch(function(err) { +// 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 Promise.resolve({flows:originalConfig}); +// } +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// credentialsLoad.called.should.be.true(); +// // 'load' type does not trigger a save +// storage.hasOwnProperty('conf').should.be.false(); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }); +// }); +// }); + +// describe('#startFlows', function() { +// it('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 Promise.resolve({flows:originalConfig}); +// } + +// events.once('flows:started',function() { +// Object.keys(flowCreate.flows).should.eql(['_GLOBAL_','t1']); +// done(); +// }); + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// return flows.startFlows(); +// }); +// }); +// it('does not start if nodes missing', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// return flows.startFlows(); +// }).then(() => { +// try { +// flowCreate.called.should.be.false(); +// done(); +// } catch(err) { +// done(err); +// } +// }); +// }); + +// it('starts when missing nodes registered', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, +// {id:"t1-3",x:10,y:10,z:"t1",type:"missing2",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// return flows.startFlows(); +// }).then(() => { +// flowCreate.called.should.be.false(); +// events.emit("type-registered","missing"); +// setTimeout(function() { +// flowCreate.called.should.be.false(); +// events.emit("type-registered","missing2"); +// setTimeout(function() { +// flowCreate.called.should.be.true(); +// done(); +// },10); +// },10); +// }); +// }); + +// it('does not start if external modules missing', function(done) { +// var originalConfig = [ +// {id:"node-with-missing-modules",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; + +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// var receivedEvent = null; +// var handleEvent = function(payload) { +// receivedEvent = payload; +// } + +// events.on("runtime-event",handleEvent); + +// //{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});" + + +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(flows.startFlows).then(() => { +// events.removeListener("runtime-event",handleEvent); +// try { +// flowCreate.called.should.be.false(); +// receivedEvent.should.have.property('id','runtime-state'); +// receivedEvent.should.have.property('payload', { +// state: 'stop', +// error: 'missing-modules', +// type: 'warning', +// text: 'notification.warnings.missing-modules', +// modules: [] +// }); + +// done(); +// }catch(err) { +// done(err) +// } +// }); +// }); + +// }); + +// describe.skip('#get',function() { + +// }); + +// describe('#eachNode', function() { +// it('iterates the flow nodes', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// var c = 0; +// flows.eachNode(function(node) { +// c++ +// }) +// c.should.equal(2); +// done(); +// }); +// }); +// }); + +// describe('#stopFlows', function() { + +// }); +// // describe('#handleError', function() { +// // it('passes error to correct flow', function(done) { +// // var originalConfig = [ +// // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// // {id:"t1",type:"tab"} +// // ]; +// // storage.getFlows = function() { +// // return Promise.resolve({flows:originalConfig}); +// // } +// // +// // events.once('flows:started',function() { +// // flows.handleError(originalConfig[0],"message",{}); +// // flowCreate.flows['t1'].handleError.called.should.be.true(); +// // done(); +// // }); +// // +// // flows.init({log:mockLog, settings:{},storage:storage}); +// // flows.load().then(function() { +// // flows.startFlows(); +// // }); +// // }); +// // it('passes error to flows that use the originating global config', function(done) { +// // var originalConfig = [ +// // {id:"configNode",type:"test"}, +// // {id:"t1",type:"tab"}, +// // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, +// // {id:"t2",type:"tab"}, +// // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, +// // {id:"t3",type:"tab"}, +// // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} +// // ]; +// // storage.getFlows = function() { +// // return Promise.resolve({flows:originalConfig}); +// // } +// // +// // events.once('flows:started',function() { +// // flows.handleError(originalConfig[0],"message",{}); +// // try { +// // flowCreate.flows['t1'].handleError.called.should.be.true(); +// // flowCreate.flows['t2'].handleError.called.should.be.false(); +// // flowCreate.flows['t3'].handleError.called.should.be.true(); +// // done(); +// // } catch(err) { +// // done(err); +// // } +// // }); +// // +// // flows.init({log:mockLog, settings:{},storage:storage}); +// // flows.load().then(function() { +// // flows.startFlows(); +// // }); +// // }); +// // }); +// // describe('#handleStatus', function() { +// // it('passes status to correct flow', function(done) { +// // var originalConfig = [ +// // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// // {id:"t1",type:"tab"} +// // ]; +// // storage.getFlows = function() { +// // return Promise.resolve({flows:originalConfig}); +// // } +// // +// // events.once('flows:started',function() { +// // flows.handleStatus(originalConfig[0],"message"); +// // flowCreate.flows['t1'].handleStatus.called.should.be.true(); +// // done(); +// // }); +// // +// // flows.init({log:mockLog, settings:{},storage:storage}); +// // flows.load().then(function() { +// // flows.startFlows(); +// // }); +// // }); +// // +// // it('passes status to flows that use the originating global config', function(done) { +// // var originalConfig = [ +// // {id:"configNode",type:"test"}, +// // {id:"t1",type:"tab"}, +// // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, +// // {id:"t2",type:"tab"}, +// // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, +// // {id:"t3",type:"tab"}, +// // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} +// // ]; +// // storage.getFlows = function() { +// // return Promise.resolve({flows:originalConfig}); +// // } +// // +// // events.once('flows:started',function() { +// // flows.handleStatus(originalConfig[0],"message"); +// // try { +// // flowCreate.flows['t1'].handleStatus.called.should.be.true(); +// // flowCreate.flows['t2'].handleStatus.called.should.be.false(); +// // flowCreate.flows['t3'].handleStatus.called.should.be.true(); +// // done(); +// // } catch(err) { +// // done(err); +// // } +// // }); +// // +// // flows.init({log:mockLog, settings:{},storage:storage}); +// // flows.load().then(function() { +// // flows.startFlows(); +// // }); +// // }); +// // }); + +// describe('#checkTypeInUse', function() { + +// before(function() { +// sinon.stub(typeRegistry,"getNodeInfo").callsFake(function(id) { +// if (id === 'unused-module') { +// return {types:['one','two','three']} +// } else { +// return {types:['one','test','three']} +// } +// }); +// }); + +// after(function() { +// typeRegistry.getNodeInfo.restore(); +// }); + +// it('returns cleanly if type not is use', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig).then(function() { +// flows.checkTypeInUse("unused-module"); +// done(); +// }); +// }); +// it('throws error if type is in use', function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.setFlows(originalConfig).then(function() { +// /*jshint immed: false */ +// try { +// flows.checkTypeInUse("used-module"); +// done("type_in_use error not thrown"); +// } catch(err) { +// err.code.should.eql("type_in_use"); +// done(); +// } +// }); +// }); +// }); + +// describe('#addFlow', function() { +// it("rejects duplicate node id",function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// flows.addFlow({ +// label:'new flow', +// nodes:[ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]} +// ] +// }).then(function() { +// done(new Error('failed to reject duplicate node id')); +// }).catch(function(err) { +// done(); +// }) +// }); + +// }); + +// it("addFlow",function(done) { +// var originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = function() { +// return Promise.resolve({flows:originalConfig}); +// } +// storage.setFlows = function() { +// return Promise.resolve(); +// } +// flows.init({log:mockLog, settings:{},storage:storage}); +// flows.load().then(function() { +// return flows.startFlows(); +// }).then(function() { +// flows.addFlow({ +// label:'new flow', +// nodes:[ +// {id:"t2-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t2-2",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t2-3",z:"t1",type:"test"} +// ] +// }).then(function(id) { +// flows.getFlows().flows.should.have.lengthOf(6); +// var createdFlows = Object.keys(flowCreate.flows); +// createdFlows.should.have.lengthOf(3); +// createdFlows[2].should.eql(id); +// done(); +// }).catch(function(err) { +// done(err); +// }) +// }); + +// }); +// }) +// describe('#updateFlow', function() { +// it.skip("updateFlow"); +// }) +// describe('#removeFlow', function() { +// it.skip("removeFlow"); +// }) +// describe('#disableFlow', function() { +// it.skip("disableFlow"); +// }) +// describe('#enableFlow', function() { +// it.skip("enableFlow"); +// }) +// }); + + +//***************************************************************************************************************** */ + +//MY code +// const should = require("should"); +// const sinon = require("sinon"); +// const clone = require("clone"); +// const NR_TEST_UTILS = require("nr-test-utils"); + +// const flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); +// const Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); +// const events = NR_TEST_UTILS.require("@node-red/util/lib/events"); +// const credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); +// const typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); + +// describe('flows/index', function() { +// let storage; +// let eventsOn; +// let credentialsClean; +// let credentialsLoad; +// let credentialsAdd; +// let flowCreate; +// let getType; +// let checkFlowDependencies; +// let mockLog; + +// before(function() { +// getType = sinon.stub(typeRegistry, "get").callsFake(type => type.indexOf('missing') === -1); +// checkFlowDependencies = sinon.stub(typeRegistry, "checkFlowDependencies").callsFake(async flow => { +// if (flow[0].id === "node-with-missing-modules") { +// throw new Error("Missing module"); +// } +// }); +// }); + +// after(function() { +// getType.restore(); +// checkFlowDependencies.restore(); +// }); + +// beforeEach(function() { +// eventsOn = sinon.spy(events, "on"); +// credentialsClean = sinon.stub(credentials, "clean").callsFake(conf => { +// conf.forEach(n => delete n.credentials); +// return Promise.resolve(); +// }); +// credentialsLoad = sinon.stub(credentials, "load").callsFake(creds => { +// if (creds && creds.hasOwnProperty("$") && creds['$'] === "fail") { +// return Promise.reject("creds error"); +// } +// return Promise.resolve(); +// }); +// credentialsAdd = sinon.stub(credentials, "add").callsFake(async (id, conf) => {}); + +// flowCreate = sinon.stub(Flow, "create").callsFake((parent, global, flow) => { +// let id = flow?.id ?? '_GLOBAL_'; +// flowCreate.flows[id] = { +// flow, +// global, +// start: sinon.spy(async () => {}), +// update: sinon.spy(), +// stop: sinon.spy(), +// getActiveNodes: () => flow.nodes || {}, +// handleError: sinon.spy(), +// handleStatus: sinon.spy() +// }; +// return flowCreate.flows[id]; +// }); +// flowCreate.flows = {}; + +// storage = { +// saveFlows: conf => { +// storage.conf = conf; +// return Promise.resolve(); +// } +// }; + +// mockLog = { +// log: sinon.stub(), +// debug: sinon.stub(), +// trace: sinon.stub(), +// warn: sinon.stub(), +// info: sinon.stub(), +// metric: sinon.stub(), +// _: () => "abc" +// }; +// }); + +// afterEach(function(done) { +// eventsOn.restore(); +// credentialsClean.restore(); +// credentialsLoad.restore(); +// credentialsAdd.restore(); +// flowCreate.restore(); +// flows.stopFlows().then(done); +// }); + +// describe('#setFlows', function() { +// it('sets the full flow', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// flows.init({log:mockLog, settings:{},storage}); +// flows.setFlows(originalConfig).then(() => { +// credentialsClean.called.should.be.true(); +// storage.should.have.property('conf'); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }).catch(done); +// }); + +// it('loads the full flow for type load', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// const loadStorage = { +// saveFlows: function(conf) { +// loadStorage.conf = conf; +// return Promise.resolve(456); +// }, +// getFlows: function() { +// return Promise.resolve({flows:originalConfig, rev:123}); +// } +// }; +// flows.init({log:mockLog, settings:{},storage:loadStorage}); +// flows.setFlows(originalConfig, "load").then(() => { +// credentialsClean.called.should.be.false(); +// loadStorage.should.not.have.property('conf'); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }).catch(done); +// }); + +// it('extracts credentials from the full flow', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[], credentials:{"a":1}}, +// {id:"t1",type:"tab"} +// ]; +// flows.init({log:mockLog, settings:{},storage}); +// flows.setFlows(originalConfig).then(() => { +// credentialsClean.called.should.be.true(); +// storage.should.have.property('conf'); +// const cleanedFlows = flows.getFlows(); +// storage.conf.flows.should.eql(cleanedFlows.flows); +// cleanedFlows.flows.should.not.eql(originalConfig); +// cleanedFlows.flows[0].credentials = {"a":1}; +// cleanedFlows.flows.should.eql(originalConfig); +// done(); +// }).catch(done); +// }); + +// it('sets the full flow including credentials', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// const credentials = {"t1-1": {"a":1}}; +// flows.init({log:mockLog, settings:{},storage}); +// flows.setFlows(originalConfig, credentials).then(() => { +// credentialsClean.called.should.be.true(); +// credentialsAdd.called.should.be.true(); +// credentialsAdd.lastCall.args[0].should.eql("t1-1"); +// credentialsAdd.lastCall.args[1].should.eql({"a":1}); +// flows.getFlows().flows.should.eql(originalConfig); +// done(); +// }).catch(done); +// }); + +// it('updates existing flows with partial deployment - nodes', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// const newConfig = clone(originalConfig); +// newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); +// newConfig.push({id:"t2",type:"tab"}); +// newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); +// storage.getFlows = () => Promise.resolve({flows:originalConfig}); + +// events.once('flows:started', () => { +// events.once('flows:started', () => { +// try { +// flows.getFlows().flows.should.eql(newConfig); +// flowCreate.flows['t1'].update.called.should.be.true(); +// flowCreate.flows['t2'].start.called.should.be.true(); +// flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); +// done(); +// } catch(err) { +// done(err); +// } +// }); +// flows.setFlows(newConfig, "nodes"); +// }); + +// flows.init({log:mockLog, settings:{},storage}); +// flows.load().then(flows.startFlows).catch(done); +// }); + +// it('updates existing flows with partial deployment - flows', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// const newConfig = clone(originalConfig); +// newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); +// newConfig.push({id:"t2",type:"tab"}); +// newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); +// storage.getFlows = () => Promise.resolve({flows:originalConfig}); + +// events.once('flows:started', () => { +// events.once('flows:started', () => { +// flows.getFlows().flows.should.eql(newConfig); +// flowCreate.flows['t1'].update.called.should.be.true(); +// flowCreate.flows['t2'].start.called.should.be.true(); +// flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); +// flows.stopFlows().then(done).catch(done); +// }); +// flows.setFlows(newConfig, "flows"); +// }); + +// flows.init({log:mockLog, settings:{},storage}); +// flows.load().then(flows.startFlows).catch(done); +// }); + +// it('retains missing flows with partial deployment - nodes', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"}, +// {id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]} +// ]; +// const newConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = () => Promise.resolve({flows:originalConfig}); + +// events.once('flows:started', () => { +// events.once('flows:started', () => { +// flows.getFlows().flows.should.eql(originalConfig); +// flowCreate.flows['t1'].update.called.should.be.true(); +// done(); +// }); +// flows.setFlows(newConfig, "nodes"); +// }); + +// flows.init({log:mockLog, settings:{},storage}); +// flows.load().then(flows.startFlows).catch(done); +// }); +// }); + +// describe('#startFlows', function() { +// it('starts empty flow configuration', function(done) { +// flows.init({log:mockLog, settings:{},storage}); +// flows.startFlows().then(() => { +// flows.getFlows().flows.should.eql([]); +// done(); +// }).catch(done); +// }); +// }); + +// describe('#stopFlows', function() { +// it('stops running flow configuration', function(done) { +// const originalConfig = [ +// {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, +// {id:"t1",type:"tab"} +// ]; +// storage.getFlows = () => Promise.resolve({flows:originalConfig}); + +// flows.init({log:mockLog, settings:{},storage}); +// flows.load().then(flows.startFlows).then(() => { +// flows.getFlows().flows.should.eql(originalConfig); +// return flows.stopFlows(); +// }).then(() => { +// Object.keys(flowCreate.flows).forEach(id => { +// flowCreate.flows[id].stop.called.should.be.true(); +// }); +// done(); +// }).catch(done); +// }); +// }); +// }); + +//***************************************************************** */ var should = require("should"); var sinon = require("sinon"); @@ -49,7 +996,6 @@ describe('flows/index', function() { _: function() { return "abc"} } - before(function() { getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { return type.indexOf('missing') === -1; @@ -260,9 +1206,9 @@ describe('flows/index', function() { flowCreate.flows['t1'].update.called.should.be.true(); flowCreate.flows['t2'].start.called.should.be.true(); flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); - flows.stopFlows().then(done); - }) - flows.setFlows(newConfig,"nodes") + done(); + }); + flows.setFlows(newConfig,"flows") }); flows.init({log:mockLog, settings:{},storage:storage}); @@ -271,338 +1217,193 @@ describe('flows/index', function() { }); }); - it('returns error if it cannot decrypt credentials', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var credentials = {"$":"fail"}; - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig,credentials).then(function() { - done("Unexpected success when credentials couldn't be decrypted") - }).catch(function(err) { - 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 Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - credentialsLoad.called.should.be.true(); - // 'load' type does not trigger a save - storage.hasOwnProperty('conf').should.be.false(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - }); - }); - - describe('#startFlows', function() { - it('starts the loaded config', function(done) { + it('updates existing flows with partial deployment - full', function(done) { var originalConfig = [ {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, {id:"t1",type:"tab"} ]; + var newConfig = clone(originalConfig); + newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); + newConfig.push({id:"t2",type:"tab"}); + newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); storage.getFlows = function() { return Promise.resolve({flows:originalConfig}); } events.once('flows:started',function() { - Object.keys(flowCreate.flows).should.eql(['_GLOBAL_','t1']); + events.once('flows:started',function() { + flows.getFlows().flows.should.eql(newConfig); + flowCreate.flows['t1'].update.called.should.be.true(); + flowCreate.flows['t2'].start.called.should.be.true(); + flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); + done(); + }); + flows.setFlows(newConfig,"full") + }); + + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows(); + }); + }); + + it('removes a tab with partial deployment - nodes', function(done) { + var originalConfig = [ + {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, + {id:"t1",type:"tab"}, + {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, + {id:"t2",type:"tab"} + ]; + var newConfig = [ + {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, + {id:"t1",type:"tab"} + ]; + + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig}); + } + + events.once('flows:started',function() { + flows.getFlows().flows.should.eql(newConfig); + should.not.exist(flowCreate.flows['t2']); + flowCreate.flows['t1'].update.called.should.be.true(); + flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); done(); }); flows.init({log:mockLog, settings:{},storage:storage}); flows.load().then(function() { - return flows.startFlows(); + flows.startFlows().then(function() { + flows.setFlows(newConfig,"nodes") + }); }); }); - it('does not start if nodes missing', function(done) { + + it('removes a tab with partial deployment - flows', function(done) { var originalConfig = [ {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1",type:"tab"} + {id:"t1",type:"tab"}, + {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, + {id:"t2",type:"tab"} ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - try { - flowCreate.called.should.be.false(); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('starts when missing nodes registered', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1-3",x:10,y:10,z:"t1",type:"missing2",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing"); - setTimeout(function() { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing2"); - setTimeout(function() { - flowCreate.called.should.be.true(); - done(); - },10); - },10); - }); - }); - - it('does not start if external modules missing', function(done) { - var originalConfig = [ - {id:"node-with-missing-modules",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - var receivedEvent = null; - var handleEvent = function(payload) { - receivedEvent = payload; - } - - events.on("runtime-event",handleEvent); - - //{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});" - - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(flows.startFlows).then(() => { - events.removeListener("runtime-event",handleEvent); - try { - flowCreate.called.should.be.false(); - receivedEvent.should.have.property('id','runtime-state'); - receivedEvent.should.have.property('payload', { - state: 'stop', - error: 'missing-modules', - type: 'warning', - text: 'notification.warnings.missing-modules', - modules: [] - }); - - done(); - }catch(err) { - done(err) - } - }); - }); - - }); - - describe.skip('#get',function() { - - }); - - describe('#eachNode', function() { - it('iterates the flow nodes', function(done) { - var originalConfig = [ + var newConfig = [ {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, {id:"t1",type:"tab"} ]; + storage.getFlows = function() { return Promise.resolve({flows:originalConfig}); } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - var c = 0; - flows.eachNode(function(node) { - c++ - }) - c.should.equal(2); + + events.once('flows:started',function() { + flows.getFlows().flows.should.eql(newConfig); + should.not.exist(flowCreate.flows['t2']); + flowCreate.flows['t1'].update.called.should.be.true(); + flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); done(); }); + + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows().then(function() { + flows.setFlows(newConfig,"flows") + }); + }); + }); + + it('removes a tab with partial deployment - full', function(done) { + var originalConfig = [ + {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, + {id:"t1",type:"tab"}, + {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, + {id:"t2",type:"tab"} + ]; + var newConfig = [ + {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, + {id:"t1",type:"tab"} + ]; + + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig}); + } + + events.once('flows:started',function() { + flows.getFlows().flows.should.eql(newConfig); + should.not.exist(flowCreate.flows['t2']); + flowCreate.flows['t1'].update.called.should.be.true(); + flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); + done(); + }); + + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows().then(function() { + flows.setFlows(newConfig,"full") + }); + }); + }); + }); + + describe('#startFlows', function() { + it('starts an empty flow', function(done) { + var originalConfig = []; + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig}); + } + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows().then(function() { + done(); + }); + }); + }); + it('starts an empty flow with credentials error', function(done) { + var originalConfig = []; + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig,credentials:{$:"fail"}}); + } + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows().then(function() { + done(); + }); + }); + }); + + it('starts a non-empty flow', function(done) { + var originalConfig = [ + {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, + {id:"t1",type:"tab"} + ]; + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig}); + } + flows.init({log:mockLog, settings:{},storage:storage}); + flows.load().then(function() { + flows.startFlows().then(function() { + done(); + }); + }); }); }); describe('#stopFlows', function() { - - }); - // describe('#handleError', function() { - // it('passes error to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // it('passes error to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // try { - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // flowCreate.flows['t2'].handleError.called.should.be.false(); - // flowCreate.flows['t3'].handleError.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - // describe('#handleStatus', function() { - // it('passes status to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // - // it('passes status to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // try { - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // flowCreate.flows['t2'].handleStatus.called.should.be.false(); - // flowCreate.flows['t3'].handleStatus.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - - describe('#checkTypeInUse', function() { - - before(function() { - sinon.stub(typeRegistry,"getNodeInfo").callsFake(function(id) { - if (id === 'unused-module') { - return {types:['one','two','three']} - } else { - return {types:['one','test','three']} - } - }); - }); - - after(function() { - typeRegistry.getNodeInfo.restore(); - }); - - it('returns cleanly if type not is use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; + it('stops an empty flow', function(done) { + var originalConfig = []; + storage.getFlows = function() { + return Promise.resolve({flows:originalConfig}); + } flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - flows.checkTypeInUse("unused-module"); - done(); + flows.load().then(function() { + flows.startFlows().then(function() { + flows.stopFlows().then(function() { + done(); + }); + }); }); }); - it('throws error if type is in use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - /*jshint immed: false */ - try { - flows.checkTypeInUse("used-module"); - done("type_in_use error not thrown"); - } catch(err) { - err.code.should.eql("type_in_use"); - done(); - } - }); - }); - }); - describe('#addFlow', function() { - it("rejects duplicate node id",function(done) { + it('stops a non-empty flow', function(done) { var originalConfig = [ {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, {id:"t1",type:"tab"} @@ -612,65 +1413,13 @@ describe('flows/index', function() { } flows.init({log:mockLog, settings:{},storage:storage}); flows.load().then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]} - ] - }).then(function() { - done(new Error('failed to reject duplicate node id')); - }).catch(function(err) { - done(); - }) + flows.startFlows().then(function() { + flows.stopFlows().then(function() { + done(); + }); + }); }); - }); + }); - it("addFlow",function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - storage.setFlows = function() { - return Promise.resolve(); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t2-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-2",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-3",z:"t1",type:"test"} - ] - }).then(function(id) { - flows.getFlows().flows.should.have.lengthOf(6); - var createdFlows = Object.keys(flowCreate.flows); - createdFlows.should.have.lengthOf(3); - createdFlows[2].should.eql(id); - done(); - }).catch(function(err) { - done(err); - }) - }); - - }); - }) - describe('#updateFlow', function() { - it.skip("updateFlow"); - }) - describe('#removeFlow', function() { - it.skip("removeFlow"); - }) - describe('#disableFlow', function() { - it.skip("disableFlow"); - }) - describe('#enableFlow', function() { - it.skip("enableFlow"); - }) -}); +}); \ No newline at end of file