1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Fixup all the tests

This commit is contained in:
Nick O'Leary 2018-04-24 15:01:49 +01:00
parent 34832d5942
commit 5d064aa1d7
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
50 changed files with 3480 additions and 1762 deletions

View File

@ -15,6 +15,7 @@
**/
var runtimeAPI;
var apiUtils = require("../util");
module.exports = {
init: function(_runtimeAPI) {
@ -23,7 +24,7 @@ module.exports = {
get: function(req,res) {
var version = req.get("Node-RED-API-Version")||"v1";
if (!/^v[12]$/.test(version)) {
return res.status(500).json({code:"invalid_api_version", message:"Invalid API Version requested"});
return res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
}
var opts = {
user: req.user
@ -41,7 +42,7 @@ module.exports = {
post: function(req,res) {
var version = req.get("Node-RED-API-Version")||"v1";
if (!/^v[12]$/.test(version)) {
return res.status(500).json({code:"invalid_api_version", message:"Invalid API Version requested"});
return res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
}
var opts = {
user: req.user,

View File

@ -56,7 +56,7 @@ module.exports = {
user: req.user,
module: req.params[0]
}
runtimeAPI.nodes.removeModule(opts).then(function(info) {
runtimeAPI.nodes.removeModule(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
@ -77,7 +77,7 @@ module.exports = {
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
runtimeAPI.nodes.getNodeConfig(opts).then(function(result) {
return res.json(result);
return res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
@ -100,7 +100,7 @@ module.exports = {
var body = req.body;
if (!body.hasOwnProperty("enabled")) {
// log.audit({event: "nodes.module.set",error:"invalid_request"},req);
res.status(400).json({error:"invalid_request", message:"Invalid request"});
res.status(400).json({code:"invalid_request", message:"Invalid request"});
return;
}
var opts = {
@ -119,7 +119,7 @@ module.exports = {
var body = req.body;
if (!body.hasOwnProperty("enabled")) {
// log.audit({event: "nodes.module.set",error:"invalid_request"},req);
res.status(400).json({error:"invalid_request", message:"Invalid request"});
res.status(400).json({code:"invalid_request", message:"Invalid request"});
return;
}
var opts = {

View File

@ -28,6 +28,8 @@ var runtimeAPI;
var wsServer;
var activeConnections = [];
var anonymousUser;
var retained = {};
var heartbeatTimer;
@ -173,7 +175,8 @@ CommsConnection.prototype.subscribe = function(topic) {
function start() {
if (!settings.disableEditor) {
Users.default().then(function(anonymousUser) {
Users.default().then(function(_anonymousUser) {
anonymousUser = _anonymousUser;
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
var path = settings.httpAdminRoot || "/";
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";

View File

@ -89,7 +89,6 @@ module.exports = {
// Library
var library = require("./library");
library.init(runtimeAPI);
editorApp.get("/library/flows",needsPermission("library.read"),library.getAll,apiUtil.errorHandler);
editorApp.get(/library\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
editorApp.post(/library\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
@ -107,7 +106,7 @@ module.exports = {
// User Settings
editorApp.post("/settings/user",needsPermission("settings.write"),info.updateUserSettings,apiUtil.errorHandler);
// SSH keys
editorApp.use("/settings/user/keys",info.sshkeys());
editorApp.use("/settings/user/keys",needsPermission("settings.write"),info.sshkeys());
return editorApp;
}

View File

@ -14,9 +14,9 @@
* limitations under the License.
**/
var apiUtils = require("../util");
var express = require("express");
var runtimeAPI;
var needsPermission = require("../auth").needsPermission;
function getUsername(userObj) {
var username = '__default';
@ -34,7 +34,7 @@ module.exports = {
var app = express();
// List all SSH keys
app.get("/", needsPermission("settings.read"), function(req,res) {
app.get("/", function(req,res) {
var opts = {
user: req.user
}
@ -48,7 +48,7 @@ module.exports = {
});
// Get SSH key detail
app.get("/:id", needsPermission("settings.read"), function(req,res) {
app.get("/:id", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
@ -63,11 +63,17 @@ module.exports = {
});
// Generate a SSH key
app.post("/", needsPermission("settings.write"), function(req,res) {
app.post("/", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
// TODO: validate params
opts.name = req.body.name;
opts.password = req.body.password;
opts.comment = req.body.comment;
opts.size = req.body.size;
runtimeAPI.settings.generateUserKey(opts).then(function(name) {
res.json({
name: name
@ -78,12 +84,12 @@ module.exports = {
});
// Delete a SSH key
app.delete("/:id", needsPermission("settings.write"), function(req,res) {
app.delete("/:id", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.settings.generateUserKey(opts).then(function(name) {
runtimeAPI.settings.removeUserKey(opts).then(function(name) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);

View File

@ -89,23 +89,9 @@ module.exports = {
init: init,
start: start,
stop: stop,
library: {
register: function(type) {
if (editor) {
editor.registerLibrary(type);
}
}
},
auth: {
needsPermission: auth.needsPermission
},
comms: {
publish: function(topic,data,retain) {
if (editor) {
editor.publish(topic,data,retain);
}
}
},
get adminApp() { return adminApp; },
get server() { return server; }
};

View File

@ -21,7 +21,6 @@ var i18n = require("../util").i18n; // TODO: separate module
module.exports = {
errorHandler: function(err,req,res,next) {
console.error(err.stack);
if (err.message === "request entity too large") {
log.error(err);
} else {
@ -40,13 +39,9 @@ module.exports = {
return lang;
},
rejectHandler: function(req,res,err) {
res.status(err.status||500);
if (err.code || err.message) {
res.json({
res.status(err.status||500).json({
code: err.code||"unexpected_error",
message: err.message
})
}
res.end();
message: err.message||err.toString()
});
}
}

View File

@ -111,9 +111,20 @@ module.exports = {
util: runtime.util,
version: runtime.version,
events: runtime.events,
comms: api.comms,
library: api.library,
comms: {
publish: function(topic,data,retain) {
runtime.events.emit("comms",{
topic: topic,
data: data,
retain: retain
})
}
},
library: {
register: function(type) {
return runtime.library.register(null,type);
}
},
auth: api.auth,
get app() { console.log("Deprecated use of RED.app - use RED.httpAdmin instead"); return runtime.app },

View File

@ -49,13 +49,15 @@ function publish(topic,data,retain) {
} else {
delete retained[topic];
}
connections.forEach(connection => connection.send(topic,data,retain))
connections.forEach(connection => connection.send(topic,data))
}
var api = module.exports = {
init: function(_runtime) {
runtime = _runtime;
connections = [];
retained = {};
runtime.events.removeListener("node-status",handleStatusEvent);
runtime.events.on("node-status",handleStatusEvent);
runtime.events.removeListener("runtime-event",handleRuntimeEvent);

View File

@ -135,6 +135,7 @@ var api = module.exports = {
} else {
runtime.log.audit({event: "flow.get",id:opts.id,error:"not_found"});
var err = new Error();
err.code = "not_found";
err.status = 404;
return reject(err);
}

View File

@ -42,9 +42,6 @@ var stubbedExpressApp = {
delete: function() {}
}
var adminApi = {
library: {
register: function() {}
},
auth: {
needsPermission: function() {}
},

View File

@ -29,17 +29,20 @@ function init(_runtime) {
}
function registerType(id,type) {
if (knownTypes.hasOwnProperty(type)) {
throw new Error(`Library type '${type}' already registerd by ${id}'`)
}
// TODO: would like to enforce this, but currently the tests register the same type multiple
// times and have no way to remove themselves.
// if (knownTypes.hasOwnProperty(type)) {
// throw new Error(`Library type '${type}' already registered by ${id}'`)
// }
knownTypes[type] = id;
}
function getAllEntries(type) {
if (!knownTypes.hasOwnProperty(type)) {
throw new Error(`Unknown library type '${type}'`);
}
}
// function getAllEntries(type) {
// if (!knownTypes.hasOwnProperty(type)) {
// throw new Error(`Unknown library type '${type}'`);
// }
// }
function getEntry(type,path) {
if (type !== 'flows') {
if (!knownTypes.hasOwnProperty(type)) {
@ -67,12 +70,11 @@ function getEntry(type,path) {
return reject(err);
}
}
} else {
}
// IF we get here, we didn't find the file
var error = new Error("not_found");
error.code = "not_found";
return reject(error);
}
} else {
resolve(storage.getFlow(path));
}
@ -92,8 +94,8 @@ function saveEntry(type,path,meta,body) {
module.exports = {
init: init,
registerType: registerType,
getAllEntries: getAllEntries,
register: registerType,
// getAllEntries: getAllEntries,
getEntry: getEntry,
saveEntry: saveEntry

View File

@ -79,6 +79,11 @@ function createNodeApi(node) {
retain: retain
})
}
},
library: {
register: function(type) {
return runtime.library.register(node.id,type);
}
}
}
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials" ]);
@ -88,11 +93,6 @@ function createNodeApi(node) {
copyObjectProperties(runtime.log,red.log,null,["init"]);
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
if (runtime.adminApi) {
red.library = {
register: function(type) {
return runtime.library.registerType(node.id,type);
}
};
red.auth = runtime.adminApi.auth;
red.httpAdmin = runtime.adminApi.adminApp;
red.httpNode = runtime.nodeApp;
@ -100,12 +100,6 @@ function createNodeApi(node) {
} else {
//TODO: runtime.adminApi is always stubbed if not enabled, so this block
// is unused - but may be needed for the unit tests
red.comms = {
publish: function() {}
};
red.library = {
register: function() {}
};
red.auth = {
needsPermission: function() {}
};

View File

@ -35,7 +35,7 @@ var redNodes = require("../../red/runtime/nodes");
var flows = require("../../red/runtime/nodes/flows");
var credentials = require("../../red/runtime/nodes/credentials");
var comms = require("../../red/api/editor/comms.js");
var log = require("../../red/runtime/log.js");
var log = require("../../red/util/log.js");
var context = require("../../red/runtime/nodes/context.js");
var events = require("../../red/runtime/events.js");

View File

@ -38,18 +38,23 @@ describe("api/admin/flow", function() {
describe("get", function() {
before(function() {
var opts;
flow.init({
settings:{},
nodes: {
getFlow: function(id) {
if (id === '123') {
return {id:'123'}
flows: {
getFlow: function(_opts) {
opts = _opts;
if (opts.id === '123') {
return Promise.resolve({id:'123'});
} else {
return null;
var err = new Error("message");
err.code = "not_found";
err.status = 404;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
}
},
log:{ audit: sinon.stub() }
});
})
it('gets a known flow', function(done) {
@ -75,19 +80,24 @@ describe("api/admin/flow", function() {
});
describe("add", function() {
var opts;
before(function() {
flow.init({
settings:{},
nodes: {
addFlow: function(f) {
if (f.id === "123") {
return when.resolve('123')
flows: {
addFlow: function(_opts) {
opts = _opts;
if (opts.flow.id === "123") {
return Promise.resolve('123')
} else {
return when.reject(new Error("test error"));
var err = new Error("random error");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
}
},
log:{ audit: sinon.stub() }
});
})
it('adds a new flow', function(done) {
@ -114,8 +124,8 @@ describe("api/admin/flow", function() {
if (err) {
return done(err);
}
res.body.should.has.a.property('error','unexpected_error');
res.body.should.has.a.property('message','Error: test error');
res.body.should.has.a.property('code','random_error');
res.body.should.has.a.property('message','random error');
done();
});
@ -123,35 +133,29 @@ describe("api/admin/flow", function() {
})
describe("update", function() {
var nodes;
var opts;
before(function() {
nodes = {
updateFlow: function(id,f) {
var err;
if (id === "123") {
return when.resolve()
} else if (id === "unknown") {
err = new Error();
err.code = 404;
throw err;
} else if (id === "unexpected") {
err = new Error();
err.code = 500;
throw err;
} else {
return when.reject(new Error("test error"));
}
}
};
flow.init({
settings:{},
nodes: nodes,
log:{ audit: sinon.stub() }
flows: {
updateFlow: function(_opts) {
opts = _opts;
if (opts.id === "123") {
return Promise.resolve('123')
} else {
var err = new Error("random error");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
}
});
})
it('updates an existing flow', function(done) {
sinon.spy(nodes,"updateFlow");
request(app)
.put('/flow/123')
.set('Accept', 'application/json')
@ -162,115 +166,79 @@ describe("api/admin/flow", function() {
return done(err);
}
res.body.should.has.a.property('id','123');
nodes.updateFlow.calledOnce.should.be.true();
nodes.updateFlow.lastCall.args[0].should.eql('123');
nodes.updateFlow.lastCall.args[1].should.eql({id:'123'});
nodes.updateFlow.restore();
opts.should.have.property('id','123');
opts.should.have.property('flow',{id:'123'})
done();
});
})
it('404s on an unknown flow', function(done) {
it('400 an invalid flow', function(done) {
request(app)
.put('/flow/unknown')
.put('/flow/456')
.set('Accept', 'application/json')
.send({id:'123'})
.expect(404)
.end(done);
})
it('400 on async update error', function(done) {
request(app)
.put('/flow/async_error')
.set('Accept', 'application/json')
.send({id:'123'})
.send({id:'456'})
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.has.a.property('error','unexpected_error');
res.body.should.has.a.property('message','Error: test error');
done();
});
})
res.body.should.has.a.property('code','random_error');
res.body.should.has.a.property('message','random error');
it('400 on sync update error', function(done) {
request(app)
.put('/flow/unexpected')
.set('Accept', 'application/json')
.send({id:'123'})
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.has.a.property('error',500);
res.body.should.has.a.property('message','Error');
done();
});
})
})
describe("delete", function() {
var nodes;
var opts;
before(function() {
nodes = {
removeFlow: function(id) {
var err;
if (id === "123") {
return when.resolve()
} else if (id === "unknown") {
err = new Error();
err.code = 404;
throw err;
} else if (id === "unexpected") {
err = new Error();
err.code = 500;
throw err;
}
}
};
flow.init({
settings:{},
nodes: nodes,
log:{ audit: sinon.stub() }
flows: {
deleteFlow: function(_opts) {
opts = _opts;
if (opts.id === "123") {
return Promise.resolve()
} else {
var err = new Error("random error");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
}
});
})
it('updates an existing flow', function(done) {
sinon.spy(nodes,"removeFlow");
it('deletes an existing flow', function(done) {
request(app)
.delete('/flow/123')
.del('/flow/123')
.set('Accept', 'application/json')
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
nodes.removeFlow.calledOnce.should.be.true();
nodes.removeFlow.lastCall.args[0].should.eql('123');
nodes.removeFlow.restore();
opts.should.have.property('id','123');
done();
});
})
it('404s on an unknown flow', function(done) {
it('400 an invalid flow', function(done) {
request(app)
.delete('/flow/unknown')
.expect(404)
.end(done);
})
it('400 on remove error', function(done) {
request(app)
.delete('/flow/unexpected')
.del('/flow/456')
.set('Accept', 'application/json')
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.has.a.property('error',500);
res.body.should.has.a.property('message','Error');
res.body.should.has.a.property('code','random_error');
res.body.should.has.a.property('message','random error');
done();
});
})

View File

@ -19,7 +19,6 @@ var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');
var flows = require("../../../../red/api/admin/flows");
@ -36,10 +35,8 @@ describe("api/admin/flows", function() {
it('returns flow - v1', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
flows:{
getFlows: function() { return Promise.resolve({rev:"123",flows:[1,2,3]}); }
}
});
request(app)
@ -60,10 +57,8 @@ describe("api/admin/flows", function() {
});
it('returns flow - v2', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
flows:{
getFlows: function() { return Promise.resolve({rev:"123",flows:[1,2,3]}); }
}
});
request(app)
@ -104,10 +99,9 @@ describe("api/admin/flows", function() {
});
});
it('sets flows - default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
var setFlows = sinon.spy(function() { return Promise.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
flows:{
setFlows: setFlows
}
});
@ -120,15 +114,14 @@ describe("api/admin/flows", function() {
return done(err);
}
setFlows.calledOnce.should.be.true();
setFlows.lastCall.args[1].should.eql('full');
setFlows.lastCall.args[0].should.have.property('deploymentType','full');
done();
});
});
it('sets flows - non-default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
var setFlows = sinon.spy(function() { return Promise.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
flows:{
setFlows: setFlows
}
});
@ -142,19 +135,22 @@ describe("api/admin/flows", function() {
return done(err);
}
setFlows.calledOnce.should.be.true();
setFlows.lastCall.args[1].should.eql('nodes');
setFlows.lastCall.args[0].should.have.property('deploymentType','nodes');
done();
});
});
it('set flows - rejects mismatched revision - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
flows:{
setFlows: function() {
var err = new Error("mismatch");
err.code = "version_mismatch";
err.status = 409;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
});
request(app)
@ -171,54 +167,6 @@ describe("api/admin/flows", function() {
done();
});
});
it('set flows - rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({rev:123,flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('set flows - no rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('sets flow - bad version', function(done) {
request(app)
.post('/flows')
@ -238,11 +186,10 @@ describe("api/admin/flows", function() {
});
});
it('reloads flows', function(done) {
var loadFlows = sinon.spy(function() { return when.resolve(); });
var setFlows = sinon.spy(function() { return Promise.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
loadFlows: loadFlows
flows:{
setFlows: setFlows
}
});
request(app)
@ -254,29 +201,9 @@ describe("api/admin/flows", function() {
if (err) {
return done(err);
}
loadFlows.called.should.be.true();
setFlows.called.should.be.true();
setFlows.lastCall.args[0].should.not.have.property('flows');
done();
});
});
it('returns error when set fails', function(done) {
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: function() { return when.reject(new Error("expected error")); }
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("message","expected error");
done();
});
});
});

View File

@ -27,31 +27,17 @@ var apiUtil = require("../../../../red/api/util");
describe("api/admin/nodes", function() {
var app;
function initNodes(runtime) {
runtime.log = {
audit:function(e){},//console.log(e)},
_:function(){},
info: function(){},
warn: function(){}
}
runtime.events = {
emit: function(){}
}
nodes.init(runtime);
}
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/nodes",nodes.getAll);
app.post("/nodes",nodes.post);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
app.get("/getIcons",nodes.getIcons);
app.delete("/nodes/:id",nodes.delete);
app.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.delete);
sinon.stub(apiUtil,"determineLangFromHeaders", function() {
return "en-US";
});
@ -62,10 +48,10 @@ describe("api/admin/nodes", function() {
describe('get nodes', function() {
it('returns node list', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeList: function() {
return [1,2,3];
return Promise.resolve([1,2,3]);
}
}
});
@ -84,10 +70,10 @@ describe("api/admin/nodes", function() {
});
it('returns node configs', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeConfigs: function() {
return "<script></script>";
return Promise.resolve("<script></script>");
}
},
i18n: {
@ -108,10 +94,10 @@ describe("api/admin/nodes", function() {
});
it('returns node module info', function(done) {
initNodes({
nodes.init({
nodes:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
getModuleInfo: function(opts) {
return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]);
}
}
});
@ -128,10 +114,15 @@ describe("api/admin/nodes", function() {
});
it('returns 404 for unknown module', function(done) {
initNodes({
nodes.init({
nodes:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
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;
}
}
});
@ -147,10 +138,10 @@ describe("api/admin/nodes", function() {
});
it('returns individual node info', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeInfo: function(id) {
return {"node-red/123":{id:"node-red/123"}}[id];
getNodeInfo: function(opts) {
return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]);
}
}
});
@ -168,10 +159,10 @@ describe("api/admin/nodes", function() {
});
it('returns individual node configs', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeConfig: function(id) {
return {"node-red/123":"<script></script>"}[id];
getNodeConfig: function(opts) {
return Promise.resolve({"node-red/123":"<script></script>"}[opts.id]);
}
},
i18n: {
@ -190,12 +181,16 @@ describe("api/admin/nodes", function() {
done();
});
});
it('returns 404 for unknown node', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeInfo: function(id) {
return {"node-red/123":{id:"node-red/123"}}[id];
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;
}
}
});
@ -213,46 +208,13 @@ describe("api/admin/nodes", function() {
});
describe('install', function() {
it('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
});
request(app)
.post('/nodes')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 400 if request is invalid', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.post('/nodes')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
describe('by module', function() {
it('installs the module and returns module info', function(done) {
initNodes({
settings:{available:function(){return true}},
var opts;
nodes.init({
nodes:{
getModuleInfo: function(id) { return null; },
installModule: function() {
return when.resolve({
addModule: function(_opts) {
opts = _opts;
return Promise.resolve({
name:"foo",
nodes:[{id:"123"}]
});
@ -261,7 +223,7 @@ describe("api/admin/nodes", function() {
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.send({module: 'foo',version:"1.2.3"})
.expect(200)
.end(function(err,res) {
if (err) {
@ -270,85 +232,72 @@ describe("api/admin/nodes", function() {
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");
done();
});
});
it('fails the install if already installed', function(done) {
initNodes({
settings:{available:function(){return true}},
it('returns error', function(done) {
nodes.init({
nodes:{
getModuleInfo: function(id) { return {nodes:{id:"123"}}; },
installModule: function() {
return when.resolve({id:"123"});
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'})
.send({module: 'foo',version:"1.2.3"})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.a.property('code','random_error');
done();
});
});
it('fails the install if module error', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
installModule: function() {
return when.reject(new Error("test error"));
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Error: test error");
done();
});
});
it('fails the install if module not found', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
installModule: function() {
var err = new Error("test error");
err.code = 404;
return when.reject(err);
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
});
});
describe('delete', function() {
it('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
it('uninstalls the module', function(done) {
var opts;
nodes.init({
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({
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)
@ -356,94 +305,19 @@ describe("api/admin/nodes", function() {
if (err) {
throw err;
}
res.body.should.have.a.property('code','random_error');
done();
});
});
});
describe('by module', function() {
it('uninstalls the module', function(done) {
initNodes({
settings:{available:function(){return true}},
describe('enable/disable node set', function() {
it('returns 400 for invalid request payload', function(done) {
nodes.init({
nodes:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.resolve({id:"123"});}
setNodeSetState: function(opts) {return Promise.resolve()}
}
});
request(app)
.del('/nodes/foo')
.expect(204)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('fails the uninstall if the module is not installed', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
getNodeInfo: function() { return null }
}
});
request(app)
.del('/nodes/foo')
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('fails the uninstall if the module is not installed', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.reject(new Error("test error"));}
}
});
request(app)
.del('/nodes/foo')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Error: test error");
done();
});
});
});
});
describe('enable/disable', function() {
it('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
});
request(app)
.put('/nodes/123')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 400 for invalid node payload', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/node-red/foo')
.send({})
@ -452,77 +326,23 @@ describe("api/admin/nodes", function() {
if (err) {
throw err;
}
res.body.should.have.property("code","invalid_request");
res.body.should.have.property("message","Invalid request");
done();
});
});
it('returns 400 for invalid module payload', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/foo')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Invalid request");
done();
});
});
it('returns 404 for unknown node', function(done) {
initNodes({
settings:{available:function(){return true}},
it('sets node state and returns node info', function(done) {
var opts;
nodes.init({
nodes:{
getNodeInfo: function() { return null }
setNodeSetState: function(_opts) {
opts = _opts;
return Promise.resolve({id:"123",enabled: true });
}
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:false})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 404 for unknown module', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null }
}
});
request(app)
.put('/nodes/node-blue')
.send({enabled:false})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('enables disabled node', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: false} },
enableNode: function() { return when.resolve({id:"123",enabled: true,types:['a']}); }
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:true})
@ -533,130 +353,41 @@ describe("api/admin/nodes", function() {
}
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();
});
});
it('disables enabled node', function(done) {
initNodes({
settings:{available:function(){return true}},
});
describe('enable/disable module' ,function() {
it('returns 400 for invalid request payload', function(done) {
nodes.init({
nodes:{
getNodeInfo: function() { return {id:"123",enabled: true} },
disableNode: function() { return when.resolve({id:"123",enabled: false,types:['a']}); }
setModuleState: function(opts) {return Promise.resolve()}
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:false})
.expect(200)
.put('/nodes/node-red')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",false);
res.body.should.have.property("code","invalid_request");
res.body.should.have.property("message","Invalid request");
done();
});
});
describe('no-ops if already in the right state', function() {
function run(state,done) {
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
initNodes({
settings:{available:function(){return true}},
it('sets module state and returns module info', function(done) {
var opts;
nodes.init({
nodes:{
getNodeInfo: function() { return {id:"123",enabled: state} },
enableNode: enableNode,
disableNode: disableNode
setModuleState: function(_opts) {
opts = _opts;
return Promise.resolve({name:"node-red"});
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.false();
disableNodeCalled.should.be.false();
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
describe('does not no-op if err on node', function() {
function run(state,done) {
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: state, err:"foo"} },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.equal(state);
disableNodeCalled.should.be.equal(!state);
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
it('enables disabled module', function(done) {
var n1 = {id:"123",enabled:false,types:['a']};
var n2 = {id:"456",enabled:false,types:['b']};
var enableNode = sinon.stub();
enableNode.onFirstCall().returns((function() {
n1.enabled = true;
return when.resolve(n1);
})());
enableNode.onSecondCall().returns((function() {
n2.enabled = true;
return when.resolve(n2);
})());
enableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
enableNode: enableNode
}
});
@ -669,154 +400,20 @@ describe("api/admin/nodes", function() {
throw err;
}
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",true);
res.body.nodes[1].should.have.property("enabled",true);
opts.should.have.property("enabled",true);
opts.should.have.property("module","node-red");
done();
});
});
it('disables enabled module', function(done) {
var n1 = {id:"123",enabled:true,types:['a']};
var n2 = {id:"456",enabled:true,types:['b']};
var disableNode = sinon.stub();
disableNode.onFirstCall().returns((function() {
n1.enabled = false;
return when.resolve(n1);
})());
disableNode.onSecondCall().returns((function() {
n2.enabled = false;
return when.resolve(n2);
})());
disableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:false})
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",false);
res.body.nodes[1].should.have.property("enabled",false);
done();
});
});
describe('no-ops if a node in module already in the right state', function() {
function run(state,done) {
var node = {id:"123",enabled:state,types:['a']};
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.false();
disableNodeCalled.should.be.false();
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
describe('does not no-op if err on a node in module', function() {
function run(state,done) {
var node = {id:"123",enabled:state,types:['a'],err:"foo"};
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.equal(state);
disableNodeCalled.should.be.equal(!state);
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
});
describe('get icons', function() {
it('returns icon list', function(done) {
initNodes({
nodes.init({
nodes:{
getNodeIcons: function() {
return {"module":["1.png","2.png","3.png"]};
getIconList: function() {
return Promise.resolve({module:[1,2,3]});
}
}
});
@ -827,7 +424,6 @@ describe("api/admin/nodes", function() {
if (err) {
throw err;
}
console.log(res.body);
res.body.should.have.property("module");
res.body.module.should.be.an.Array();
res.body.module.should.have.lengthOf(3);

View File

@ -23,6 +23,7 @@ var passport = require("passport");
var auth = require("../../../../red/api/auth");
var Users = require("../../../../red/api/auth/users");
var Tokens = require("../../../../red/api/auth/tokens");
var Permissions = require("../../../../red/api/auth/permissions");
describe("api/auth/index",function() {
@ -30,7 +31,7 @@ describe("api/auth/index",function() {
describe("ensureClientSecret", function() {
before(function() {
auth.init({settings:{},log:{audit:function(){}}})
auth.init({},{})
});
it("leaves client_secret alone if not present",function(done) {
var req = {
@ -85,7 +86,7 @@ describe("api/auth/index",function() {
Users.init.restore();
});
it("returns login details - credentials", function(done) {
auth.init({settings:{adminAuth:{type:"credentials"}},log:{audit:function(){}}})
auth.init({adminAuth:{type:"credentials"}},{})
auth.login(null,{json: function(resp) {
resp.should.have.a.property("type","credentials");
resp.should.have.a.property("prompts");
@ -94,14 +95,14 @@ describe("api/auth/index",function() {
}});
});
it("returns login details - none", function(done) {
auth.init({settings:{},log:{audit:function(){}}})
auth.init({},{})
auth.login(null,{json: function(resp) {
resp.should.eql({});
done();
}});
});
it("returns login details - strategy", function(done) {
auth.init({settings:{adminAuth:{type:"strategy",strategy:{label:"test-strategy",icon:"test-icon"}}},log:{audit:function(){}}})
auth.init({adminAuth:{type:"strategy",strategy:{label:"test-strategy",icon:"test-icon"}}},{})
auth.login(null,{json: function(resp) {
resp.should.have.a.property("type","strategy");
resp.should.have.a.property("prompts");
@ -115,5 +116,100 @@ describe("api/auth/index",function() {
});
});
describe("needsPermission", function() {
beforeEach(function() {
sinon.stub(Tokens,"init",function(){});
sinon.stub(Users,"init",function(){});
});
afterEach(function() {
Tokens.init.restore();
Users.init.restore();
if (passport.authenticate.restore) {
passport.authenticate.restore();
}
if (Permissions.hasPermission.restore) {
Permissions.hasPermission.restore();
}
});
it('no-ops if adminAuth not set', function(done) {
sinon.stub(passport,"authenticate",function(scopes,opts) {
return function(req,res,next) {
}
});
auth.init({});
var func = auth.needsPermission("foo");
func({},{},function() {
passport.authenticate.called.should.be.false();
done();
})
});
it('skips auth if req.user undefined', function(done) {
sinon.stub(passport,"authenticate",function(scopes,opts) {
return function(req,res,next) {
next();
}
});
sinon.stub(Permissions,"hasPermission",function(perm) { return true });
auth.init({adminAuth:{}});
var func = auth.needsPermission("foo");
func({user:null},{},function() {
try {
passport.authenticate.called.should.be.true();
Permissions.hasPermission.called.should.be.false();
done();
} catch(err) {
done(err);
}
})
});
it('passes for valid user permission', function(done) {
sinon.stub(passport,"authenticate",function(scopes,opts) {
return function(req,res,next) {
next();
}
});
sinon.stub(Permissions,"hasPermission",function(perm) { return true });
auth.init({adminAuth:{}});
var func = auth.needsPermission("foo");
func({user:true,authInfo: { scope: "read"}},{},function() {
try {
passport.authenticate.called.should.be.true();
Permissions.hasPermission.called.should.be.true();
Permissions.hasPermission.lastCall.args[0].should.eql("read");
Permissions.hasPermission.lastCall.args[1].should.eql("foo");
done();
} catch(err) {
done(err);
}
})
});
it('rejects for invalid user permission', function(done) {
sinon.stub(passport,"authenticate",function(scopes,opts) {
return function(req,res,next) {
next();
}
});
sinon.stub(Permissions,"hasPermission",function(perm) { return false });
auth.init({adminAuth:{}});
var func = auth.needsPermission("foo");
func({user:true,authInfo: { scope: "read"}},{
status: function(status) {
return { end: function() {
try {
status.should.eql(401);
done();
} catch(err) {
done(err);
}
}}
}
},function() {
done(new Error("hasPermission unexpected passed"))
});
});
});
});

View File

@ -24,9 +24,6 @@ var Tokens = require("../../../../red/api/auth/tokens");
var Clients = require("../../../../red/api/auth/clients");
describe("api/auth/strategies", function() {
before(function() {
strategies.init({log:{audit:function(){}}})
});
describe("Password Token Exchange", function() {
var userAuthentication;
afterEach(function() {

View File

@ -33,6 +33,24 @@ var listenPort = 0; // use ephemeral port
describe("api/editor/comms", function() {
var connections = [];
var mockComms = {
addConnection: function(opts) {
connections.push(opts.client);
return Promise.resolve()
},
removeConnection: function(opts) {
for (var i=0;i<connections.length;i++) {
if (connections[i] === opts.client) {
connections.splice(i,1);
break;
}
}
return Promise.resolve()
},
subscribe: function() { return Promise.resolve()},
unsubscribe: function() { return Promise.resolve(); }
}
describe("with default keepalive", function() {
var server;
@ -41,11 +59,7 @@ describe("api/editor/comms", function() {
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -63,9 +77,15 @@ describe("api/editor/comms", function() {
it('accepts connection', function(done) {
var ws = new WebSocket(url);
connections.length.should.eql(0);
ws.on('open', function() {
try {
connections.length.should.eql(1);
ws.close();
done();
} catch(err) {
done(err);
}
});
});
@ -73,7 +93,8 @@ describe("api/editor/comms", function() {
var ws = new WebSocket(url);
ws.on('open', function() {
ws.send('{"subscribe":"topic1"}');
comms.publish('topic1', 'foo');
connections.length.should.eql(1);
connections[0].send('topic1', 'foo');
});
ws.on('message', function(msg) {
msg.should.equal('[{"topic":"topic1","data":"foo"}]');
@ -82,43 +103,13 @@ describe("api/editor/comms", function() {
});
});
it('publishes retained message for subscription', function(done) {
comms.publish('topic2', 'bar', true);
var ws = new WebSocket(url);
ws.on('open', function() {
ws.send('{"subscribe":"topic2"}');
});
ws.on('message', function(msg) {
console.log(msg);
msg.should.equal('[{"topic":"topic2","data":"bar"}]');
ws.close();
done();
});
});
it('retained message is deleted by non-retained message', function(done) {
comms.publish('topic3', 'retained', true);
comms.publish('topic3', 'non-retained');
var ws = new WebSocket(url);
ws.on('open', function() {
ws.send('{"subscribe":"topic3"}');
comms.publish('topic3', 'new');
});
ws.on('message', function(msg) {
console.log(msg);
msg.should.equal('[{"topic":"topic3","data":"new"}]');
ws.close();
done();
});
});
it('malformed messages are ignored',function(done) {
var ws = new WebSocket(url);
ws.on('open', function() {
ws.send('not json');
ws.send('[]');
ws.send('{"subscribe":"topic3"}');
comms.publish('topic3', 'correct');
connections[0].send('topic3', 'correct');
});
ws.on('message', function(msg) {
console.log(msg);
@ -127,40 +118,16 @@ describe("api/editor/comms", function() {
done();
});
});
// The following test currently fails due to minimum viable
// implementation. More test should be written to test topic
// matching once this one is passing
it.skip('receives message on correct topic', function(done) {
var ws = new WebSocket(url);
ws.on('open', function() {
ws.send('{"subscribe":"topic4"}');
comms.publish('topic5', 'foo');
comms.publish('topic4', 'bar');
});
ws.on('message', function(msg) {
console.log(msg);
msg.should.equal('[{"topic":"topic4","data":"bar"}]');
ws.close();
done();
});
});
it('listens for node/status events');
});
describe("disabled editor", function() {
var server;
var url;
var port;
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
sinon.stub(Users,"default",function() { return Promise.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{disableEditor:true},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {disableEditor:true}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -177,12 +144,14 @@ describe("api/editor/comms", function() {
});
it('rejects websocket connections',function(done) {
connections.length.should.eql(0);
var ws = new WebSocket(url);
ws.on('open', function() {
done(new Error("Socket connection unexpectedly accepted"));
ws.close();
});
ws.on('error', function() {
connections.length.should.eql(0);
done();
});
@ -196,11 +165,7 @@ describe("api/editor/comms", function() {
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{httpAdminRoot:"/adminPath"},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {httpAdminRoot:"/adminPath"}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -217,8 +182,10 @@ describe("api/editor/comms", function() {
});
it('accepts connections',function(done) {
connections.length.should.eql(0);
var ws = new WebSocket(url);
ws.on('open', function() {
connections.length.should.eql(1);
ws.close();
done();
});
@ -236,11 +203,7 @@ describe("api/editor/comms", function() {
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server,{
settings:{httpAdminRoot:"/adminPath"},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {httpAdminRoot:"/adminPath/"}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -257,8 +220,10 @@ describe("api/editor/comms", function() {
});
it('accepts connections',function(done) {
connections.length.should.eql(0);
var ws = new WebSocket(url);
ws.on('open', function() {
connections.length.should.eql(1);
ws.close();
done();
});
@ -276,11 +241,7 @@ describe("api/editor/comms", function() {
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{httpAdminRoot:"adminPath"},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {httpAdminRoot:"adminPath"}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -297,8 +258,10 @@ describe("api/editor/comms", function() {
});
it('accepts connections',function(done) {
connections.length.should.eql(0);
var ws = new WebSocket(url);
ws.on('open', function() {
connections.length.should.eql(1);
ws.close();
done();
});
@ -316,11 +279,7 @@ describe("api/editor/comms", function() {
before(function(done) {
sinon.stub(Users,"default",function() { return when.resolve(null);});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{webSocketKeepAliveTime: 100},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {webSocketKeepAliveTime: 100}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -355,7 +314,7 @@ describe("api/editor/comms", function() {
ws.on('open', function() {
ws.send('{"subscribe":"foo"}');
interval = setInterval(function() {
comms.publish('foo', 'bar');
connections[0].send('foo', 'bar');
}, 50);
});
ws.on('message', function(data) {
@ -403,11 +362,7 @@ describe("api/editor/comms", function() {
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server,{
settings:{adminAuth:{}},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {adminAuth:{}}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -447,7 +402,7 @@ describe("api/editor/comms", function() {
if (received == 1) {
msg.should.equal('{"auth":"ok"}');
ws.send('{"subscribe":"foo"}');
comms.publish('foo', 'correct');
connections[0].send('foo', 'correct');
} else {
msg.should.equal('[{"topic":"foo","data":"correct"}]');
ws.close();
@ -494,11 +449,7 @@ describe("api/editor/comms", function() {
before(function(done) {
getDefaultUser = sinon.stub(Users,"default",function() { return when.resolve({permissions:"read"});});
server = stoppable(http.createServer(function(req,res){app(req,res)}));
comms.init(server, {
settings:{adminAuth:{}},
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
events:{on:function(){},removeListener:function(){}}
});
comms.init(server, {adminAuth:{}}, {comms: mockComms});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
@ -520,7 +471,7 @@ describe("api/editor/comms", function() {
ws.on('open', function() {
ws.send('{"subscribe":"foo"}');
setTimeout(function() {
comms.publish('foo', 'correct');
connections[0].send('foo', 'correct');
},200);
});
ws.on('message', function(msg) {

View File

@ -29,64 +29,30 @@ describe('api/editor/credentials', function() {
app = express();
app.get('/credentials/:type/:id',credentials.get);
credentials.init({
log:{audit:function(){}},
nodes:{
getCredentials: function(id) {
if (id === "n1") {
return {user1:"abc",password1:"123"};
flows: {
getNodeCredentials: function(opts) {
if (opts.type === "known-type" && opts.id === "n1") {
return Promise.resolve({
user1:"abc",
has_password1: true
});
} else {
return null;
}
},
getCredentialDefinition:function(type) {
if (type === "known-type") {
return {user1:{type:"text"},password1:{type:"password"}};
} else {
return null;
var err = new Error("message");
err.code = "test_code";
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
}
});
});
it('returns empty credentials if unknown type',function(done) {
request(app)
.get("/credentials/unknown-type/n1")
.expect(200)
.expect("Content-Type",/json/)
.end(function(err,res) {
if (err) {
done(err);
} else {
try {
res.body.should.eql({});
done();
} catch(e) {
done(e);
}
}
})
});
it('returns empty credentials if none are stored',function(done) {
request(app)
.get("/credentials/known-type/n2")
.expect("Content-Type",/json/)
.end(function(err,res) {
if (err) {
done(err);
} else {
try {
res.body.should.eql({});
done();
} catch(e) {
done(e);
}
}
})
});
it('returns stored credentials',function(done) {
request(app)
.get("/credentials/known-type/n1")
.expect("Content-Type",/json/)
.expect(200)
.end(function(err,res) {
if (err) {
done(err);
@ -102,5 +68,26 @@ describe('api/editor/credentials', function() {
}
})
});
it('returns any error',function(done) {
request(app)
.get("/credentials/unknown-type/n2")
.expect("Content-Type",/json/)
.expect(500)
.end(function(err,res) {
if (err) {
done(err);
} else {
try {
res.body.should.have.property('code');
res.body.code.should.be.equal("test_code");
res.body.should.have.property('message');
res.body.message.should.be.equal('message');
done();
} catch(e) {
done(e);
}
}
})
});
});

View File

@ -22,6 +22,10 @@ var editorApi = require("../../../../red/api/editor");
var comms = require("../../../../red/api/editor/comms");
var info = require("../../../../red/api/editor/settings");
var auth = require("../../../../red/api/auth");
var log = require("../../../../red/util/log");
var when = require("when");
@ -37,9 +41,7 @@ describe("api/editor/index", function() {
info.init.restore();
});
it("disables the editor", function() {
var editorApp = editorApi.init({},{
settings:{disableEditor:true}
});
var editorApp = editorApi.init({},{disableEditor:true},{});
should.not.exist(editorApp);
comms.init.called.should.be.false();
info.init.called.should.be.false();
@ -67,15 +69,13 @@ describe("api/editor/index", function() {
})
require("../../../../red/api/editor/theme").app.restore();
auth.needsPermission.restore();
log.error.restore();
});
before(function() {
app = editorApi.init({},{
log:{audit:function(){},error:function(msg){errors.push(msg)}},
settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:false,exportNodeSettings:function(){}},
events:{on:function(){},removeListener:function(){}},
isStarted: function() { return isStarted; },
nodes: {paletteEditorEnabled: function() { return false }}
sinon.stub(log,"error",function(err) { errors.push(err)})
app = editorApi.init({},{httpNodeRoot:true, httpAdminRoot: true,disableEditor:false,exportNodeSettings:function(){}},{
isStarted: () => Promise.resolve(isStarted)
});
});
it('serves the editor', function(done) {
@ -117,7 +117,7 @@ describe("api/editor/index", function() {
done();
});
});
// it('GET /settings', function(done) {
// it.skip('GET /settings', function(done) {
// request(app).get("/settings").expect(200).end(function(err,res) {
// if (err) {
// return done(err);

View File

@ -16,334 +16,287 @@
var should = require("should");
var sinon = require("sinon");
var fs = require("fs");
var fspath = require('path');
var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var when = require('when');
var app;
var library = require("../../../../red/api/editor/library");
var auth = require("../../../../red/api/auth");
describe("api/editor/library", function() {
function initLibrary(_flows,_libraryEntries,_examples,_exampleFlowPathFunction) {
var flows = _flows;
var libraryEntries = _libraryEntries;
library.init(app,{
log:{audit:function(){},_:function(){},warn:function(){}},
storage: {
init: function() {
return when.resolve();
},
getAllFlows: function() {
return when.resolve(flows);
},
getFlow: function(fn) {
if (flows[fn]) {
return when.resolve(flows[fn]);
} else if (fn.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
} else {
return when.reject();
}
},
saveFlow: function(fn,data) {
if (fn.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
flows[fn] = data;
return when.resolve();
},
getLibraryEntry: function(type,path) {
if (path.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
if (libraryEntries[type] && libraryEntries[type][path]) {
return when.resolve(libraryEntries[type][path]);
} else {
return when.reject();
}
},
saveLibraryEntry: function(type,path,meta,body) {
if (path.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
libraryEntries[type][path] = body;
return when.resolve();
}
},
events: {
on: function(){},
removeListener: function(){}
},
nodes: {
getNodeExampleFlows: function() {
return _examples;
},
getNodeExampleFlowPath: _exampleFlowPathFunction
}
});
}
describe("flows", function() {
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/library/flows",library.getAll);
app.post(new RegExp("/library/flows\/(.*)"),library.post);
app.get(new RegExp("/library/flows\/(.*)"),library.get);
app.response.sendFile = function (path) {
app.response.json.call(this, {sendFile: path});
};
sinon.stub(fs,"statSync",function() { return true; });
app.post(/library\/([^\/]+)\/(.*)/,library.saveEntry);
app.get(/library\/([^\/]+)(?:$|\/(.*))/,library.getEntry);
});
after(function() {
fs.statSync.restore();
});
it('returns empty result', function(done) {
initLibrary({},{flows:{}});
it('returns all flows', function(done) {
library.init({
library: {
getEntries: function(opts) {
return Promise.resolve({a:1,b:2});
}
}
});
request(app)
.get('/library/flows')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.not.have.property('f');
res.body.should.not.have.property('d');
res.body.should.have.property('a',1);
res.body.should.have.property('b',2);
done();
});
});
it('returns 404 for non-existent entry', function(done) {
initLibrary({},{flows:{}});
request(app)
.get('/library/flows/foo')
.expect(404)
.end(done);
});
it('can store and retrieve item', function(done) {
initLibrary({},{flows:{}});
var flow = '[]';
request(app)
.post('/library/flows/foo')
.set('Content-Type', 'application/json')
.send(flow)
.expect(204).end(function (err, res) {
if (err) {
throw err;
})
it('returns an error on all flows', function(done) {
library.init({
library: {
getEntries: function(opts) {
var err = new Error("message");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
request(app)
.get('/library/flows/foo')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.text.should.equal(flow);
done();
});
});
});
it('lists a stored item', function(done) {
initLibrary({f:["bar"]});
request(app)
.get('/library/flows')
.expect(200)
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.have.property('f');
should.deepEqual(res.body.f,['bar']);
res.body.should.have.property('code');
res.body.code.should.be.equal("random_error");
res.body.should.have.property('message');
res.body.message.should.be.equal("message");
done();
});
});
it('returns 403 for malicious get attempt', function(done) {
initLibrary({});
// without the userDir override the malicious url would be
// http://127.0.0.1:1880/library/flows/../../package to
// obtain package.json from the node-red root.
request(app)
.get('/library/flows/../../../../../package')
.expect(403)
.end(done);
it('returns an individual entry - flow type', function(done) {
var opts;
library.init({
library: {
getEntry: function(_opts) {
opts = _opts;
return Promise.resolve('{"a":1,"b":2}');
}
}
});
it('returns 403 for malicious post attempt', function(done) {
initLibrary({});
// without the userDir override the malicious url would be
// http://127.0.0.1:1880/library/flows/../../package to
// obtain package.json from the node-red root.
request(app)
.post('/library/flows/../../../../../package')
.expect(403)
.end(done);
});
it('includes examples flows if set', function(done) {
var examples = {"d":{"node-module":{"f":["example-one"]}}};
initLibrary({},{},examples);
request(app)
.get('/library/flows')
.get('/library/flows/abc')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.have.property('d');
res.body.d.should.have.property('_examples_');
should.deepEqual(res.body.d._examples_,examples);
res.body.should.have.property('a',1);
res.body.should.have.property('b',2);
opts.should.have.property('type','flows');
opts.should.have.property('path','abc');
done();
});
});
it('can retrieve an example flow', function(done) {
var examples = {"d":{"node-module":{"f":["example-one"]}}};
initLibrary({},{},examples,function(module,path) {
return module + ':' + path
})
it('returns a directory listing - flow type', function(done) {
var opts;
library.init({
library: {
getEntry: function(_opts) {
opts = _opts;
return Promise.resolve({"a":1,"b":2});
}
}
});
request(app)
.get('/library/flows/_examples_/node-module/example-one')
.get('/library/flows/abc/def')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.have.property('sendFile',
'node-module:example-one');
res.body.should.have.property('a',1);
res.body.should.have.property('b',2);
opts.should.have.property('type','flows');
opts.should.have.property('path','abc/def');
done();
});
});
it('can retrieve an example flow in an org scoped package', function(done) {
var examples = {"d":{"@org_scope/node_package":{"f":["example-one"]}}};
initLibrary({},{},examples,function(module,path) {
return module + ':' + path
})
it('returns an individual entry - non-flow type', function(done) {
var opts;
library.init({
library: {
getEntry: function(_opts) {
opts = _opts;
return Promise.resolve('{"a":1,"b":2}');
}
}
});
request(app)
.get('/library/flows/_examples_/@org_scope/node_package/example-one')
.get('/library/non-flow/abc')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.have.property('sendFile',
'@org_scope/node_package:example-one');
opts.should.have.property('type','non-flow');
opts.should.have.property('path','abc');
res.text.should.eql('{"a":1,"b":2}');
done();
});
})
it('returns a directory listing - non-flow type', function(done) {
var opts;
library.init({
library: {
getEntry: function(_opts) {
opts = _opts;
return Promise.resolve({"a":1,"b":2});
}
}
});
});
describe("type", function() {
before(function() {
app = express();
app.use(bodyParser.json());
initLibrary({},{});
auth.init({settings:{}});
library.register("test");
});
it('returns empty result', function(done) {
initLibrary({},{'test':{"":[]}});
request(app)
.get('/library/test')
.get('/library/non-flow/abc/def')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.body.should.not.have.property('f');
res.body.should.have.property('a',1);
res.body.should.have.property('b',2);
opts.should.have.property('type','non-flow');
opts.should.have.property('path','abc/def');
done();
});
});
})
it('returns 404 for non-existent entry', function(done) {
initLibrary({},{});
request(app)
.get('/library/test/foo')
.expect(404)
.end(done);
});
it('can store and retrieve item', function(done) {
initLibrary({},{'test':{}});
var flow = {text:"test content"};
request(app)
.post('/library/test/foo')
.set('Content-Type', 'application/json')
.send(flow)
.expect(204).end(function (err, res) {
if (err) {
throw err;
it('returns an error on individual get', function(done) {
var opts;
library.init({
library: {
getEntry: function(_opts) {
opts = _opts;
var err = new Error("message");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
});
request(app)
.get('/library/test/foo')
.expect(200)
.get('/library/flows/123')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
return done(err);
}
res.text.should.equal(flow.text);
done();
});
});
});
opts.should.have.property('type','flows');
opts.should.have.property('path','123');
it('lists a stored item', function(done) {
initLibrary({},{'test':{'a':['abc','def']}});
request(app)
.get('/library/test/a')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
// This response isn't strictly accurate - but it
// verifies the api returns what storage gave it
should.deepEqual(res.body,['abc','def']);
res.body.should.have.property('code');
res.body.code.should.be.equal("random_error");
res.body.should.have.property('message');
res.body.message.should.be.equal("message");
done();
});
});
it('returns 403 for malicious access attempt', function(done) {
it('saves an individual entry - flow type', function(done) {
var opts;
library.init({
library: {
saveEntry: function(_opts) {
opts = _opts;
return Promise.resolve();
}
}
});
request(app)
.get('/library/test/../../../../../../../../../../etc/passwd')
.expect(403)
.end(done);
.post('/library/flows/abc/def')
.expect(204)
.send({a:1,b:2,c:3})
.end(function(err,res) {
if (err) {
return done(err);
}
opts.should.have.property('type','flows');
opts.should.have.property('path','abc/def');
opts.should.have.property('meta',{});
opts.should.have.property('body',JSON.stringify({a:1,b:2,c:3}));
done();
});
})
it('returns 403 for malicious access attempt', function(done) {
it('saves an individual entry - non-flow type', function(done) {
var opts;
library.init({
library: {
saveEntry: function(_opts) {
opts = _opts;
return Promise.resolve();
}
}
});
request(app)
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
.expect(403)
.end(done);
.post('/library/non-flow/abc/def')
.expect(204)
.send({a:1,b:2,text:"123"})
.end(function(err,res) {
if (err) {
return done(err);
}
opts.should.have.property('type','non-flow');
opts.should.have.property('path','abc/def');
opts.should.have.property('meta',{a:1,b:2});
opts.should.have.property('body',"123");
done();
});
})
it('returns 403 for malicious access attempt', function(done) {
it('returns an error on individual save', function(done) {
var opts;
library.init({
library: {
saveEntry: function(_opts) {
opts = _opts;
var err = new Error("message");
err.code = "random_error";
err.status = 400;
var p = Promise.reject(err);
p.catch(()=>{});
return p;
}
}
});
request(app)
.post('/library/test/../../../../../../../../../../etc/passwd')
.set('Content-Type', 'text/plain')
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
.expect(403)
.end(done);
});
.post('/library/non-flow/abc/def')
.send({a:1,b:2,text:"123"})
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
opts.should.have.property('type','non-flow');
opts.should.have.property('path','abc/def');
res.body.should.have.property('code');
res.body.code.should.be.equal("random_error");
res.body.should.have.property('message');
res.body.message.should.be.equal("message");
done();
});
});
});

View File

@ -20,6 +20,8 @@ var express = require('express');
var sinon = require('sinon');
var locales = require("../../../../red/api/editor/locales");
var i18n = require("../../../../red/util/i18n");
describe("api/editor/locales", function() {
beforeEach(function() {
@ -30,24 +32,18 @@ describe("api/editor/locales", function() {
var app;
before(function() {
// bit of a mess of internal workings
locales.init({
i18n: {
i: {
lng: function() { return 'en-US'},
setLng: function(lang,callback) {
if (callback) {
callback();
}
}
},
catalog: function(namespace, lang) {
return {namespace:namespace, lang:lang};
}
}
});
locales.init({});
sinon.stub(i18n.i,'lng',function() { return 'en-US'});
sinon.stub(i18n.i,'setLng',function(lang,callback) { if (callback) {callback();}});
sinon.stub(i18n,'catalog',function(namespace, lang) {return {namespace:namespace, lang:lang};});
app = express();
app.get(/locales\/(.+)\/?$/,locales.get);
});
after(function() {
i18n.i.lng.restore();
i18n.i.setLng.restore();
i18n.catalog.restore();
})
it('returns with default language', function(done) {
request(app)
.get("/locales/message-catalog")
@ -79,30 +75,31 @@ describe("api/editor/locales", function() {
var app;
before(function() {
// bit of a mess of internal workings
locales.init({
i18n: {
catalog: function(namespace, lang) {
sinon.stub(i18n,'catalog',function(namespace, lang) {
return {
"node-red": "should not return",
"test-module-a-id": "test-module-a-catalog",
"test-module-b-id": "test-module-b-catalog",
"test-module-c-id": "test-module-c-catalog"
}[namespace]
}
},
});
locales.init({
nodes: {
getNodeList: function() {
return [
getNodeList: function(opts) {
return Promise.resolve([
{module:"node-red",id:"node-red-id"},
{module:"test-module-a",id:"test-module-a-id"},
{module:"test-module-b",id:"test-module-b-id"}
];
]);
}
}
});
app = express();
app.get("/locales/nodes",locales.getAllNodes);
});
after(function() {
i18n.catalog.restore();
})
it('returns with the node catalogs', function(done) {
request(app)
.get("/locales/nodes")

View File

@ -17,215 +17,103 @@
var should = require("should");
var request = require('supertest');
var express = require('express');
var bodyParser = require("body-parser");
var sinon = require('sinon');
var when = require('when');
var app = express();
var app;
var info = require("../../../../red/api/editor/settings");
var theme = require("../../../../red/api/editor/theme");
describe("api/editor/settings", function() {
describe("settings handler", function() {
before(function() {
sinon.stub(theme,"settings",function() { return { test: 456 };});
app = express();
app.use(bodyParser.json());
app.get("/settings",info.runtimeSettings);
app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings);
app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings);
});
after(function() {
theme.settings.restore();
});
it('returns the filtered settings', function(done) {
it('returns the runtime settings', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
res.body.should.have.property("version","testVersion");
res.body.should.have.property("paletteCategories",["red","blue","green"]);
res.body.should.have.property("editorTheme",{test:456});
res.body.should.have.property("testNodeSetting","helloWorld");
res.body.should.not.have.property("foo",123);
res.body.should.have.property("flowEncryptionType","test-key-type");
done();
});
});
it('includes project settings if projects available', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
getActiveProject: () => 'test-active-project',
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("project","test-active-project");
res.body.should.not.have.property("files");
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('includes existing files details if projects enabled but no active project and files exist', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
flowFileExists: () => true,
getActiveProject: () => false,
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.not.have.property("project");
res.body.should.have.property("files");
res.body.files.should.have.property("flow",'test-flow-file');
res.body.files.should.have.property("credentials",'test-creds-file');
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('does not include file details if projects enabled but no active project and files do not exist', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
flowFileExists: () => false,
getActiveProject: () => false,
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.not.have.property("project");
res.body.should.not.have.property("files");
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('overrides palette editable if runtime says it is disabled', function(done) {
info.init({
settings: {
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function() {}
},
nodes: {
paletteEditorEnabled: function() { return false; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
res.body.should.have.property("version","testVersion");
res.body.should.have.property("paletteCategories",["red","blue","green"]);
res.body.should.have.property("editorTheme");
res.body.editorTheme.should.have.property("test",456);
res.body.editorTheme.should.have.property("palette",{editable:false});
done();
});
getRuntimeSettings: function(opts) {
return Promise.resolve({
a:1,
b:2
})
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("a",1);
res.body.should.have.property("b",2);
res.body.should.have.property("editorTheme",{test:456});
done();
});
});
it('returns the user settings', function(done) {
info.init({
settings: {
getUserSettings: function(opts) {
if (opts.user !== "fred") {
return Promise.reject(new Error("Unknown user"));
}
return Promise.resolve({
c:3,
d:4
})
}
}
});
request(app)
.get("/settings/user")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.eql({c:3,d:4});
done();
});
});
it('updates the user settings', function(done) {
var update;
info.init({
settings: {
updateUserSettings: function(opts) {
if (opts.user !== "fred") {
return Promise.reject(new Error("Unknown user"));
}
update = opts.settings;
return Promise.resolve()
}
}
});
request(app)
.post("/settings/user")
.send({
e:4,
f:5
})
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
update.should.eql({e:4,f:5});
done();
});
});
});

View File

@ -18,83 +18,42 @@ var should = require("should");
var sinon = require("sinon");
var request = require("supertest");
var express = require("express");
var editorApi = require("../../../../red/api/editor");
var comms = require("../../../../red/api/editor/comms");
var info = require("../../../../red/api/editor/settings");
var auth = require("../../../../red/api/auth");
var sshkeys = require("../../../../red/api/editor/sshkeys");
var when = require("when");
var bodyParser = require("body-parser");
var fs = require("fs-extra");
var fspath = require("path");
describe("api/editor/sshkeys", function() {
var app;
var mockList = [
'library','theme','locales','credentials','comms'
]
var isStarted = true;
var errors = [];
var session_data = {};
var mockRuntime = {
settings: {
httpNodeRoot: true,
httpAdminRoot: true,
disableEditor: false,
exportNodeSettings:function(){},
storage: {
getSessions: function(){
return when.resolve(session_data);
},
setSessions: function(_session) {
session_data = _session;
return when.resolve();
getUserKeys: function() {},
getUserKey: function() {},
generateUserKey: function() {},
removeUserKey: function() {}
}
}
},
log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}},
storage: {
projects: {
ssh: {
init: function(){},
listSSHKeys: function(){},
getSSHKey: function(){},
generateSSHKey: function(){},
deleteSSHKey: function(){}
}
}
},
events:{on:function(){},removeListener:function(){}},
isStarted: function() { return isStarted; },
nodes: {paletteEditorEnabled: function() { return false }}
};
before(function() {
auth.init(mockRuntime);
sshkeys.init(mockRuntime);
app = express();
app.use(bodyParser.json());
app.use(editorApi.init({},mockRuntime));
app.use("/settings/user/keys", sshkeys.app());
});
after(function() {
})
beforeEach(function() {
sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys");
sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey");
sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey");
sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey");
sinon.stub(mockRuntime.settings, "getUserKeys");
sinon.stub(mockRuntime.settings, "getUserKey");
sinon.stub(mockRuntime.settings, "generateUserKey");
sinon.stub(mockRuntime.settings, "removeUserKey");
})
afterEach(function() {
mockRuntime.storage.projects.ssh.listSSHKeys.restore();
mockRuntime.storage.projects.ssh.getSSHKey.restore();
mockRuntime.storage.projects.ssh.generateSSHKey.restore();
mockRuntime.storage.projects.ssh.deleteSSHKey.restore();
mockRuntime.settings.getUserKeys.restore();
mockRuntime.settings.getUserKey.restore();
mockRuntime.settings.generateUserKey.restore();
mockRuntime.settings.removeUserKey.restore();
})
it('GET /settings/user/keys --- return empty list', function(done) {
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([]));
mockRuntime.settings.getUserKeys.returns(Promise.resolve([]));
request(app)
.get("/settings/user/keys")
.expect(200)
@ -118,7 +77,7 @@ describe("api/editor/sshkeys", function() {
name: elem
};
});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList));
mockRuntime.settings.getUserKeys.returns(Promise.resolve(retList));
request(app)
.get("/settings/user/keys")
.expect(200)
@ -139,16 +98,16 @@ describe("api/editor/sshkeys", function() {
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
mockRuntime.settings.getUserKeys.returns(p);
request(app)
.get("/settings/user/keys")
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal(errInstance.code);
res.body.should.have.property('code');
res.body.code.should.be.equal(errInstance.code);
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
@ -156,7 +115,12 @@ describe("api/editor/sshkeys", function() {
});
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null));
var errInstance = new Error("Not Found.");
errInstance.code = "not_found";
errInstance.status = 404;
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.settings.getUserKey.returns(p);
request(app)
.get("/settings/user/keys/NOT_REAL")
.expect(404)
@ -168,19 +132,19 @@ describe("api/editor/sshkeys", function() {
});
});
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
var errInstance = new Error("Messages.....");
var errInstance = new Error();
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
mockRuntime.settings.getUserKeys.returns(p)
request(app)
.get("/settings/user/keys")
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('code');
res.body.code.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
done();
@ -190,7 +154,7 @@ describe("api/editor/sshkeys", function() {
it('GET /settings/user/keys/<key_file_name> --- return content', function(done) {
var key_file_name = "test_key";
var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n";
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent));
mockRuntime.settings.getUserKey.returns(Promise.resolve(fileContent));
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(200)
@ -198,7 +162,8 @@ describe("api/editor/sshkeys", function() {
if (err) {
return done(err);
}
mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true();
mockRuntime.settings.getUserKey.called.should.be.true();
mockRuntime.settings.getUserKey.firstCall.args[0].should.eql({ user: undefined, id: 'test_key' });
res.body.should.be.deepEqual({ publickey: fileContent });
done();
});
@ -210,16 +175,16 @@ describe("api/editor/sshkeys", function() {
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
mockRuntime.settings.getUserKey.returns(p);
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal(errInstance.code);
res.body.should.have.property('code');
res.body.code.should.be.equal(errInstance.code);
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
@ -231,25 +196,25 @@ describe("api/editor/sshkeys", function() {
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
mockRuntime.settings.getUserKey.returns(p);
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('code');
res.body.code.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
res.body.message.should.be.equal("Messages.....");
done();
});
});
it('POST /settings/user/keys --- success', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
mockRuntime.settings.generateUserKey.returns(Promise.resolve(key_file_name));
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
@ -262,41 +227,23 @@ describe("api/editor/sshkeys", function() {
});
});
it('POST /settings/user/keys --- return parameter error', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
request(app)
.post("/settings/user/keys")
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal("You need to have body or body.name");
done();
});
});
it('POST /settings/user/keys --- return Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
mockRuntime.settings.generateUserKey.returns(p);
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("test_code");
res.body.should.have.property('code');
res.body.code.should.be.equal("test_code");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
@ -308,26 +255,26 @@ describe("api/editor/sshkeys", function() {
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
mockRuntime.settings.generateUserKey.returns(p);
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('code');
res.body.code.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
res.body.message.should.be.equal("Messages.....");
done();
});
});
it('DELETE /settings/user/keys/<key_file_name> --- success', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true));
mockRuntime.settings.removeUserKey.returns(Promise.resolve(true));
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(204)
@ -346,16 +293,16 @@ describe("api/editor/sshkeys", function() {
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
mockRuntime.settings.removeUserKey.returns(p);
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("test_code");
res.body.should.have.property('code');
res.body.code.should.be.equal("test_code");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
@ -367,18 +314,18 @@ describe("api/editor/sshkeys", function() {
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
mockRuntime.settings.removeUserKey.returns(p);
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(400)
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('code');
res.body.code.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
res.body.message.should.be.equal('Messages.....');
done();
});
});

View File

@ -33,7 +33,7 @@ describe("api/editor/theme", function() {
fs.statSync.restore();
});
it("applies the default theme", function() {
var result = theme.init({settings:{},version:function() { return '123.456'}});
var result = theme.init({});
should.not.exist(result);
var context = theme.context();
@ -43,13 +43,12 @@ describe("api/editor/theme", function() {
context.should.have.a.property("header");
context.header.should.have.a.property("title","Node-RED");
context.header.should.have.a.property("image","red/images/node-red.png");
context.should.have.a.property("version","123.456");
should.not.exist(theme.settings());
});
it("picks up custom theme", function() {
theme.init({settings:{
theme.init({
editorTheme: {
page: {
title: "Test Page Title",
@ -84,7 +83,7 @@ describe("api/editor/theme", function() {
image: "/absolute/path/to/login/page/big/image" // a 256x256 image
}
}
}});
});
theme.app();

View File

@ -20,8 +20,6 @@ var express = require("express");
var fs = require("fs");
var path = require("path");
var EventEmitter = require('events').EventEmitter;
var events = new EventEmitter();
var ui = require("../../../../red/api/editor/ui");
@ -30,10 +28,13 @@ describe("api/editor/ui", function() {
before(function() {
ui.init({
events:events,
nodes: {
getNodeIconPath: function(module,icon) {
return path.resolve(__dirname+'/../../../../public/icons/arrow-in.png');
getIcon: function(opts) {
return new Promise(function(resolve,reject) {
fs.readFile(path.resolve(__dirname+'/../../../../public/icons/arrow-in.png'), function(err,data) {
resolve(data);
})
});
}
}
});

View File

@ -23,7 +23,6 @@ var fs = require("fs");
var path = require("path");
var api = require("../../../red/api");
var apiUtil = require("../../../red/api/util");
var apiAuth = require("../../../red/api/auth");
var apiEditor = require("../../../red/api/editor");
var apiAdmin = require("../../../red/api/admin");
@ -31,7 +30,6 @@ var apiAdmin = require("../../../red/api/admin");
describe("api/index", function() {
var beforeEach = function() {
sinon.stub(apiUtil,"init",function(){});
sinon.stub(apiAuth,"init",function(){});
sinon.stub(apiEditor,"init",function(){
var app = express();
@ -48,7 +46,6 @@ describe("api/index", function() {
});
};
var afterEach = function() {
apiUtil.init.restore();
apiAuth.init.restore();
apiAuth.login.restore();
apiEditor.init.restore();
@ -59,18 +56,14 @@ describe("api/index", function() {
afterEach(afterEach);
it("does not setup admin api if httpAdminRoot is false", function(done) {
api.init({},{
settings: { httpAdminRoot: false }
});
api.init({},{ httpAdminRoot: false },{},{});
should.not.exist(api.adminApp);
done();
});
describe('initalises admin api without adminAuth', function(done) {
before(function() {
beforeEach();
api.init({},{
settings: { }
});
api.init({},{},{},{});
});
after(afterEach);
it('exposes the editor',function(done) {
@ -87,9 +80,7 @@ describe("api/index", function() {
describe('initalises admin api without editor', function(done) {
before(function() {
beforeEach();
api.init({},{
settings: { disableEditor: true }
});
api.init({},{ disableEditor: true },{},{});
});
after(afterEach);
it('does not expose the editor',function(done) {

View File

@ -15,11 +15,17 @@
**/
var should = require("should");
var sinon = require("sinon");
var request = require('supertest');
var express = require('express');
var apiUtil = require("../../../red/api/util");
var log = require("../../../red/util").log; // TODO: separate module
var i18n = require("../../../red/util").i18n; // TODO: separate module
describe("api/util", function() {
describe("errorHandler", function() {
var loggedError = null;
@ -27,17 +33,8 @@ describe("api/util", function() {
var app;
before(function() {
app = express();
apiUtil.init({
log:{
error: function(msg) {
loggedError = msg;
},
audit: function(event) {
loggedEvent = event;
}
},
i18n:{}
})
sinon.stub(log,'error',function(msg) {loggedError = msg;});
sinon.stub(log,'audit',function(event) {loggedEvent = event;});
app.get("/tooLarge", function(req,res) {
var err = new Error();
err.message = "request entity too large";
@ -49,6 +46,10 @@ describe("api/util", function() {
throw err;
},apiUtil.errorHandler)
});
after(function() {
log.error.restore();
log.audit.restore();
})
beforeEach(function() {
loggedError = null;
loggedEvent = null;
@ -91,11 +92,13 @@ describe("api/util", function() {
})
describe('determineLangFromHeaders', function() {
var oldDefaultLang;
before(function() {
apiUtil.init({
log:{},
i18n:{defaultLang:"en-US"}
});
oldDefaultLang = i18n.defaultLang;
i18n.defaultLang = "en-US";
})
after(function() {
i18n.defaultLang = oldDefaultLang;
})
it('returns the default lang if non provided', function() {
apiUtil.determineLangFromHeaders(null).should.eql("en-US");

View File

@ -14,6 +14,229 @@
* limitations under the License.
**/
var should = require("should");
var sinon = require("sinon");
var comms = require("../../../red/runtime-api/comms");
describe("runtime-api/comms", function() {
it.skip('more tests needed', function(){})
describe("listens for events", function() {
var messages = [];
var clientConnection = {
send: function(topic,data) {
messages.push({topic,data})
}
}
var eventHandlers = {};
before(function(done) {
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);
})
afterEach(function() {
messages = [];
})
it('runtime events',function(){
eventHandlers.should.have.property('runtime-event');
eventHandlers['runtime-event']({
id: "my-event",
payload: "my-payload"
})
messages.should.have.length(1);
messages[0].should.have.property("topic","notification/my-event");
messages[0].should.have.property("data","my-payload")
})
it('status events',function(){
eventHandlers.should.have.property('node-status');
eventHandlers['node-status']({
id: "my-event",
status: "my-status"
})
messages.should.have.length(1);
messages[0].should.have.property("topic","status/my-event");
messages[0].should.have.property("data","my-status")
})
it('comms events',function(){
eventHandlers.should.have.property('runtime-event');
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(1);
messages[0].should.have.property("topic","my-topic");
messages[0].should.have.property("data","my-payload")
})
});
describe("manages connections", function() {
var eventHandlers = {};
var messages = [];
var clientConnection1 = {
send: function(topic,data) {
messages.push({topic,data})
}
}
var clientConnection2 = {
send: function(topic,data) {
messages.push({topic,data})
}
}
before(function() {
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
afterEach(function(done) {
comms.removeConnection({client: clientConnection1}).then(function() {
comms.removeConnection({client: clientConnection2}).then(done);
});
messages = [];
})
it('adds new connections',function(done){
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection1}).then(function() {
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(1);
comms.addConnection({client: clientConnection2}).then(function() {
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(3);
done();
}).catch(done);
});
});
it('removes connections',function(done){
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection1}).then(function() {
comms.addConnection({client: clientConnection2}).then(function() {
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(2);
comms.removeConnection({client: clientConnection1}).then(function() {
eventHandlers['comms']({
topic: "my-topic",
data: "my-payload"
})
messages.should.have.length(3);
done();
});
}).catch(done);
});
})
})
describe("subscriptions", function() {
var messages = [];
var clientConnection = {
send: function(topic,data) {
messages.push({topic,data})
}
}
var clientConnection2 = {
send: function(topic,data) {
messages.push({topic,data})
}
}
var eventHandlers = {};
before(function() {
comms.init({
log: {
trace: function(){}
},
events: {
removeListener: function() {},
on: function(evt,handler) {
eventHandlers[evt] = handler;
}
}
})
})
afterEach(function(done) {
messages = [];
comms.removeConnection({client: clientConnection}).then(done);
})
it('subscribe triggers retained messages',function(done){
eventHandlers['comms']({
topic: "my-event",
data: "my-payload",
retain: true
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
messages.should.have.length(1);
messages[0].should.have.property("topic","my-event");
messages[0].should.have.property("data","my-payload");
done();
});
}).catch(done);
})
it('retained messages get cleared',function(done) {
eventHandlers['comms']({
topic: "my-event",
data: "my-payload",
retain: true
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
messages.should.have.length(1);
messages[0].should.have.property("topic","my-event");
messages[0].should.have.property("data","my-payload");
// Now we have a retained message, clear it
eventHandlers['comms']({
topic: "my-event",
data: "my-payload-cleared"
});
messages.should.have.length(2);
messages[1].should.have.property("topic","my-event");
messages[1].should.have.property("data","my-payload-cleared");
// Now add a second client and subscribe - no message should arrive
return comms.addConnection({client: clientConnection2}).then(function() {
return comms.subscribe({client: clientConnection2, topic: "my-event"}).then(function() {
messages.should.have.length(2);
done();
});
});
});
}).catch(done);
});
})
});

View File

@ -17,3 +17,275 @@
describe("runtime-api/flows", function() {
it.skip('more tests needed', function(){})
});
/*
var should = require("should");
var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');
var flows = require("../../../../red/api/admin/flows");
describe("api/admin/flows", function() {
var app;
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/flows",flows.get);
app.post("/flows",flows.post);
});
it('returns flow - v1', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
}
});
request(app)
.get('/flows')
.set('Accept', 'application/json')
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.lengthOf(3);
done();
} catch(e) {
return done(e);
}
});
});
it('returns flow - v2', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
}
});
request(app)
.get('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('rev','123');
res.body.should.have.a.property('flows');
res.body.flows.should.have.lengthOf(3);
done();
} catch(e) {
return done(e);
}
});
});
it('returns flow - bad version', function(done) {
request(app)
.get('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','xxx')
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('code','invalid_api_version');
done();
} catch(e) {
return done(e);
}
});
});
it('sets flows - default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
setFlows.calledOnce.should.be.true();
setFlows.lastCall.args[1].should.eql('full');
done();
});
});
it('sets flows - non-default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-Deployment-Type','nodes')
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
setFlows.calledOnce.should.be.true();
setFlows.lastCall.args[1].should.eql('nodes');
done();
});
});
it('set flows - rejects mismatched revision - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({rev:456,flows:[4,5,6]})
.expect(409)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("code","version_mismatch");
done();
});
});
it('set flows - rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({rev:123,flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('set flows - no rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('sets flow - bad version', function(done) {
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','xxx')
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('code','invalid_api_version');
done();
} catch(e) {
return done(e);
}
});
});
it('reloads flows', function(done) {
var loadFlows = sinon.spy(function() { return when.resolve(); });
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
loadFlows: loadFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-Deployment-Type','reload')
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
loadFlows.called.should.be.true();
done();
});
});
it('returns error when set fails', function(done) {
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: function() { return when.reject(new Error("expected error")); }
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.expect(500)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("message","expected error");
done();
});
});
});
*/

View File

@ -14,6 +14,41 @@
* limitations under the License.
**/
var should = require("should");
var sinon = require("sinon");
var index = require("../../../red/runtime-api/index");
describe("runtime-api/index", function() {
it.skip('more tests needed', function(){})
before(function() {
["comms","flows","nodes","settings","library","projects"].forEach(n => {
sinon.stub(require(`../../../red/runtime-api/${n}`),"init",()=>{});
})
});
after(function() {
["comms","flows","nodes","settings","library","projects"].forEach(n => {
require(`../../../red/runtime-api/${n}`).init.restore()
})
})
it('isStarted', function(done) {
index.init({
isStarted: ()=>true
});
index.isStarted({}).then(function(started) {
started.should.be.true();
done();
}).catch(done);
})
it('isStarted', function(done) {
index.init({
version: ()=>"1.2.3.4"
});
index.version({}).then(function(version) {
version.should.eql("1.2.3.4");
done();
}).catch(done);
})
});

View File

@ -17,3 +17,342 @@
describe("runtime-api/library", function() {
it.skip('more tests needed', function(){})
});
/*
var should = require("should");
var sinon = require("sinon");
var fs = require("fs");
var fspath = require('path');
var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var when = require('when');
var app;
var library = require("../../../../red/api/editor/library");
var auth = require("../../../../red/api/auth");
describe("api/editor/library", function() {
function initLibrary(_flows,_libraryEntries,_examples,_exampleFlowPathFunction) {
var flows = _flows;
var libraryEntries = _libraryEntries;
library.init(app,{
log:{audit:function(){},_:function(){},warn:function(){}},
storage: {
init: function() {
return when.resolve();
},
getAllFlows: function() {
return when.resolve(flows);
},
getFlow: function(fn) {
if (flows[fn]) {
return when.resolve(flows[fn]);
} else if (fn.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
} else {
return when.reject();
}
},
saveFlow: function(fn,data) {
if (fn.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
flows[fn] = data;
return when.resolve();
},
getLibraryEntry: function(type,path) {
if (path.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
if (libraryEntries[type] && libraryEntries[type][path]) {
return when.resolve(libraryEntries[type][path]);
} else {
return when.reject();
}
},
saveLibraryEntry: function(type,path,meta,body) {
if (path.indexOf("..")!==-1) {
var err = new Error();
err.code = 'forbidden';
return when.reject(err);
}
libraryEntries[type][path] = body;
return when.resolve();
}
},
events: {
on: function(){},
removeListener: function(){}
},
nodes: {
getNodeExampleFlows: function() {
return _examples;
},
getNodeExampleFlowPath: _exampleFlowPathFunction
}
});
}
describe("flows", function() {
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/library/flows",library.getAll);
app.post(new RegExp("/library/flows\/(.*)"),library.post);
app.get(new RegExp("/library/flows\/(.*)"),library.get);
app.response.sendFile = function (path) {
app.response.json.call(this, {sendFile: path});
};
sinon.stub(fs,"statSync",function() { return true; });
});
after(function() {
fs.statSync.restore();
});
it('returns empty result', function(done) {
initLibrary({},{flows:{}});
request(app)
.get('/library/flows')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.not.have.property('f');
res.body.should.not.have.property('d');
done();
});
});
it('returns 404 for non-existent entry', function(done) {
initLibrary({},{flows:{}});
request(app)
.get('/library/flows/foo')
.expect(404)
.end(done);
});
it('can store and retrieve item', function(done) {
initLibrary({},{flows:{}});
var flow = '[]';
request(app)
.post('/library/flows/foo')
.set('Content-Type', 'application/json')
.send(flow)
.expect(204).end(function (err, res) {
if (err) {
throw err;
}
request(app)
.get('/library/flows/foo')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.text.should.equal(flow);
done();
});
});
});
it('lists a stored item', function(done) {
initLibrary({f:["bar"]});
request(app)
.get('/library/flows')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property('f');
should.deepEqual(res.body.f,['bar']);
done();
});
});
it('returns 403 for malicious get attempt', function(done) {
initLibrary({});
// without the userDir override the malicious url would be
// http://127.0.0.1:1880/library/flows/../../package to
// obtain package.json from the node-red root.
request(app)
.get('/library/flows/../../../../../package')
.expect(403)
.end(done);
});
it('returns 403 for malicious post attempt', function(done) {
initLibrary({});
// without the userDir override the malicious url would be
// http://127.0.0.1:1880/library/flows/../../package to
// obtain package.json from the node-red root.
request(app)
.post('/library/flows/../../../../../package')
.expect(403)
.end(done);
});
it('includes examples flows if set', function(done) {
var examples = {"d":{"node-module":{"f":["example-one"]}}};
initLibrary({},{},examples);
request(app)
.get('/library/flows')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property('d');
res.body.d.should.have.property('_examples_');
should.deepEqual(res.body.d._examples_,examples);
done();
});
});
it('can retrieve an example flow', function(done) {
var examples = {"d":{"node-module":{"f":["example-one"]}}};
initLibrary({},{},examples,function(module,path) {
return module + ':' + path
});
request(app)
.get('/library/flows/_examples_/node-module/example-one')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property('sendFile',
'node-module:example-one');
done();
});
});
it('can retrieve an example flow in an org scoped package', function(done) {
var examples = {"d":{"@org_scope/node_package":{"f":["example-one"]}}};
initLibrary({},{},examples,function(module,path) {
return module + ':' + path
});
request(app)
.get('/library/flows/_examples_/@org_scope/node_package/example-one')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property('sendFile',
'@org_scope/node_package:example-one');
done();
});
});
});
describe("type", function() {
before(function() {
app = express();
app.use(bodyParser.json());
initLibrary({},{});
auth.init({settings:{}});
library.register("test");
});
it('returns empty result', function(done) {
initLibrary({},{'test':{"":[]}});
request(app)
.get('/library/test')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.not.have.property('f');
done();
});
});
it('returns 404 for non-existent entry', function(done) {
initLibrary({},{});
request(app)
.get('/library/test/foo')
.expect(404)
.end(done);
});
it('can store and retrieve item', function(done) {
initLibrary({},{'test':{}});
var flow = {text:"test content"};
request(app)
.post('/library/test/foo')
.set('Content-Type', 'application/json')
.send(flow)
.expect(204).end(function (err, res) {
if (err) {
throw err;
}
request(app)
.get('/library/test/foo')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.text.should.equal(flow.text);
done();
});
});
});
it('lists a stored item', function(done) {
initLibrary({},{'test':{'a':['abc','def']}});
request(app)
.get('/library/test/a')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
// This response isn't strictly accurate - but it
// verifies the api returns what storage gave it
should.deepEqual(res.body,['abc','def']);
done();
});
});
it('returns 403 for malicious access attempt', function(done) {
request(app)
.get('/library/test/../../../../../../../../../../etc/passwd')
.expect(403)
.end(done);
});
it('returns 403 for malicious access attempt', function(done) {
request(app)
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
.expect(403)
.end(done);
});
it('returns 403 for malicious access attempt', function(done) {
request(app)
.post('/library/test/../../../../../../../../../../etc/passwd')
.set('Content-Type', 'text/plain')
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
.expect(403)
.end(done);
});
});
});
*/

View File

@ -17,3 +17,829 @@
describe("runtime-api/nodes", function() {
it.skip('more tests needed', function(){})
});
/*
var should = require("should");
var request = require('supertest');
var express = require('express');
var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');
var nodes = require("../../../../red/api/admin/nodes");
var apiUtil = require("../../../../red/api/util");
describe("api/admin/nodes", function() {
var app;
function initNodes(runtime) {
runtime.log = {
audit:function(e){},//console.log(e)},
_:function(){},
info: function(){},
warn: function(){}
}
runtime.events = {
emit: function(){}
}
nodes.init(runtime);
}
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/nodes",nodes.getAll);
app.post("/nodes",nodes.post);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
app.get("/getIcons",nodes.getIcons);
app.delete("/nodes/:id",nodes.delete);
sinon.stub(apiUtil,"determineLangFromHeaders", function() {
return "en-US";
});
});
after(function() {
apiUtil.determineLangFromHeaders.restore();
})
describe('get nodes', function() {
it('returns node list', function(done) {
initNodes({
nodes:{
getNodeList: function() {
return [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) {
initNodes({
nodes:{
getNodeConfigs: function() {
return "<script></script>";
}
},
i18n: {
determineLangFromHeaders: function(){}
}
});
request(app)
.get('/nodes')
.set('Accept', 'text/html')
.expect(200)
.expect("<script></script>")
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns node module info', function(done) {
initNodes({
nodes:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
}
}
});
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) {
initNodes({
nodes:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
}
}
});
request(app)
.get('/nodes/node-blue')
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns individual node info', function(done) {
initNodes({
nodes:{
getNodeInfo: function(id) {
return {"node-red/123":{id:"node-red/123"}}[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) {
initNodes({
nodes:{
getNodeConfig: function(id) {
return {"node-red/123":"<script></script>"}[id];
}
},
i18n: {
determineLangFromHeaders: function(){}
}
});
request(app)
.get('/nodes/node-red/123')
.set('Accept', 'text/html')
.expect(200)
.expect("<script></script>")
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 404 for unknown node', function(done) {
initNodes({
nodes:{
getNodeInfo: function(id) {
return {"node-red/123":{id:"node-red/123"}}[id];
}
}
});
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('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
});
request(app)
.post('/nodes')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 400 if request is invalid', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.post('/nodes')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
describe('by module', function() {
it('installs the module and returns module info', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null; },
installModule: function() {
return when.resolve({
name:"foo",
nodes:[{id:"123"}]
});
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.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");
done();
});
});
it('fails the install if already installed', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return {nodes:{id:"123"}}; },
installModule: function() {
return when.resolve({id:"123"});
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('fails the install if module error', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
installModule: function() {
return when.reject(new Error("test error"));
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Error: test error");
done();
});
});
it('fails the install if module not found', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
installModule: function() {
var err = new Error("test error");
err.code = 404;
return when.reject(err);
}
}
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
});
});
describe('delete', function() {
it('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
});
request(app)
.del('/nodes/123')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
describe('by module', function() {
it('uninstalls the module', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.resolve({id:"123"});}
}
});
request(app)
.del('/nodes/foo')
.expect(204)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('fails the uninstall if the module is not installed', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null },
getNodeInfo: function() { return null }
}
});
request(app)
.del('/nodes/foo')
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('fails the uninstall if the module is not installed', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.reject(new Error("test error"));}
}
});
request(app)
.del('/nodes/foo')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Error: test error");
done();
});
});
});
});
describe('enable/disable', function() {
it('returns 400 if settings are unavailable', function(done) {
initNodes({
settings:{available:function(){return false}}
});
request(app)
.put('/nodes/123')
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 400 for invalid node payload', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/node-red/foo')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Invalid request");
done();
});
});
it('returns 400 for invalid module payload', function(done) {
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/foo')
.send({})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("message","Invalid request");
done();
});
});
it('returns 404 for unknown node', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return null }
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:false})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('returns 404 for unknown module', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function(id) { return null }
}
});
request(app)
.put('/nodes/node-blue')
.send({enabled:false})
.expect(404)
.end(function(err,res) {
if (err) {
throw err;
}
done();
});
});
it('enables disabled node', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: false} },
enableNode: function() { return when.resolve({id:"123",enabled: true,types:['a']}); }
}
});
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);
done();
});
});
it('disables enabled node', function(done) {
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: true} },
disableNode: function() { return when.resolve({id:"123",enabled: false,types:['a']}); }
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:false})
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",false);
done();
});
});
describe('no-ops if already in the right state', function() {
function run(state,done) {
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: state} },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.false();
disableNodeCalled.should.be.false();
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
describe('does not no-op if err on node', function() {
function run(state,done) {
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
initNodes({
settings:{available:function(){return true}},
nodes:{
getNodeInfo: function() { return {id:"123",enabled: state, err:"foo"} },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red/foo')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.equal(state);
disableNodeCalled.should.be.equal(!state);
res.body.should.have.property("id","123");
res.body.should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
it('enables disabled module', function(done) {
var n1 = {id:"123",enabled:false,types:['a']};
var n2 = {id:"456",enabled:false,types:['b']};
var enableNode = sinon.stub();
enableNode.onFirstCall().returns((function() {
n1.enabled = true;
return when.resolve(n1);
})());
enableNode.onSecondCall().returns((function() {
n2.enabled = true;
return when.resolve(n2);
})());
enableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
enableNode: enableNode
}
});
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");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",true);
res.body.nodes[1].should.have.property("enabled",true);
done();
});
});
it('disables enabled module', function(done) {
var n1 = {id:"123",enabled:true,types:['a']};
var n2 = {id:"456",enabled:true,types:['b']};
var disableNode = sinon.stub();
disableNode.onFirstCall().returns((function() {
n1.enabled = false;
return when.resolve(n1);
})());
disableNode.onSecondCall().returns((function() {
n2.enabled = false;
return when.resolve(n2);
})());
disableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:false})
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",false);
res.body.nodes[1].should.have.property("enabled",false);
done();
});
});
describe('no-ops if a node in module already in the right state', function() {
function run(state,done) {
var node = {id:"123",enabled:state,types:['a']};
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.false();
disableNodeCalled.should.be.false();
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
describe('does not no-op if err on a node in module', function() {
function run(state,done) {
var node = {id:"123",enabled:state,types:['a'],err:"foo"};
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
nodes:{
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
enableNode: enableNode,
disableNode: disableNode
}
});
request(app)
.put('/nodes/node-red')
.send({enabled:state})
.expect(200)
.end(function(err,res) {
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
if (err) {
throw err;
}
enableNodeCalled.should.be.equal(state);
disableNodeCalled.should.be.equal(!state);
res.body.should.have.property("name","node-red");
res.body.should.have.property("nodes");
res.body.nodes[0].should.have.property("enabled",state);
done();
});
}
it('already enabled', function(done) {
run(true,done);
});
it('already disabled', function(done) {
run(false,done);
});
});
});
describe('get icons', function() {
it('returns icon list', function(done) {
initNodes({
nodes:{
getNodeIcons: function() {
return {"module":["1.png","2.png","3.png"]};
}
}
});
request(app)
.get('/getIcons')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
console.log(res.body);
res.body.should.have.property("module");
res.body.module.should.be.an.Array();
res.body.module.should.have.lengthOf(3);
done();
});
});
});
});
*/

View File

@ -17,3 +17,569 @@
describe("runtime-api/settings", function() {
it.skip('more tests needed', function(){})
});
/*
it('returns the filtered settings', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
res.body.should.have.property("version","testVersion");
res.body.should.have.property("paletteCategories",["red","blue","green"]);
res.body.should.have.property("editorTheme",{test:456});
res.body.should.have.property("testNodeSetting","helloWorld");
res.body.should.not.have.property("foo",123);
res.body.should.have.property("flowEncryptionType","test-key-type");
done();
});
});
it('includes project settings if projects available', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
getActiveProject: () => 'test-active-project',
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("project","test-active-project");
res.body.should.not.have.property("files");
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('includes existing files details if projects enabled but no active project and files exist', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
flowFileExists: () => true,
getActiveProject: () => false,
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.not.have.property("project");
res.body.should.have.property("files");
res.body.files.should.have.property("flow",'test-flow-file');
res.body.files.should.have.property("credentials",'test-creds-file');
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('does not include file details if projects enabled but no active project and files do not exist', function(done) {
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function(obj) {
obj.testNodeSetting = "helloWorld";
}
},
nodes: {
paletteEditorEnabled: function() { return true; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {
projects: {
flowFileExists: () => false,
getActiveProject: () => false,
getFlowFilename: () => 'test-flow-file',
getCredentialsFilename: () => 'test-creds-file',
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
}
}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.not.have.property("project");
res.body.should.not.have.property("files");
res.body.should.have.property("git");
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
done();
});
});
it('overrides palette editable if runtime says it is disabled', function(done) {
info.init({
settings: {
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"],
exportNodeSettings: function() {}
},
nodes: {
paletteEditorEnabled: function() { return false; },
getCredentialKeyType: function() { return "test-key-type"}
},
log: { error: console.error },
storage: {}
});
request(app)
.get("/settings")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
res.body.should.have.property("version","testVersion");
res.body.should.have.property("paletteCategories",["red","blue","green"]);
res.body.should.have.property("editorTheme");
res.body.editorTheme.should.have.property("test",456);
res.body.editorTheme.should.have.property("palette",{editable:false});
done();
});
})
*/
/*
var should = require("should");
var sinon = require("sinon");
var request = require("supertest");
var express = require("express");
var editorApi = require("../../../../red/api/editor");
var comms = require("../../../../red/api/editor/comms");
var info = require("../../../../red/api/editor/settings");
var auth = require("../../../../red/api/auth");
var sshkeys = require("../../../../red/api/editor/sshkeys");
var when = require("when");
var bodyParser = require("body-parser");
var fs = require("fs-extra");
var fspath = require("path");
describe("api/editor/sshkeys", function() {
var app;
var mockList = [
'library','theme','locales','credentials','comms'
]
var isStarted = true;
var errors = [];
var session_data = {};
var mockRuntime = {
settings:{
httpNodeRoot: true,
httpAdminRoot: true,
disableEditor: false,
exportNodeSettings:function(){},
storage: {
getSessions: function(){
return when.resolve(session_data);
},
setSessions: function(_session) {
session_data = _session;
return when.resolve();
}
}
},
log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}},
storage: {
projects: {
ssh: {
init: function(){},
listSSHKeys: function(){},
getSSHKey: function(){},
generateSSHKey: function(){},
deleteSSHKey: function(){}
}
}
},
events:{on:function(){},removeListener:function(){}},
isStarted: function() { return isStarted; },
nodes: {paletteEditorEnabled: function() { return false }}
};
before(function() {
auth.init(mockRuntime);
app = express();
app.use(bodyParser.json());
app.use(editorApi.init({},mockRuntime));
});
after(function() {
})
beforeEach(function() {
sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys");
sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey");
sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey");
sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey");
})
afterEach(function() {
mockRuntime.storage.projects.ssh.listSSHKeys.restore();
mockRuntime.storage.projects.ssh.getSSHKey.restore();
mockRuntime.storage.projects.ssh.generateSSHKey.restore();
mockRuntime.storage.projects.ssh.deleteSSHKey.restore();
})
it('GET /settings/user/keys --- return empty list', function(done) {
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([]));
request(app)
.get("/settings/user/keys")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('keys');
res.body.keys.should.be.empty();
done();
});
});
it('GET /settings/user/keys --- return normal list', function(done) {
var fileList = [
'test_key01',
'test_key02'
];
var retList = fileList.map(function(elem) {
return {
name: elem
};
});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList));
request(app)
.get("/settings/user/keys")
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('keys');
for (var item of retList) {
res.body.keys.should.containEql(item);
}
done();
});
});
it('GET /settings/user/keys --- return Error', function(done) {
var errInstance = new Error("Messages here.....");
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
request(app)
.get("/settings/user/keys")
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal(errInstance.code);
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
});
});
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null));
request(app)
.get("/settings/user/keys/NOT_REAL")
.expect(404)
.end(function(err,res) {
if (err) {
return done(err);
}
done();
});
});
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
request(app)
.get("/settings/user/keys")
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
done();
});
});
it('GET /settings/user/keys/<key_file_name> --- return content', function(done) {
var key_file_name = "test_key";
var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n";
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent));
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true();
res.body.should.be.deepEqual({ publickey: fileContent });
done();
});
});
it('GET /settings/user/keys/<key_file_name> --- return Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal(errInstance.code);
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
});
});
it('GET /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
request(app)
.get("/settings/user/keys/" + key_file_name)
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
done();
});
});
it('POST /settings/user/keys --- success', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
done();
});
});
it('POST /settings/user/keys --- return parameter error', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
request(app)
.post("/settings/user/keys")
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal("You need to have body or body.name");
done();
});
});
it('POST /settings/user/keys --- return Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("test_code");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
});
});
it('POST /settings/user/keys --- return Unexpected error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
request(app)
.post("/settings/user/keys")
.send({ name: key_file_name })
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
done();
});
});
it('DELETE /settings/user/keys/<key_file_name> --- success', function(done) {
var key_file_name = "test_key";
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true));
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(204)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.be.deepEqual({});
done();
});
});
it('DELETE /settings/user/keys/<key_file_name> --- return Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
errInstance.code = "test_code";
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("test_code");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.message);
done();
});
});
it('DELETE /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
var key_file_name = "test_key";
var errInstance = new Error("Messages.....");
var p = Promise.reject(errInstance);
p.catch(()=>{});
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
request(app)
.delete("/settings/user/keys/" + key_file_name)
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property('error');
res.body.error.should.be.equal("unexpected_error");
res.body.should.have.property('message');
res.body.message.should.be.equal(errInstance.toString());
done();
});
});
});
*/

View File

@ -14,7 +14,6 @@
* limitations under the License.
**/
var should = require("should");
var when = require("when");
var sinon = require("sinon");
var path = require("path");
@ -45,6 +44,7 @@ describe("runtime", function() {
log: sinon.stub(),
warn: sinon.stub(),
info: sinon.stub(),
trace: sinon.stub(),
metric: sinon.stub().returns(!!metrics),
_: function() { return "abc"}
},
@ -90,11 +90,11 @@ describe("runtime", function() {
var redNodesLoadFlows;
var redNodesStartFlows;
beforeEach(function() {
storageInit = sinon.stub(storage,"init",function(settings) {return when.resolve();});
storageInit = sinon.stub(storage,"init",function(settings) {return Promise.resolve();});
redNodesInit = sinon.stub(redNodes,"init", function() {});
redNodesLoad = sinon.stub(redNodes,"load", function() {return when.resolve()});
redNodesLoad = sinon.stub(redNodes,"load", function() {return Promise.resolve()});
redNodesCleanModuleList = sinon.stub(redNodes,"cleanModuleList",function(){});
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {return when.resolve()});
redNodesLoadFlows = sinon.stub(redNodes,"loadFlows",function() {return Promise.resolve()});
redNodesStartFlows = sinon.stub(redNodes,"startFlows",function() {});
});
afterEach(function() {
@ -114,7 +114,7 @@ describe("runtime", function() {
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return when.resolve();}},util);
runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
// sinon.stub(console,"log");
runtime.start().then(function() {
// console.log.restore();
@ -143,9 +143,9 @@ describe("runtime", function() {
{ module:"node-red",enabled:true,loaded:false,types:["typeC","typeD"]} // missing
].filter(cb);
});
var serverInstallModule = sinon.stub(redNodes,"installModule",function(name) { return when.resolve({nodes:[]});});
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 when.resolve();}},util);
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
@ -172,7 +172,7 @@ describe("runtime", function() {
].filter(cb);
});
var util = mockUtil();
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return when.resolve();}},util);
runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();
@ -190,7 +190,7 @@ describe("runtime", function() {
var stopFlows = sinon.stub(redNodes,"stopFlows",function() {} );
redNodesGetNodeList = sinon.stub(redNodes,"getNodeList", function() {return []});
var util = mockUtil(true);
runtime.init({testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return when.resolve();}},util);
runtime.init({testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return Promise.resolve();}},util);
sinon.stub(console,"log");
runtime.start().then(function() {
console.log.restore();

View File

@ -14,6 +14,167 @@
* limitations under the License.
**/
var should = require("should");
var sinon = require("sinon");
var fs = require("fs");
var library = require("../../../../red/runtime/library/index")
var mockLog = {
log: sinon.stub(),
debug: sinon.stub(),
trace: sinon.stub(),
warn: sinon.stub(),
info: sinon.stub(),
metric: sinon.stub(),
audit: sinon.stub(),
_: function() { return "abc"}
}
describe("runtime/library", function() {
it.skip('more tests needed', function(){})
describe("register", function() {
// it("throws error for duplicate type", function() {
// library.init({});
// library.register("unknown","/abc");
// should(()=>{library.register("unknown","/abc")} ).throw();
// })
})
describe("getEntry", function() {
before(function() {
library.init({
log: mockLog,
storage: {
getLibraryEntry: function(type,path) {
return Promise.resolve({type,path});
},
getFlow: function(path) {
return Promise.resolve({path});
}
},
nodes: {
getNodeExampleFlowPath: function(module,entryPath) {
if (module === "unknown") {
return null;
}
return "/tmp/"+module+"/"+entryPath;
}
}
});
sinon.stub(fs,"readFile", function(path,opts,callback) {
if (path === "/tmp/test-module/abc") {
callback(null,"Example flow result");
} else if (path === "/tmp/@scope/test-module/abc") {
callback(null,"Example scope flow result");
} else if (path === "/tmp/test-module/throw") {
throw new Error("Instant error")
} else {
callback(new Error("Unexpected path:"+path))
}
})
});
after(function() {
fs.readFile.restore();
})
it('throws error for unregistered type', function() {
should(()=>{library.getEntry("unknown","/abc")} ).throw();
});
it('returns a registered non-flow entry', function(done) {
library.register("test-module","test-type");
library.getEntry("test-type","/abc").then(function(result) {
result.should.have.property("type","test-type")
result.should.have.property("path","/abc")
done();
}).catch(done);
});
it ('returns a flow entry', function(done) {
library.getEntry("flows","/abc").then(function(result) {
result.should.have.property("path","/abc")
done();
}).catch(done);
});
it ('returns a flow example entry', function(done) {
library.getEntry("flows","_examples_/test-module/abc").then(function(result) {
result.should.eql("Example flow result");
done();
}).catch(done);
});
it ('returns a flow example entry from scoped module', function(done) {
library.getEntry("flows","_examples_/@scope/test-module/abc").then(function(result) {
result.should.eql("Example scope flow result");
done();
}).catch(done);
});
it ('returns an error for unknown flow example entry', function(done) {
library.getEntry("flows","_examples_/unknown/abc").then(function(result) {
done(new Error("No error thrown"))
}).catch(function(err) {
err.should.have.property("code","not_found");
done();
});
});
it ('returns an error for file load error - async', function(done) {
library.getEntry("flows","_examples_/test-module/unknown").then(function(result) {
done(new Error("No error thrown"))
}).catch(function(err) {
done();
});
});
it ('returns an error for file load error - sync', function(done) {
library.getEntry("flows","_examples_/test-module/throw").then(function(result) {
done(new Error("No error thrown"))
}).catch(function(err) {
done();
});
});
});
describe("saveEntry", function() {
before(function() {
library.init({
log: mockLog,
storage: {
saveLibraryEntry: function(type, path, meta, body) {
return Promise.resolve({type,path,meta,body})
},
saveFlow: function(path,body) {
return Promise.resolve({path,body});
}
},
nodes: {
getNodeExampleFlowPath: function(module,entryPath) {
if (module === "unknown") {
return null;
}
return "/tmp/"+module+"/"+entryPath;
}
}
});
});
it('throws error for unregistered type', function() {
should(()=>{library.saveEntry("unknown","/abc",{id:"meta"},{id:"body"})} ).throw();
});
it('saves a flow entry', function(done) {
library.saveEntry('flows','/abc',{id:"meta"},{id:"body"}).then(function(result) {
result.should.have.property("path","/abc");
result.should.have.property("body",{id:"body"});
done();
}).catch(done);
})
it('saves a non-flow entry', function(done) {
library.register("test-module","test-type");
library.saveEntry('test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) {
result.should.have.property("type","test-type");
result.should.have.property("path","/abc");
result.should.have.property("meta",{id:"meta"});
result.should.have.property("body",{id:"body"});
done();
}).catch(done);
})
});
});

View File

@ -110,7 +110,7 @@ describe('Node', function() {
p.then(function() {
callbacksClosed.should.eql(3);
testdone();
}).otherwise(function(e) {
}).catch(function(e) {
testdone(e);
});
});

View File

@ -427,7 +427,7 @@ describe('red/runtime/nodes/credentials', function() {
// credentials.dirty().should.be.true();
// should.not.exist(credentials.get("node"));
done();
}).otherwise(function(err) {
}).catch(function(err) {
err.should.have.property('code','credentials_load_failed');
done();
});
@ -443,7 +443,7 @@ describe('red/runtime/nodes/credentials', function() {
// credentials.dirty().should.be.true();
// should.not.exist(credentials.get("node"));
done();
}).otherwise(function(err) {
}).catch(function(err) {
err.should.have.property('code','credentials_load_failed');
done();
});

View File

@ -524,7 +524,7 @@ describe('flows/index', function() {
]
}).then(function() {
done(new Error('failed to reject duplicate node id'));
}).otherwise(function(err) {
}).catch(function(err) {
done();
})
});
@ -559,7 +559,7 @@ describe('flows/index', function() {
createdFlows.should.have.lengthOf(3);
createdFlows[2].should.eql(id);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
})
});

View File

@ -19,7 +19,6 @@ var fs = require('fs-extra');
var path = require('path');
var when = require("when");
var sinon = require('sinon');
console.log(__dirname);
var index = require("../../../../red/runtime/nodes/index");
var flows = require("../../../../red/runtime/nodes/flows");
var registry = require("../../../../red/runtime/nodes/registry");
@ -81,7 +80,7 @@ describe("red/nodes/index", function() {
testnode.credentials.should.have.property('c',"2");
testnode.credentials.should.have.property('d',"bar");
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -93,7 +92,7 @@ describe("red/nodes/index", function() {
// console.log(index.getFlows());
should.deepEqual(testFlows, index.getFlows().flows);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
@ -258,7 +257,7 @@ describe("red/nodes/index", function() {
info.should.eql(randomNodeInfo);
done();
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -273,7 +272,7 @@ describe("red/nodes/index", function() {
}).should.throw();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -288,7 +287,7 @@ describe("red/nodes/index", function() {
}).should.throw();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -341,7 +340,7 @@ describe("red/nodes/index", function() {
}).should.throw();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -356,7 +355,7 @@ describe("red/nodes/index", function() {
}).should.throw();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -60,7 +60,7 @@ describe('red/nodes/registry/index', function() {
registry.addModule("foo").then(function(info) {
info.should.eql("info");
done();
}).otherwise(function(err) { done(err); });
}).catch(function(err) { done(err); });
});
it('rejects if loader rejects', function(done) {
stubs.push(sinon.stub(loader,"addModule",function(module) {
@ -71,7 +71,7 @@ describe('red/nodes/registry/index', function() {
}));
registry.addModule("foo").then(function(info) {
done(new Error("unexpected resolve"));
}).otherwise(function(err) {
}).catch(function(err) {
err.should.eql("error");
done();
})
@ -90,7 +90,7 @@ describe('red/nodes/registry/index', function() {
typeRegistry.enableNodeSet.called.should.be.true();
ns.should.have.a.property('id','node-set');
done();
}).otherwise(function(err) { done(err); });
}).catch(function(err) { done(err); });
});
it('rejects if node unknown',function() {
@ -121,7 +121,7 @@ describe('red/nodes/registry/index', function() {
ns.should.have.a.property('id','node-set');
ns.should.have.a.property('loaded',true);
done();
}).otherwise(function(err) { done(err); });
}).catch(function(err) { done(err); });
});
});

View File

@ -83,7 +83,7 @@ describe('nodes/registry/installer', function() {
return ee;
});
installer.installModule("this_wont_exist").otherwise(function(err) {
installer.installModule("this_wont_exist").catch(function(err) {
err.should.have.property("code",404);
done();
});
@ -105,7 +105,7 @@ describe('nodes/registry/installer', function() {
}
});
installer.installModule("this_wont_exist","0.1.2").otherwise(function(err) {
installer.installModule("this_wont_exist","0.1.2").catch(function(err) {
err.code.should.be.eql(404);
done();
});
@ -116,7 +116,7 @@ describe('nodes/registry/installer', function() {
version: "0.1.1"
}
});
installer.installModule("this_wont_exist","0.1.1").otherwise(function(err) {
installer.installModule("this_wont_exist","0.1.1").catch(function(err) {
err.code.should.be.eql('module_already_loaded');
done();
});
@ -135,7 +135,7 @@ describe('nodes/registry/installer', function() {
installer.installModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).otherwise(function(err) {
}).catch(function(err) {
done();
});
});
@ -160,7 +160,7 @@ describe('nodes/registry/installer', function() {
// commsMessages[0].topic.should.equal("node/added");
// commsMessages[0].msg.should.eql(nodeInfo.nodes);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -169,7 +169,7 @@ describe('nodes/registry/installer', function() {
var resourcesDir = path.resolve(path.join(__dirname,"..","resources","local","TestNodeModule","node_modules","NonExistant"));
installer.installModule(resourcesDir).then(function() {
done(new Error("Unexpected success"));
}).otherwise(function(err) {
}).catch(function(err) {
if (err.hasOwnProperty("code")) {
err.code.should.eql(404);
done();
@ -199,7 +199,7 @@ describe('nodes/registry/installer', function() {
installer.installModule(resourcesDir).then(function(info) {
info.should.eql(nodeInfo);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -228,7 +228,7 @@ describe('nodes/registry/installer', function() {
installer.uninstallModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).otherwise(function(err) {
}).catch(function(err) {
done();
});
});
@ -252,7 +252,7 @@ describe('nodes/registry/installer', function() {
// commsMessages[0].topic.should.equal("node/removed");
// commsMessages[0].msg.should.eql(nodeInfo);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -69,7 +69,7 @@ describe("red/nodes/registry/loader",function() {
loader.load("foo",true).then(function() {
registry.saveNodeList.called.should.be.true();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
})
});
@ -118,7 +118,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.lastCall.args[1].should.eql('test-node-1');
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -169,7 +169,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.secondCall.args[1].should.eql('test-node-multiple-1b');
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -219,7 +219,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.lastCall.args[1].should.eql('test-node-2');
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -267,7 +267,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.calledOnce.should.be.false();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -313,7 +313,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.calledOnce.should.be.false();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -360,7 +360,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.calledOnce.should.be.false();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -379,7 +379,7 @@ describe("red/nodes/registry/loader",function() {
stubs.push(sinon.stub(registry,"getModuleInfo",function(){return{}}));
loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}});
loader.addModule("test-module").otherwise(function(err) {
loader.addModule("test-module").catch(function(err) {
err.code.should.eql("module_already_loaded");
done();
});
@ -390,7 +390,7 @@ describe("red/nodes/registry/loader",function() {
throw new Error("failure");
}));
loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}});
loader.addModule("test-module").otherwise(function(err) {
loader.addModule("test-module").catch(function(err) {
err.message.should.eql("failure");
done();
});
@ -441,7 +441,7 @@ describe("red/nodes/registry/loader",function() {
nodes.registerType.calledOnce.should.be.true();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -477,7 +477,7 @@ describe("red/nodes/registry/loader",function() {
registry.addNodeSet.called.should.be.false();
nodes.registerType.called.should.be.false();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -498,7 +498,7 @@ describe("red/nodes/registry/loader",function() {
node.enabled.should.be.false();
nodes.registerType.called.should.be.false();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -517,7 +517,7 @@ describe("red/nodes/registry/loader",function() {
node.err.toString().should.eql("Error: fail to require (line:1)");
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -292,7 +292,7 @@ describe("red/nodes/registry/registry",function() {
it('rejects when settings unavailable',function(done) {
typeRegistry.init(stubSettings({},false,{}));
typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1");
typeRegistry.saveNodeList().otherwise(function(err) {
typeRegistry.saveNodeList().catch(function(err) {
done();
});
});
@ -312,7 +312,7 @@ describe("red/nodes/registry/registry",function() {
nn.should.not.have.property('id');
}
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -14,7 +14,6 @@
* limitations under the License.
**/
var should = require("should");
var when = require("when");
var settings = require("../../../red/runtime/settings");
@ -86,12 +85,12 @@ describe("red/settings", function() {
var saveCount = 0;
var storage = {
getSettings: function() {
return when.resolve({globalA:789});
return Promise.resolve({globalA:789});
},
saveSettings: function(settings) {
saveCount++;
savedSettings = settings;
return when.resolve();
return Promise.resolve();
}
}
settings.init(userSettings);
@ -115,7 +114,7 @@ describe("red/settings", function() {
done();
});
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -47,7 +47,7 @@ describe('storage/localfilesystem', function() {
fs.existsSync(path.join(userDir,"lib")).should.be.true();
fs.existsSync(path.join(userDir,"lib",'flows')).should.be.true();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -70,7 +70,7 @@ describe('storage/localfilesystem', function() {
} finally {
process.env.NODE_RED_HOME = oldNRH;
}
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -96,7 +96,7 @@ describe('storage/localfilesystem', function() {
process.env.NODE_RED_HOME = oldNRH;
process.env.NODE_HOMEPATH = oldHOMEPATH;
}
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -124,7 +124,7 @@ describe('storage/localfilesystem', function() {
process.env.HOME = oldHOME;
process.env.HOMEPATH = oldHOMEPATH;
}
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -155,7 +155,7 @@ describe('storage/localfilesystem', function() {
process.env.HOMEPATH = oldHOMEPATH;
process.env.USERPROFILE = oldUSERPROFILE;
}
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -168,10 +168,10 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql([]);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -186,10 +186,10 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql([]);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -208,11 +208,11 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql(testFlow);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
},50);
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -230,13 +230,13 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql(testFlow);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -257,13 +257,13 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql(testFlow);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -278,13 +278,13 @@ describe('storage/localfilesystem', function() {
localfilesystem.getFlows().then(function(flows) {
flows.should.eql(testFlow);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -298,11 +298,11 @@ describe('storage/localfilesystem', function() {
fs.fsync.callCount.should.be.greaterThan(0);
fs.fsync.restore();
done();
}).otherwise(function(err) {
}).catch(function(err) {
fs.fsync.restore();
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -321,10 +321,10 @@ describe('storage/localfilesystem', function() {
fs.fsync.callCount.should.be.greaterThan(0);
fs.fsync.restore();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -358,14 +358,14 @@ describe('storage/localfilesystem', function() {
content2.should.not.equal(backupContent);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
@ -382,10 +382,10 @@ describe('storage/localfilesystem', function() {
localfilesystem.getCredentials().then(function(creds) {
creds.should.eql({});
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -406,13 +406,13 @@ describe('storage/localfilesystem', function() {
localfilesystem.getCredentials().then(function(creds) {
creds.should.eql(credentials);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -437,10 +437,10 @@ describe('storage/localfilesystem', function() {
fs.existsSync(credFile).should.be.true();
fs.existsSync(credFileBackup).should.be.true();
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -463,13 +463,13 @@ describe('storage/localfilesystem', function() {
localfilesystem.getCredentials().then(function(creds) {
creds.should.eql(credentials);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -38,7 +38,7 @@ describe('storage/localfilesystem/sessions', function() {
localfilesystemSessions.getSessions().then(function(sessions) {
sessions.should.eql({});
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -51,7 +51,7 @@ describe('storage/localfilesystem/sessions', function() {
localfilesystemSessions.getSessions().then(function(sessions) {
sessions.should.eql({});
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -69,10 +69,10 @@ describe('storage/localfilesystem/sessions', function() {
localfilesystemSessions.getSessions().then(function(_sessions) {
_sessions.should.eql(sessions);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});

View File

@ -39,7 +39,7 @@ describe('storage/localfilesystem/settings', function() {
localfilesystemSettings.getSettings().then(function(settings) {
settings.should.eql({});
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -52,7 +52,7 @@ describe('storage/localfilesystem/settings', function() {
localfilesystemSettings.getSettings().then(function(settings) {
settings.should.eql({});
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});
@ -70,10 +70,10 @@ describe('storage/localfilesystem/settings', function() {
localfilesystemSettings.getSettings().then(function(_settings) {
_settings.should.eql(settings);
done();
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
}).otherwise(function(err) {
}).catch(function(err) {
done(err);
});
});