diff --git a/red/api/admin/flows.js b/red/api/admin/flows.js
index 35b234dfa..ccad8718f 100644
--- a/red/api/admin/flows.js
+++ b/red/api/admin/flows.js
@@ -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,
diff --git a/red/api/admin/nodes.js b/red/api/admin/nodes.js
index 6dbe1cc05..31f231f78 100644
--- a/red/api/admin/nodes.js
+++ b/red/api/admin/nodes.js
@@ -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 = {
diff --git a/red/api/editor/comms.js b/red/api/editor/comms.js
index b7d2fea89..10741bd08 100644
--- a/red/api/editor/comms.js
+++ b/red/api/editor/comms.js
@@ -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";
diff --git a/red/api/editor/index.js b/red/api/editor/index.js
index 58e698d03..fb617c154 100644
--- a/red/api/editor/index.js
+++ b/red/api/editor/index.js
@@ -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;
}
diff --git a/red/api/editor/sshkeys.js b/red/api/editor/sshkeys.js
index 62ccdc574..3e7b0de87 100644
--- a/red/api/editor/sshkeys.js
+++ b/red/api/editor/sshkeys.js
@@ -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);
diff --git a/red/api/index.js b/red/api/index.js
index 298dacdd5..91059ce6b 100644
--- a/red/api/index.js
+++ b/red/api/index.js
@@ -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; }
};
diff --git a/red/api/util.js b/red/api/util.js
index 6a6d97e1d..4f87e3ce2 100644
--- a/red/api/util.js
+++ b/red/api/util.js
@@ -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({
- code: err.code||"unexpected_error",
- message: err.message
- })
- }
- res.end();
+ res.status(err.status||500).json({
+ code: err.code||"unexpected_error",
+ message: err.message||err.toString()
+ });
}
}
diff --git a/red/red.js b/red/red.js
index 717d74507..5e9da6402 100644
--- a/red/red.js
+++ b/red/red.js
@@ -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 },
diff --git a/red/runtime-api/comms.js b/red/runtime-api/comms.js
index d89123795..3ab361f3d 100644
--- a/red/runtime-api/comms.js
+++ b/red/runtime-api/comms.js
@@ -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);
diff --git a/red/runtime-api/flows.js b/red/runtime-api/flows.js
index ba52090a0..30a6e83ef 100644
--- a/red/runtime-api/flows.js
+++ b/red/runtime-api/flows.js
@@ -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);
}
diff --git a/red/runtime/index.js b/red/runtime/index.js
index 05f7fcdba..b9a2b21ab 100644
--- a/red/runtime/index.js
+++ b/red/runtime/index.js
@@ -42,9 +42,6 @@ var stubbedExpressApp = {
delete: function() {}
}
var adminApi = {
- library: {
- register: function() {}
- },
auth: {
needsPermission: function() {}
},
diff --git a/red/runtime/library/index.js b/red/runtime/library/index.js
index ce95d66ff..e0d8b6d82 100644
--- a/red/runtime/library/index.js
+++ b/red/runtime/library/index.js
@@ -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);
}
+ // 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
diff --git a/red/runtime/nodes/registry/loader.js b/red/runtime/nodes/registry/loader.js
index c580939c3..f4c3bd51f 100644
--- a/red/runtime/nodes/registry/loader.js
+++ b/red/runtime/nodes/registry/loader.js
@@ -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() {}
};
diff --git a/test/nodes/helper.js b/test/nodes/helper.js
index 31290d5f3..4e165103b 100644
--- a/test/nodes/helper.js
+++ b/test/nodes/helper.js
@@ -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");
diff --git a/test/red/api/admin/flow_spec.js b/test/red/api/admin/flow_spec.js
index e39b92da8..1f7f9e5e4 100644
--- a/test/red/api/admin/flow_spec.js
+++ b/test/red/api/admin/flow_spec.js
@@ -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({
+ 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;
+ }
}
}
- };
- flow.init({
- settings:{},
- nodes: nodes,
- log:{ audit: sinon.stub() }
});
})
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({
+ 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;
+ }
}
}
- };
- flow.init({
- settings:{},
- nodes: nodes,
- log:{ audit: sinon.stub() }
});
})
- 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();
});
})
diff --git a/test/red/api/admin/flows_spec.js b/test/red/api/admin/flows_spec.js
index 57b8de99c..e55b38933 100644
--- a/test/red/api/admin/flows_spec.js
+++ b/test/red/api/admin/flows_spec.js
@@ -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();
- });
- });
-
});
diff --git a/test/red/api/admin/nodes_spec.js b/test/red/api/admin/nodes_spec.js
index 1720b2527..fb970a65c 100644
--- a/test/red/api/admin/nodes_spec.js
+++ b/test/red/api/admin/nodes_spec.js
@@ -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 "";
+ return Promise.resolve("");
}
},
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":""}[id];
+ getNodeConfig: function(opts) {
+ return Promise.resolve({"node-red/123":""}[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,142 +208,96 @@ describe("api/admin/nodes", function() {
});
describe('install', function() {
-
- it('returns 400 if settings are unavailable', function(done) {
- initNodes({
- settings:{available:function(){return false}}
+ it('installs the module and returns module info', function(done) {
+ var opts;
+ nodes.init({
+ nodes:{
+ addModule: function(_opts) {
+ opts = _opts;
+ return Promise.resolve({
+ name:"foo",
+ nodes:[{id:"123"}]
+ });
+ }
+ }
+ });
+ request(app)
+ .post('/nodes')
+ .send({module: 'foo',version:"1.2.3"})
+ .expect(200)
+ .end(function(err,res) {
+ if (err) {
+ throw err;
+ }
+ res.body.should.have.property("name","foo");
+ res.body.should.have.property("nodes");
+ res.body.nodes[0].should.have.property("id","123");
+ opts.should.have.property("module","foo");
+ opts.should.have.property("version","1.2.3");
+ done();
+ });
+ });
+ it('returns error', function(done) {
+ nodes.init({
+ nodes:{
+ addModule: function(opts) {
+ var errInstance = new Error("Message");
+ errInstance.code = "random_error";
+ errInstance.status = 400;
+ var p = Promise.reject(errInstance);
+ p.catch(()=>{});
+ return p;
+ }
+ }
});
request(app)
.post('/nodes')
+ .send({module: 'foo',version:"1.2.3"})
.expect(400)
.end(function(err,res) {
if (err) {
throw err;
}
+ res.body.should.have.a.property('code','random_error');
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}}
+ 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,93 +305,18 @@ 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}},
- 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}}
+ describe('enable/disable node set', function() {
+ it('returns 400 for invalid request payload', function(done) {
+ nodes.init({
+ nodes:{
+ setNodeSetState: function(opts) {return Promise.resolve()}
+ }
});
request(app)
.put('/nodes/node-red/foo')
@@ -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}},
- 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}},
+ it('sets module state and returns module info', function(done) {
+ var opts;
+ nodes.init({
nodes:{
- getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
- enableNode: enableNode
+ setModuleState: function(_opts) {
+ opts = _opts;
+ return Promise.resolve({name:"node-red"});
+ }
}
});
@@ -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);
diff --git a/test/red/api/auth/index_spec.js b/test/red/api/auth/index_spec.js
index 9849b602c..ef01d2391 100644
--- a/test/red/api/auth/index_spec.js
+++ b/test/red/api/auth/index_spec.js
@@ -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"))
+ });
+ });
+ });
});
diff --git a/test/red/api/auth/strategies_spec.js b/test/red/api/auth/strategies_spec.js
index db8e290bf..5002e8b4b 100644
--- a/test/red/api/auth/strategies_spec.js
+++ b/test/red/api/auth/strategies_spec.js
@@ -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() {
diff --git a/test/red/api/editor/comms_spec.js b/test/red/api/editor/comms_spec.js
index c2e9dabe2..eb9f6582c 100644
--- a/test/red/api/editor/comms_spec.js
+++ b/test/red/api/editor/comms_spec.js
@@ -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{});
+ 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);
+ }
+ }
+ })
+ });
});
diff --git a/test/red/api/editor/index_spec.js b/test/red/api/editor/index_spec.js
index b6b93964c..7e84395f5 100644
--- a/test/red/api/editor/index_spec.js
+++ b/test/red/api/editor/index_spec.js
@@ -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);
diff --git a/test/red/api/editor/library_spec.js b/test/red/api/editor/library_spec.js
index 65e53e459..ef4590b69 100644
--- a/test/red/api/editor/library_spec.js
+++ b/test/red/api/editor/library_spec.js
@@ -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();
+ before(function() {
+ app = express();
+ app.use(bodyParser.json());
+
+ app.get("/library/flows",library.getAll);
+ app.post(/library\/([^\/]+)\/(.*)/,library.saveEntry);
+ app.get(/library\/([^\/]+)(?:$|\/(.*))/,library.getEntry);
+ });
+ after(function() {
+ });
+ it('returns all flows', function(done) {
+ library.init({
+ library: {
+ getEntries: function(opts) {
+ return Promise.resolve({a:1,b:2});
}
- },
- 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')
+ .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);
+ done();
});
- 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('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;
+ }
+ }
});
-
- 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')
+ .expect(400)
+ .end(function(err,res) {
+ if (err) {
+ return done(err);
+ }
+ 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();
});
- 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 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 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();
- });
+ request(app)
+ .get('/library/flows/abc')
+ .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);
+ opts.should.have.property('type','flows');
+ opts.should.have.property('path','abc');
+ done();
+ });
+ })
+ 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});
+ }
+ }
});
-
- it('returns 404 for non-existent entry', function(done) {
- initLibrary({},{});
- request(app)
- .get('/library/test/foo')
- .expect(404)
- .end(done);
+ request(app)
+ .get('/library/flows/abc/def')
+ .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);
+ opts.should.have.property('type','flows');
+ opts.should.have.property('path','abc/def');
+ done();
+ });
+ })
+ 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}');
+ }
+ }
});
-
- 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();
- });
- });
+ request(app)
+ .get('/library/non-flow/abc')
+ .expect(200)
+ .end(function(err,res) {
+ if (err) {
+ return done(err);
+ }
+ 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});
+ }
+ }
});
+ request(app)
+ .get('/library/non-flow/abc/def')
+ .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);
+ opts.should.have.property('type','non-flow');
+ opts.should.have.property('path','abc/def');
+ 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 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/flows/123')
+ .expect(400)
+ .end(function(err,res) {
+ if (err) {
+ return done(err);
+ }
+ opts.should.have.property('type','flows');
+ opts.should.have.property('path','123');
+
+ 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) {
- request(app)
- .get('/library/test/../../../../../../../../../../etc/passwd')
- .expect(403)
- .end(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)
+ .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) {
- request(app)
- .get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
- .expect(403)
- .end(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)
+ .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) {
- 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);
+ 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/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();
+ });
});
});
diff --git a/test/red/api/editor/locales_spec.js b/test/red/api/editor/locales_spec.js
index f6d1ff762..d2ee32d4a 100644
--- a/test/red/api/editor/locales_spec.js
+++ b/test/red/api/editor/locales_spec.js
@@ -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")
diff --git a/test/red/api/editor/settings_spec.js b/test/red/api/editor/settings_spec.js
index 6f3089cf9..842c2509d 100644
--- a/test/red/api/editor/settings_spec.js
+++ b/test/red/api/editor/settings_spec.js
@@ -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.get("/settings",info.runtimeSettings);
- });
+ 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();
- });
+ after(function() {
+ theme.settings.restore();
+ });
- 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'}}
- }
+ it('returns the runtime settings', function(done) {
+ info.init({
+ settings: {
+ 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("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.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")
- .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/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)
- .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();
- });
+ 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();
+ });
});
});
diff --git a/test/red/api/editor/sshkeys_spec.js b/test/red/api/editor/sshkeys_spec.js
index fbcd3ec88..a6ab51f99 100644
--- a/test/red/api/editor/sshkeys_spec.js
+++ b/test/red/api/editor/sshkeys_spec.js
@@ -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();
- }
- }
- },
- 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 }}
- };
-
+ settings: {
+ getUserKeys: function() {},
+ getUserKey: function() {},
+ generateUserKey: function() {},
+ removeUserKey: function() {}
+ }
+ }
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/ --- 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/ --- 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/ --- 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();
});
});
diff --git a/test/red/api/editor/theme_spec.js b/test/red/api/editor/theme_spec.js
index 513a8e912..88d265875 100644
--- a/test/red/api/editor/theme_spec.js
+++ b/test/red/api/editor/theme_spec.js
@@ -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();
diff --git a/test/red/api/editor/ui_spec.js b/test/red/api/editor/ui_spec.js
index 9ebfa8185..0704adcd4 100644
--- a/test/red/api/editor/ui_spec.js
+++ b/test/red/api/editor/ui_spec.js
@@ -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);
+ })
+ });
}
}
});
diff --git a/test/red/api/index_spec.js b/test/red/api/index_spec.js
index 103949dea..a97ec3843 100644
--- a/test/red/api/index_spec.js
+++ b/test/red/api/index_spec.js
@@ -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) {
diff --git a/test/red/api/util_spec.js b/test/red/api/util_spec.js
index fb5151c14..bb6b27521 100644
--- a/test/red/api/util_spec.js
+++ b/test/red/api/util_spec.js
@@ -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");
diff --git a/test/red/runtime-api/comms_spec.js b/test/red/runtime-api/comms_spec.js
index 1db07365b..8cc252b90 100644
--- a/test/red/runtime-api/comms_spec.js
+++ b/test/red/runtime-api/comms_spec.js
@@ -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);
+ });
+ })
+
});
diff --git a/test/red/runtime-api/flows_spec.js b/test/red/runtime-api/flows_spec.js
index ddbab2b9d..502eaddca 100644
--- a/test/red/runtime-api/flows_spec.js
+++ b/test/red/runtime-api/flows_spec.js
@@ -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();
+ });
+ });
+
+});
+
+*/
diff --git a/test/red/runtime-api/index_spec.js b/test/red/runtime-api/index_spec.js
index 650f96da1..c5e1865e2 100644
--- a/test/red/runtime-api/index_spec.js
+++ b/test/red/runtime-api/index_spec.js
@@ -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);
+ })
+
});
diff --git a/test/red/runtime-api/library_spec.js b/test/red/runtime-api/library_spec.js
index f996ea487..7c41a32de 100644
--- a/test/red/runtime-api/library_spec.js
+++ b/test/red/runtime-api/library_spec.js
@@ -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);
+ });
+
+ });
+});
+
+*/
diff --git a/test/red/runtime-api/nodes_spec.js b/test/red/runtime-api/nodes_spec.js
index a22b49081..80237816e 100644
--- a/test/red/runtime-api/nodes_spec.js
+++ b/test/red/runtime-api/nodes_spec.js
@@ -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 "";
+ }
+ },
+ i18n: {
+ determineLangFromHeaders: function(){}
+ }
+ });
+ request(app)
+ .get('/nodes')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect("")
+ .end(function(err,res) {
+ if (err) {
+ throw err;
+ }
+ done();
+ });
+ });
+
+ it('returns node module info', function(done) {
+ 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":""}[id];
+ }
+ },
+ i18n: {
+ determineLangFromHeaders: function(){}
+ }
+ });
+ request(app)
+ .get('/nodes/node-red/123')
+ .set('Accept', 'text/html')
+ .expect(200)
+ .expect("")
+ .end(function(err,res) {
+ if (err) {
+ throw err;
+ }
+ done();
+ });
+ });
+
+ it('returns 404 for unknown node', function(done) {
+ 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();
+ });
+ });
+ });
+});
+
+*/
diff --git a/test/red/runtime-api/settings_spec.js b/test/red/runtime-api/settings_spec.js
index 77c2090e8..668ae96a3 100644
--- a/test/red/runtime-api/settings_spec.js
+++ b/test/red/runtime-api/settings_spec.js
@@ -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/ --- 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/ --- 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/ --- 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/ --- 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/ --- 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/ --- 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/ --- 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();
+ });
+ });
+});
+*/
diff --git a/test/red/runtime/index_spec.js b/test/red/runtime/index_spec.js
index 59ab9c3dc..499dfecfc 100644
--- a/test/red/runtime/index_spec.js
+++ b/test/red/runtime/index_spec.js
@@ -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();
diff --git a/test/red/runtime/library/index_spec.js b/test/red/runtime/library/index_spec.js
index bfb550835..882d4d75a 100644
--- a/test/red/runtime/library/index_spec.js
+++ b/test/red/runtime/library/index_spec.js
@@ -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);
+ })
+
+ });
});
diff --git a/test/red/runtime/nodes/Node_spec.js b/test/red/runtime/nodes/Node_spec.js
index 80bae7654..c2df3a8ec 100644
--- a/test/red/runtime/nodes/Node_spec.js
+++ b/test/red/runtime/nodes/Node_spec.js
@@ -110,7 +110,7 @@ describe('Node', function() {
p.then(function() {
callbacksClosed.should.eql(3);
testdone();
- }).otherwise(function(e) {
+ }).catch(function(e) {
testdone(e);
});
});
diff --git a/test/red/runtime/nodes/credentials_spec.js b/test/red/runtime/nodes/credentials_spec.js
index b4a324bcc..75a9fc3cc 100644
--- a/test/red/runtime/nodes/credentials_spec.js
+++ b/test/red/runtime/nodes/credentials_spec.js
@@ -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();
});
diff --git a/test/red/runtime/nodes/flows/index_spec.js b/test/red/runtime/nodes/flows/index_spec.js
index 68f896b58..c39085187 100644
--- a/test/red/runtime/nodes/flows/index_spec.js
+++ b/test/red/runtime/nodes/flows/index_spec.js
@@ -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);
})
});
diff --git a/test/red/runtime/nodes/index_spec.js b/test/red/runtime/nodes/index_spec.js
index 10f309227..f36918a10 100644
--- a/test/red/runtime/nodes/index_spec.js
+++ b/test/red/runtime/nodes/index_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/nodes/registry/index_spec.js b/test/red/runtime/nodes/registry/index_spec.js
index 30cd8685e..590d60040 100644
--- a/test/red/runtime/nodes/registry/index_spec.js
+++ b/test/red/runtime/nodes/registry/index_spec.js
@@ -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); });
});
});
diff --git a/test/red/runtime/nodes/registry/installer_spec.js b/test/red/runtime/nodes/registry/installer_spec.js
index dd9c782f4..78566a3d3 100644
--- a/test/red/runtime/nodes/registry/installer_spec.js
+++ b/test/red/runtime/nodes/registry/installer_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/nodes/registry/loader_spec.js b/test/red/runtime/nodes/registry/loader_spec.js
index 87e1c060b..4f9ddf8a7 100644
--- a/test/red/runtime/nodes/registry/loader_spec.js
+++ b/test/red/runtime/nodes/registry/loader_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/nodes/registry/registry_spec.js b/test/red/runtime/nodes/registry/registry_spec.js
index ce761c012..c6f3e19af 100644
--- a/test/red/runtime/nodes/registry/registry_spec.js
+++ b/test/red/runtime/nodes/registry/registry_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/settings_spec.js b/test/red/runtime/settings_spec.js
index d3b5f35ed..4766e85e7 100644
--- a/test/red/runtime/settings_spec.js
+++ b/test/red/runtime/settings_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/storage/localfilesystem/index_spec.js b/test/red/runtime/storage/localfilesystem/index_spec.js
index ed2b4e010..9921581b2 100644
--- a/test/red/runtime/storage/localfilesystem/index_spec.js
+++ b/test/red/runtime/storage/localfilesystem/index_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/storage/localfilesystem/sessions_spec.js b/test/red/runtime/storage/localfilesystem/sessions_spec.js
index aa2322d3c..cbfc3cf84 100644
--- a/test/red/runtime/storage/localfilesystem/sessions_spec.js
+++ b/test/red/runtime/storage/localfilesystem/sessions_spec.js
@@ -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);
});
});
diff --git a/test/red/runtime/storage/localfilesystem/settings_spec.js b/test/red/runtime/storage/localfilesystem/settings_spec.js
index 3785b7ce0..56fd10cf9 100644
--- a/test/red/runtime/storage/localfilesystem/settings_spec.js
+++ b/test/red/runtime/storage/localfilesystem/settings_spec.js
@@ -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);
});
});