mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add unit tests for externalModules
This commit is contained in:
		| @@ -41,8 +41,8 @@ async function refreshExternalModules() { | ||||
|  | ||||
| function init(_settings) { | ||||
|     settings = _settings; | ||||
|     path.resolve(settings.userDir || process.env.NODE_RED_HOME || "."); | ||||
|  | ||||
|     knownExternalModules = {}; | ||||
|     installEnabled = true; | ||||
|     if (settings.externalModules && settings.externalModules.modules) { | ||||
|         if (settings.externalModules.modules.allowList || settings.externalModules.modules.denyList) { | ||||
|             installAllowList = settings.externalModules.modules.allowList; | ||||
| @@ -197,7 +197,6 @@ async function installModule(moduleDetails) { | ||||
|     }).catch(result => { | ||||
|         var output = result.stderr; | ||||
|         var e; | ||||
|         var lookForVersionNotFound = new RegExp("version not found: ","m"); | ||||
|         if (/E404/.test(output) || /ETARGET/.test(output)) { | ||||
|             log.error(log._("server.install.install-failed-not-found",{name:installSpec})); | ||||
|             e = new Error("Module not found"); | ||||
| @@ -208,7 +207,9 @@ async function installModule(moduleDetails) { | ||||
|             log.error("------------------------------------------"); | ||||
|             log.error(output); | ||||
|             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, | ||||
|     // register: register, | ||||
|     // registerSubflow: registerSubflow, | ||||
|     // checkFlowDependencies: checkFlowDependencies, | ||||
|     // require: requireModule | ||||
|     // | ||||
|  | ||||
| const should = require("should"); | ||||
|  | ||||
| const fs = require("fs"); | ||||
| const sinon = require("sinon"); | ||||
| const fs = require("fs-extra"); | ||||
| 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() { | ||||
|      | ||||
|     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"); | ||||
|             } | ||||
|         }) | ||||
|     }) | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user