Move exec and events components to util module

The exec and events components are common components that
are used by both runtime and registry. It makes sense to
move them into the util package.

This also adds some docs to the registry module
This commit is contained in:
Nick O'Leary
2020-12-02 09:25:10 +00:00
parent a1f565f756
commit 6fb96fa3c1
33 changed files with 491 additions and 319 deletions

View File

@@ -19,6 +19,7 @@ var sinon = require("sinon");
var NR_TEST_UTILS = require("nr-test-utils");
var comms = NR_TEST_UTILS.require("@node-red/runtime/lib/api/comms");
var events = NR_TEST_UTILS.require("@node-red/util/lib/events");
describe("runtime-api/comms", function() {
describe("listens for events", function() {
@@ -30,21 +31,19 @@ describe("runtime-api/comms", function() {
}
var eventHandlers = {};
before(function(done) {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
comms.addConnection({client: clientConnection}).then(done);
})
after(function(done) {
comms.removeConnection({client: clientConnection}).then(done);
events.removeListener.restore();
events.on.restore();
})
afterEach(function() {
messages = [];
@@ -98,18 +97,18 @@ describe("runtime-api/comms", function() {
}
}
before(function() {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
after(function() {
events.removeListener.restore();
events.on.restore();
})
afterEach(function(done) {
comms.removeConnection({client: clientConnection1}).then(function() {
comms.removeConnection({client: clientConnection2}).then(done);
@@ -178,18 +177,18 @@ describe("runtime-api/comms", function() {
}
var eventHandlers = {};
before(function() {
sinon.stub(events,"removeListener", function() {})
sinon.stub(events,"on", function(evt,handler) { eventHandlers[evt] = handler })
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
after(function() {
events.removeListener.restore();
events.on.restore();
})
afterEach(function(done) {
messages = [];
comms.removeConnection({client: clientConnection}).then(done);

View File

@@ -1,25 +0,0 @@
/**
* 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 NR_TEST_UTILS = require("nr-test-utils");
describe("runtime/events", function() {
it('can be required without errors', function() {
NR_TEST_UTILS.require("@node-red/runtime/lib/events");
});
it.skip('more tests needed', function(){})
});

View File

@@ -1,138 +0,0 @@
/**
* 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 path = require("path");
var events = require("events");
var child_process = require('child_process');
var NR_TEST_UTILS = require("nr-test-utils");
var exec = NR_TEST_UTILS.require("@node-red/runtime/lib/exec");
describe("runtime/exec", function() {
var logEvents;
var mockProcess;
beforeEach(function() {
var logEventHandler = new events.EventEmitter();
logEvents = [];
logEventHandler.on('event-log', function(ev) {
logEvents.push(ev);
});
exec.init({events:logEventHandler});
mockProcess = new events.EventEmitter();
mockProcess.stdout = new events.EventEmitter();
mockProcess.stderr = new events.EventEmitter();
sinon.stub(child_process,'spawn',function(command,args,options) {
mockProcess._args = {command,args,options};
return mockProcess;
});
});
afterEach(function() {
if (child_process.spawn.restore) {
child_process.spawn.restore();
}
});
it("runs command and resolves on success - no emit", function(done) {
var command = "cmd";
var args = [1,2,3];
var opts = { a: true };
exec.run(command,args,opts).then(function(result) {
command.should.eql(mockProcess._args.command);
args.should.eql(mockProcess._args.args);
opts.should.eql(mockProcess._args.options);
logEvents.length.should.eql(0);
result.code.should.eql(0);
result.stdout.should.eql("123");
result.stderr.should.eql("abc");
done();
}).catch(done);
mockProcess.stdout.emit('data',"1");
mockProcess.stderr.emit('data',"a");
mockProcess.stderr.emit('data',"b");
mockProcess.stdout.emit('data',"2");
mockProcess.stdout.emit('data',"3");
mockProcess.stderr.emit('data',"c");
mockProcess.emit('close',0);
});
it("runs command and resolves on success - emit", function(done) {
var command = "cmd";
var args = [1,2,3];
var opts = { a: true };
exec.run(command,args,opts,true).then(function(result) {
logEvents.length.should.eql(8);
done();
}).catch(done);
mockProcess.stdout.emit('data',"1");
mockProcess.stderr.emit('data',"a");
mockProcess.stderr.emit('data',"b");
mockProcess.stdout.emit('data',"2");
mockProcess.stdout.emit('data',"3");
mockProcess.stderr.emit('data',"c");
mockProcess.emit('close',0);
})
it("runs command and rejects on error - close", function(done) {
var command = "cmd";
var args = [1,2,3];
var opts = { a: true };
exec.run(command,args,opts).then(function() {
done("Command should have rejected");
}).catch(function(result) {
result
result.code.should.eql(123);
result.stdout.should.eql("123");
result.stderr.should.eql("abc");
done();
}).catch(done);
mockProcess.stdout.emit('data',"1");
mockProcess.stderr.emit('data',"a");
mockProcess.stderr.emit('data',"b");
mockProcess.stdout.emit('data',"2");
mockProcess.stdout.emit('data',"3");
mockProcess.stderr.emit('data',"c");
mockProcess.emit('close',123);
})
it("runs command and rejects on error - error", function(done) {
var command = "cmd";
var args = [1,2,3];
var opts = { a: true };
exec.run(command,args,opts).then(function() {
done("Command should have rejected");
}).catch(function(result) {
result
result.code.should.eql(456);
result.stdout.should.eql("");
result.stderr.should.eql("test-error");
done();
}).catch(done);
mockProcess.emit('error',"test-error");
mockProcess.emit('close',456);
})
});

View File

@@ -22,7 +22,7 @@ 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/runtime/lib/events");
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");

View File

@@ -28,6 +28,7 @@ var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings");
var util = NR_TEST_UTILS.require("@node-red/util");
var log = NR_TEST_UTILS.require("@node-red/util").log;
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
describe("runtime", function() {
afterEach(function() {
@@ -43,43 +44,45 @@ describe("runtime", function() {
delete process.env.NODE_RED_HOME;
});
function mockUtil(metrics) {
return {
log:{
log: sinon.stub(),
warn: sinon.stub(),
info: sinon.stub(),
trace: sinon.stub(),
metric: sinon.stub().returns(!!metrics),
_: function() { return "abc"}
},
i18n: {
registerMessageCatalog: function(){
return Promise.resolve();
}
}
}
sinon.stub(log,"log",function(){})
sinon.stub(log,"warn",function(){})
sinon.stub(log,"info",function(){})
sinon.stub(log,"trace",function(){})
sinon.stub(log,"metric",function(){ return !!metrics })
sinon.stub(log,"_",function(){ return "abc"})
sinon.stub(i18n,"registerMessageCatalog",function(){ return Promise.resolve()})
}
function unmockUtil() {
log.log.restore && log.log.restore();
log.warn.restore && log.warn.restore();
log.info.restore && log.info.restore();
log.trace.restore && log.trace.restore();
log.metric.restore && log.metric.restore();
log._.restore && log._.restore();
i18n.registerMessageCatalog.restore && i18n.registerMessageCatalog.restore();
}
describe("init", function() {
beforeEach(function() {
sinon.stub(log,"init",function() {});
sinon.stub(settings,"init",function() {});
sinon.stub(redNodes,"init",function() {})
mockUtil();
});
afterEach(function() {
log.init.restore();
settings.init.restore();
redNodes.init.restore();
unmockUtil();
})
it("initialises components", function() {
runtime.init({testSettings: true, httpAdminRoot:"/"},mockUtil());
runtime.init({testSettings: true, httpAdminRoot:"/"});
settings.init.called.should.be.true();
redNodes.init.called.should.be.true();
});
it("returns version", function() {
runtime.init({testSettings: true, httpAdminRoot:"/"},mockUtil());
runtime.init({testSettings: true, httpAdminRoot:"/"});
return runtime.version().then(version => {
/^\d+\.\d+\.\d+(-.*)?$/.test(version).should.be.true();
});
@@ -98,7 +101,6 @@ describe("runtime", function() {
var redNodesLoadFlows;
var redNodesStartFlows;
var redNodesLoadContextsPlugin;
var i18nRegisterMessageCatalog;
beforeEach(function() {
storageInit = sinon.stub(storage,"init",function(settings) {return Promise.resolve();});
@@ -108,7 +110,7 @@ describe("runtime", function() {
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {return Promise.resolve()});
redNodesStartFlows = sinon.stub(redNodes,"startFlows",function() {});
redNodesLoadContextsPlugin = sinon.stub(redNodes,"loadContextsPlugin",function() {return Promise.resolve()});
i18nRegisterMessageCatalog = sinon.stub(util.i18n,"registerMessageCatalog",function() {return Promise.resolve()});
mockUtil();
});
afterEach(function() {
storageInit.restore();
@@ -119,7 +121,7 @@ describe("runtime", function() {
redNodesLoadFlows.restore();
redNodesStartFlows.restore();
redNodesLoadContextsPlugin.restore();
i18nRegisterMessageCatalog.restore();
unmockUtil();
});
it("reports errored/missing modules",function(done) {
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function(cb) {
@@ -128,8 +130,7 @@ describe("runtime", function() {
{ module:"module",enabled:true,loaded:false,types:["typeA","typeB"]} // missing
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
// sinon.stub(console,"log");
runtime.start().then(function() {
// console.log.restore();
@@ -139,9 +140,9 @@ describe("runtime", function() {
redNodesLoad.calledOnce.should.be.true();
redNodesLoadFlows.calledOnce.should.be.true();
util.log.warn.calledWithMatch("Failed to register 1 node type");
util.log.warn.calledWithMatch("Missing node modules");
util.log.warn.calledWithMatch(" - module: typeA, typeB");
log.warn.calledWithMatch("Failed to register 1 node type");
log.warn.calledWithMatch("Missing node modules");
log.warn.calledWithMatch(" - module: typeA, typeB");
redNodesCleanModuleList.calledOnce.should.be.true();
done();
} catch(err) {
@@ -159,16 +160,15 @@ describe("runtime", function() {
].filter(cb);
});
var serverInstallModule = sinon.stub(redNodes,"installModule",function(name) { return Promise.resolve({nodes:[]});});
var util = mockUtil();
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
try {
util.log.warn.calledWithMatch("Failed to register 2 node types");
util.log.warn.calledWithMatch("Missing node modules");
util.log.warn.calledWithMatch(" - module: typeA, typeB");
util.log.warn.calledWithMatch(" - node-red: typeC, typeD");
log.warn.calledWithMatch("Failed to register 2 node types");
log.warn.calledWithMatch("Missing node modules");
log.warn.calledWithMatch(" - module: typeA, typeB");
log.warn.calledWithMatch(" - node-red: typeC, typeD");
redNodesCleanModuleList.calledOnce.should.be.false();
serverInstallModule.calledOnce.should.be.true();
serverInstallModule.calledWithMatch("module");
@@ -186,14 +186,13 @@ describe("runtime", function() {
{ err:"errored",name:"errName" } // error
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}});
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
try {
util.log.warn.neverCalledWithMatch("Failed to register 1 node type");
util.log.warn.calledWithMatch("[errName] errored");
log.warn.neverCalledWithMatch("Failed to register 1 node type");
log.warn.calledWithMatch("[errName] errored");
done();
} catch(err) {
done(err);
@@ -204,21 +203,21 @@ describe("runtime", function() {
it("reports runtime metrics",function(done) {
var stopFlows = sinon.stub(redNodes,"stopFlows",function() { return Promise.resolve();} );
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function() {return []});
var util = mockUtil(true);
unmockUtil();
mockUtil(true);
runtime.init(
{testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return Promise.resolve();}},
{},
undefined,
util);
undefined);
// sinon.stub(console,"log");
runtime.start().then(function() {
// console.log.restore();
setTimeout(function() {
try {
util.log.log.args.should.have.lengthOf(3);
util.log.log.args[0][0].should.have.property("event","runtime.memory.rss");
util.log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal");
util.log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed");
log.log.args.should.have.lengthOf(3);
log.log.args[0][0].should.have.property("event","runtime.memory.rss");
log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal");
log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed");
done();
} catch(err) {
done(err);