mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add unit tests for externalModules
This commit is contained in:
parent
d2c9ccbfdd
commit
05beb6ca79
@ -41,8 +41,8 @@ async function refreshExternalModules() {
|
|||||||
|
|
||||||
function init(_settings) {
|
function init(_settings) {
|
||||||
settings = _settings;
|
settings = _settings;
|
||||||
path.resolve(settings.userDir || process.env.NODE_RED_HOME || ".");
|
knownExternalModules = {};
|
||||||
|
installEnabled = true;
|
||||||
if (settings.externalModules && settings.externalModules.modules) {
|
if (settings.externalModules && settings.externalModules.modules) {
|
||||||
if (settings.externalModules.modules.allowList || settings.externalModules.modules.denyList) {
|
if (settings.externalModules.modules.allowList || settings.externalModules.modules.denyList) {
|
||||||
installAllowList = settings.externalModules.modules.allowList;
|
installAllowList = settings.externalModules.modules.allowList;
|
||||||
@ -197,7 +197,6 @@ async function installModule(moduleDetails) {
|
|||||||
}).catch(result => {
|
}).catch(result => {
|
||||||
var output = result.stderr;
|
var output = result.stderr;
|
||||||
var e;
|
var e;
|
||||||
var lookForVersionNotFound = new RegExp("version not found: ","m");
|
|
||||||
if (/E404/.test(output) || /ETARGET/.test(output)) {
|
if (/E404/.test(output) || /ETARGET/.test(output)) {
|
||||||
log.error(log._("server.install.install-failed-not-found",{name:installSpec}));
|
log.error(log._("server.install.install-failed-not-found",{name:installSpec}));
|
||||||
e = new Error("Module not found");
|
e = new Error("Module not found");
|
||||||
@ -208,7 +207,9 @@ async function installModule(moduleDetails) {
|
|||||||
log.error("------------------------------------------");
|
log.error("------------------------------------------");
|
||||||
log.error(output);
|
log.error(output);
|
||||||
log.error("------------------------------------------");
|
log.error("------------------------------------------");
|
||||||
throw new Error(log._("server.install.install-failed"));
|
e = new Error(log._("server.install.install-failed"));
|
||||||
|
e.code = "unexpected_error";
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,302 @@
|
|||||||
// init: init,
|
// init: init,
|
||||||
// register: register,
|
// register: register,
|
||||||
|
// registerSubflow: registerSubflow,
|
||||||
// checkFlowDependencies: checkFlowDependencies,
|
// checkFlowDependencies: checkFlowDependencies,
|
||||||
// require: requireModule
|
// require: requireModule
|
||||||
//
|
//
|
||||||
|
|
||||||
const should = require("should");
|
const should = require("should");
|
||||||
|
const sinon = require("sinon");
|
||||||
const fs = require("fs");
|
const fs = require("fs-extra");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const os = require("os");
|
||||||
|
|
||||||
var NR_TEST_UTILS = require("nr-test-utils");
|
const NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
const externalModules = NR_TEST_UTILS.require("@node-red/registry/lib/externalModules");
|
||||||
|
const exec = NR_TEST_UTILS.require("@node-red/util/lib/exec");
|
||||||
|
|
||||||
var externalModules = NR_TEST_UTILS.require("@node-red/registry/lib/externalModules");
|
let homeDir;
|
||||||
|
|
||||||
|
async function createUserDir() {
|
||||||
|
if (!homeDir) {
|
||||||
|
homeDir = path.join(os.tmpdir(),"nr-test-"+Math.floor(Math.random()*100000));
|
||||||
|
}
|
||||||
|
await fs.ensureDir(homeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupExternalModulesPackage(dependencies) {
|
||||||
|
await fs.ensureDir(path.join(homeDir,"externalModules"))
|
||||||
|
await fs.writeFile(path.join(homeDir,"externalModules","package.json"),`{
|
||||||
|
"name": "Node-RED-External-Modules",
|
||||||
|
"description": "These modules are automatically installed by Node-RED to use in Function nodes.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": ${JSON.stringify(dependencies)}
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
describe("externalModules api", function() {
|
describe("externalModules api", function() {
|
||||||
|
beforeEach(async function() {
|
||||||
|
await createUserDir()
|
||||||
|
})
|
||||||
|
afterEach(async function() {
|
||||||
|
await fs.remove(homeDir);
|
||||||
|
})
|
||||||
|
describe("checkFlowDependencies", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
sinon.stub(exec,"run", async function(cmd, args, options) {
|
||||||
|
let error;
|
||||||
|
if (args[1] === "moduleNotFound") {
|
||||||
|
error = new Error();
|
||||||
|
error.stderr = "E404";
|
||||||
|
} else if (args[1] === "moduleVersionNotFound") {
|
||||||
|
error = new Error();
|
||||||
|
error.stderr = "ETARGET";
|
||||||
|
} else if (args[1] === "moduleFail") {
|
||||||
|
error = new Error();
|
||||||
|
error.stderr = "Some unexpected install error";
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
afterEach(function() {
|
||||||
|
exec.run.restore();
|
||||||
|
})
|
||||||
|
it("does nothing when no types are registered",async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
|
])
|
||||||
|
exec.run.called.should.be.false();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips install for modules already installed", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
await setupExternalModulesPackage({"foo": "1.2.3", "bar":"2.3.4"});
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
|
])
|
||||||
|
exec.run.called.should.be.false();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("skips install for built-in modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "fs"}]}
|
||||||
|
])
|
||||||
|
exec.run.called.should.be.false();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("installs missing modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
fs.existsSync(path.join(homeDir,"externalModuels")).should.be.false();
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
|
])
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
fs.existsSync(path.join(homeDir,"externalModules")).should.be.true();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("installs missing modules from inside subflow module", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
externalModules.registerSubflow("sf", {"flow":[{type: "function", libs:[{module: "foo"}]}]});
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "sf"}
|
||||||
|
])
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
})
|
||||||
|
|
||||||
|
it("reports install fail - 404", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "moduleNotFound"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","moduleNotFound");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code",404);
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it("reports install fail - target", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "moduleVersionNotFound"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","moduleVersionNotFound");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code",404);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("reports install fail - unexpected", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "moduleFail"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","moduleFail");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code","unexpected_error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it("reports install fail - multiple", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "moduleNotFound"},{module: "moduleFail"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.called.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(2);
|
||||||
|
// Sort the array so we know the order to test for
|
||||||
|
err.sort(function(A,B) {
|
||||||
|
return A.module.module.localeCompare(B.module.module);
|
||||||
|
})
|
||||||
|
err[1].should.have.property("module");
|
||||||
|
err[1].module.should.have.property("module","moduleNotFound");
|
||||||
|
err[1].should.have.property("error");
|
||||||
|
err[1].error.should.have.property("code",404);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","moduleFail");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code","unexpected_error");
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it("reports install fail - install disabled", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, externalModules: {
|
||||||
|
modules: {
|
||||||
|
allowInstall: false
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
// Should not try to install
|
||||||
|
exec.run.called.should.be.false();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","foo");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code","install_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("reports install fail - module disallowed", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, externalModules: {
|
||||||
|
modules: {
|
||||||
|
denyList: ['foo']
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
// foo disallowed
|
||||||
|
// bar allowed
|
||||||
|
{type: "function", libs:[{module: "foo"},{module: "bar"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.calledOnce.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","foo");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code","install_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("reports install fail - built-in module disallowed", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, externalModules: {
|
||||||
|
modules: {
|
||||||
|
denyList: ['fs']
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
externalModules.register("function", "libs");
|
||||||
|
try {
|
||||||
|
await externalModules.checkFlowDependencies([
|
||||||
|
// foo disallowed
|
||||||
|
// bar allowed
|
||||||
|
{type: "function", libs:[{module: "fs"},{module: "bar"}]}
|
||||||
|
])
|
||||||
|
throw new Error("checkFlowDependencies did not reject after install fail")
|
||||||
|
} catch(err) {
|
||||||
|
exec.run.calledOnce.should.be.true();
|
||||||
|
Array.isArray(err).should.be.true();
|
||||||
|
err.should.have.length(1);
|
||||||
|
err[0].should.have.property("module");
|
||||||
|
err[0].module.should.have.property("module","fs");
|
||||||
|
err[0].should.have.property("error");
|
||||||
|
err[0].error.should.have.property("code","module_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("require", async function() {
|
||||||
|
it("requires built-in modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
const result = externalModules.require("fs")
|
||||||
|
result.should.eql(require("fs"));
|
||||||
|
})
|
||||||
|
it("rejects unknown modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir});
|
||||||
|
try {
|
||||||
|
externalModules.require("foo")
|
||||||
|
throw new Error("require did not reject after fail")
|
||||||
|
} catch(err) {
|
||||||
|
err.should.have.property("code","module_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects disallowed modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, externalModules: {
|
||||||
|
modules: {
|
||||||
|
denyList: ['fs']
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
try {
|
||||||
|
externalModules.require("fs")
|
||||||
|
throw new Error("require did not reject after fail")
|
||||||
|
} catch(err) {
|
||||||
|
err.should.have.property("code","module_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user