/** * 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 request = require('supertest'); var express = require('express'); var bodyParser = require('body-parser'); var sinon = require('sinon'); var NR_TEST_UTILS = require("nr-test-utils"); var nodes = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/nodes"); var apiUtil = NR_TEST_UTILS.require("@node-red/editor-api/lib/util"); describe("api/admin/nodes", function() { var app; before(function() { app = express(); app.use(bodyParser.json()); app.get("/nodes",nodes.getAll); app.post("/nodes",nodes.post); app.get(/\/nodes\/messages/,nodes.getModuleCatalogs); app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,nodes.getModuleCatalog); app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule); app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule); app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet); app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet); app.get("/getIcons",nodes.getIcons); app.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.delete); sinon.stub(apiUtil,"determineLangFromHeaders").callsFake(function() { return "en-US"; }); }); after(function() { apiUtil.determineLangFromHeaders.restore(); }) describe('get nodes', function() { it('returns node list', function(done) { nodes.init({ settings: {}, nodes:{ getNodeList: function() { return Promise.resolve([1,2,3]); } } }); request(app) .get('/nodes') .set('Accept', 'application/json') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.be.an.Array(); res.body.should.have.lengthOf(3); done(); }); }); it('returns node configs', function(done) { nodes.init({ settings: {}, nodes:{ getNodeConfigs: function() { return Promise.resolve(""); } }, i18n: { determineLangFromHeaders: function(){} } }); request(app) .get('/nodes') .set('Accept', 'text/html') .expect(200) .expect("") .end(function(err,res) { if (err) { throw err; } done(); }); }); it('returns node module info', function(done) { nodes.init({ settings: {}, nodes:{ getModuleInfo: function(opts) { return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]); } } }); request(app) .get('/nodes/node-red') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("name","node-red"); done(); }); }); it('returns 404 for unknown module', function(done) { nodes.init({ settings: {}, nodes:{ getModuleInfo: function(opts) { var errInstance = new Error("Not Found"); errInstance.code = "not_found"; errInstance.status = 404; var p = Promise.reject(errInstance); p.catch(()=>{}); return p; } } }); request(app) .get('/nodes/node-blue') .expect(404) .end(function(err,res) { if (err) { throw err; } done(); }); }); it('returns individual node info', function(done) { nodes.init({ settings: {}, nodes:{ getNodeInfo: function(opts) { return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]); } } }); request(app) .get('/nodes/node-red/123') .set('Accept', 'application/json') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("id","node-red/123"); done(); }); }); it('returns individual node configs', function(done) { nodes.init({ settings: {}, nodes:{ getNodeConfig: function(opts) { return Promise.resolve({"node-red/123":""}[opts.id]); } }, i18n: { determineLangFromHeaders: function(){} } }); request(app) .get('/nodes/node-red/123') .set('Accept', 'text/html') .expect(200) .expect("") .end(function(err,res) { if (err) { throw err; } done(); }); }); it('returns 404 for unknown node', function(done) { nodes.init({ settings: {}, nodes:{ getNodeInfo: function(opts) { var errInstance = new Error("Not Found"); errInstance.code = "not_found"; errInstance.status = 404; var p = Promise.reject(errInstance); p.catch(()=>{}); return p; } } }); request(app) .get('/nodes/node-red/456') .set('Accept', 'application/json') .expect(404) .end(function(err,res) { if (err) { throw err; } done(); }); }); }); describe('install', function() { it('installs the module and returns module info', function(done) { var opts; nodes.init({ settings: {}, nodes:{ addModule: function(_opts) { opts = _opts; return Promise.resolve({ name:"foo", nodes:[{id:"123"}] }); } } }); request(app) .post('/nodes') .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("name","foo"); res.body.should.have.property("nodes"); res.body.nodes[0].should.have.property("id","123"); opts.should.have.property("module","foo"); opts.should.have.property("version","1.2.3"); opts.should.have.property("url","https://example/foo-1.2.3.tgz"); done(); }); }); it('returns error', function(done) { nodes.init({ settings: {}, nodes:{ addModule: function(opts) { var errInstance = new Error("Message"); errInstance.code = "random_error"; errInstance.status = 400; var p = Promise.reject(errInstance); p.catch(()=>{}); return p; } } }); request(app) .post('/nodes') .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) .expect(400) .end(function(err,res) { if (err) { throw err; } res.body.should.have.a.property('code','random_error'); done(); }); }); }); describe('delete', function() { it('uninstalls the module', function(done) { var opts; nodes.init({ settings: {}, nodes:{ removeModule: function(_opts) { opts = _opts; return Promise.resolve(); } } }); request(app) .del('/nodes/123') .expect(204) .end(function(err,res) { if (err) { throw err; } opts.should.have.property("module","123"); done(); }); }); it('returns error', function(done) { nodes.init({ settings: {}, nodes:{ removeModule: function(opts) { var errInstance = new Error("Message"); errInstance.code = "random_error"; errInstance.status = 400; var p = Promise.reject(errInstance); p.catch(()=>{}); return p; } } }); request(app) .del('/nodes/123') .expect(400) .end(function(err,res) { if (err) { throw err; } res.body.should.have.a.property('code','random_error'); done(); }); }); }); describe('enable/disable node set', function() { it('returns 400 for invalid request payload', function(done) { nodes.init({ settings: {}, nodes:{ setNodeSetState: function(opts) {return Promise.resolve()} } }); request(app) .put('/nodes/node-red/foo') .send({}) .expect(400) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("code","invalid_request"); res.body.should.have.property("message","Invalid request"); done(); }); }); it('sets node state and returns node info', function(done) { var opts; nodes.init({ settings: {}, nodes:{ setNodeSetState: function(_opts) { opts = _opts; return Promise.resolve({id:"123",enabled: true }); } } }); request(app) .put('/nodes/node-red/foo') .send({enabled:true}) .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("id","123"); res.body.should.have.property("enabled",true); opts.should.have.property("enabled",true); opts.should.have.property("id","node-red/foo"); done(); }); }); }); describe('enable/disable module' ,function() { it('returns 400 for invalid request payload', function(done) { nodes.init({ settings: {}, nodes:{ setModuleState: function(opts) {return Promise.resolve()} } }); request(app) .put('/nodes/node-red') .send({}) .expect(400) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("code","invalid_request"); res.body.should.have.property("message","Invalid request"); done(); }); }); it('sets module state and returns module info', function(done) { var opts; nodes.init({ settings: {}, nodes:{ setModuleState: function(_opts) { opts = _opts; return Promise.resolve({name:"node-red"}); } } }); request(app) .put('/nodes/node-red') .send({enabled:true}) .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("name","node-red"); opts.should.have.property("enabled",true); opts.should.have.property("module","node-red"); done(); }); }); }); describe('get icons', function() { it('returns icon list', function(done) { nodes.init({ settings: {}, nodes:{ getIconList: function() { return Promise.resolve({module:[1,2,3]}); } } }); request(app) .get('/getIcons') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.have.property("module"); res.body.module.should.be.an.Array(); res.body.module.should.have.lengthOf(3); done(); }); }); }); describe('get module messages', function() { it('returns message catalog', function(done) { nodes.init({ settings: {}, nodes:{ getModuleCatalog: function(opts) { return Promise.resolve({a:123}); } } }); request(app) .get('/nodes/module/set/messages') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.eql({a:123}); done(); }); }); it('returns all node catalogs', function(done) { nodes.init({ settings: {}, nodes:{ getModuleCatalogs: function(opts) { return Promise.resolve({a:1}); } } }); request(app) .get('/nodes/messages') .expect(200) .end(function(err,res) { if (err) { throw err; } res.body.should.eql({a:1}); done(); }); }); }) });