mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Fixup all the tests
This commit is contained in:
parent
34832d5942
commit
5d064aa1d7
@ -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,
|
||||
|
@ -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 = {
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
};
|
||||
|
@ -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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
17
red/red.js
17
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 },
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ var stubbedExpressApp = {
|
||||
delete: function() {}
|
||||
}
|
||||
var adminApi = {
|
||||
library: {
|
||||
register: function() {}
|
||||
},
|
||||
auth: {
|
||||
needsPermission: function() {}
|
||||
},
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
})
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -27,31 +27,17 @@ var apiUtil = require("../../../../red/api/util");
|
||||
describe("api/admin/nodes", function() {
|
||||
|
||||
var app;
|
||||
function initNodes(runtime) {
|
||||
runtime.log = {
|
||||
audit:function(e){},//console.log(e)},
|
||||
_:function(){},
|
||||
info: function(){},
|
||||
warn: function(){}
|
||||
}
|
||||
runtime.events = {
|
||||
emit: function(){}
|
||||
}
|
||||
nodes.init(runtime);
|
||||
|
||||
}
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/nodes",nodes.getAll);
|
||||
app.post("/nodes",nodes.post);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
|
||||
app.get("/getIcons",nodes.getIcons);
|
||||
app.delete("/nodes/:id",nodes.delete);
|
||||
app.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.delete);
|
||||
sinon.stub(apiUtil,"determineLangFromHeaders", function() {
|
||||
return "en-US";
|
||||
});
|
||||
@ -62,10 +48,10 @@ describe("api/admin/nodes", function() {
|
||||
|
||||
describe('get nodes', function() {
|
||||
it('returns node list', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getNodeList: function() {
|
||||
return [1,2,3];
|
||||
return Promise.resolve([1,2,3]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -84,10 +70,10 @@ describe("api/admin/nodes", function() {
|
||||
});
|
||||
|
||||
it('returns node configs', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getNodeConfigs: function() {
|
||||
return "<script></script>";
|
||||
return Promise.resolve("<script></script>");
|
||||
}
|
||||
},
|
||||
i18n: {
|
||||
@ -108,10 +94,10 @@ describe("api/admin/nodes", function() {
|
||||
});
|
||||
|
||||
it('returns node module info', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getModuleInfo: function(id) {
|
||||
return {"node-red":{name:"node-red"}}[id];
|
||||
getModuleInfo: function(opts) {
|
||||
return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -128,10 +114,15 @@ describe("api/admin/nodes", function() {
|
||||
});
|
||||
|
||||
it('returns 404 for unknown module', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getModuleInfo: function(id) {
|
||||
return {"node-red":{name:"node-red"}}[id];
|
||||
getModuleInfo: function(opts) {
|
||||
var errInstance = new Error("Not Found");
|
||||
errInstance.code = "not_found";
|
||||
errInstance.status = 404;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -147,10 +138,10 @@ describe("api/admin/nodes", function() {
|
||||
});
|
||||
|
||||
it('returns individual node info', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getNodeInfo: function(id) {
|
||||
return {"node-red/123":{id:"node-red/123"}}[id];
|
||||
getNodeInfo: function(opts) {
|
||||
return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -168,10 +159,10 @@ describe("api/admin/nodes", function() {
|
||||
});
|
||||
|
||||
it('returns individual node configs', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getNodeConfig: function(id) {
|
||||
return {"node-red/123":"<script></script>"}[id];
|
||||
getNodeConfig: function(opts) {
|
||||
return Promise.resolve({"node-red/123":"<script></script>"}[opts.id]);
|
||||
}
|
||||
},
|
||||
i18n: {
|
||||
@ -190,12 +181,16 @@ describe("api/admin/nodes", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown node', function(done) {
|
||||
initNodes({
|
||||
nodes.init({
|
||||
nodes:{
|
||||
getNodeInfo: function(id) {
|
||||
return {"node-red/123":{id:"node-red/123"}}[id];
|
||||
getNodeInfo: function(opts) {
|
||||
var errInstance = new Error("Not Found");
|
||||
errInstance.code = "not_found";
|
||||
errInstance.status = 404;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -213,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);
|
||||
|
@ -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"))
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -33,6 +33,24 @@ var listenPort = 0; // use ephemeral port
|
||||
|
||||
|
||||
describe("api/editor/comms", function() {
|
||||
var connections = [];
|
||||
var mockComms = {
|
||||
addConnection: function(opts) {
|
||||
connections.push(opts.client);
|
||||
return Promise.resolve()
|
||||
},
|
||||
removeConnection: function(opts) {
|
||||
for (var i=0;i<connections.length;i++) {
|
||||
if (connections[i] === opts.client) {
|
||||
connections.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
subscribe: function() { return Promise.resolve()},
|
||||
unsubscribe: function() { return Promise.resolve(); }
|
||||
}
|
||||
|
||||
describe("with default keepalive", function() {
|
||||
var server;
|
||||
@ -41,11 +59,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -63,9 +77,15 @@ describe("api/editor/comms", function() {
|
||||
|
||||
it('accepts connection', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
connections.length.should.eql(0);
|
||||
ws.on('open', function() {
|
||||
ws.close();
|
||||
done();
|
||||
try {
|
||||
connections.length.should.eql(1);
|
||||
ws.close();
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -73,7 +93,8 @@ describe("api/editor/comms", function() {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic1"}');
|
||||
comms.publish('topic1', 'foo');
|
||||
connections.length.should.eql(1);
|
||||
connections[0].send('topic1', 'foo');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('[{"topic":"topic1","data":"foo"}]');
|
||||
@ -82,43 +103,13 @@ describe("api/editor/comms", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('publishes retained message for subscription', function(done) {
|
||||
comms.publish('topic2', 'bar', true);
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic2"}');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
console.log(msg);
|
||||
msg.should.equal('[{"topic":"topic2","data":"bar"}]');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retained message is deleted by non-retained message', function(done) {
|
||||
comms.publish('topic3', 'retained', true);
|
||||
comms.publish('topic3', 'non-retained');
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic3"}');
|
||||
comms.publish('topic3', 'new');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
console.log(msg);
|
||||
msg.should.equal('[{"topic":"topic3","data":"new"}]');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('malformed messages are ignored',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('not json');
|
||||
ws.send('[]');
|
||||
ws.send('{"subscribe":"topic3"}');
|
||||
comms.publish('topic3', 'correct');
|
||||
connections[0].send('topic3', 'correct');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
console.log(msg);
|
||||
@ -127,40 +118,16 @@ describe("api/editor/comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// The following test currently fails due to minimum viable
|
||||
// implementation. More test should be written to test topic
|
||||
// matching once this one is passing
|
||||
|
||||
it.skip('receives message on correct topic', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic4"}');
|
||||
comms.publish('topic5', 'foo');
|
||||
comms.publish('topic4', 'bar');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
console.log(msg);
|
||||
msg.should.equal('[{"topic":"topic4","data":"bar"}]');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('listens for node/status events');
|
||||
});
|
||||
|
||||
describe("disabled editor", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
sinon.stub(Users,"default",function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{disableEditor:true},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {disableEditor:true}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -177,12 +144,14 @@ describe("api/editor/comms", function() {
|
||||
});
|
||||
|
||||
it('rejects websocket connections',function(done) {
|
||||
connections.length.should.eql(0);
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
done(new Error("Socket connection unexpectedly accepted"));
|
||||
ws.close();
|
||||
});
|
||||
ws.on('error', function() {
|
||||
connections.length.should.eql(0);
|
||||
done();
|
||||
});
|
||||
|
||||
@ -196,11 +165,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{httpAdminRoot:"/adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {httpAdminRoot:"/adminPath"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -217,8 +182,10 @@ describe("api/editor/comms", function() {
|
||||
});
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
connections.length.should.eql(0);
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
connections.length.should.eql(1);
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
@ -236,11 +203,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server,{
|
||||
settings:{httpAdminRoot:"/adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {httpAdminRoot:"/adminPath/"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -257,8 +220,10 @@ describe("api/editor/comms", function() {
|
||||
});
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
connections.length.should.eql(0);
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
connections.length.should.eql(1);
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
@ -276,11 +241,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{httpAdminRoot:"adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {httpAdminRoot:"adminPath"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -297,8 +258,10 @@ describe("api/editor/comms", function() {
|
||||
});
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
connections.length.should.eql(0);
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
connections.length.should.eql(1);
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
@ -316,11 +279,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{webSocketKeepAliveTime: 100},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {webSocketKeepAliveTime: 100}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -355,7 +314,7 @@ describe("api/editor/comms", function() {
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
interval = setInterval(function() {
|
||||
comms.publish('foo', 'bar');
|
||||
connections[0].send('foo', 'bar');
|
||||
}, 50);
|
||||
});
|
||||
ws.on('message', function(data) {
|
||||
@ -403,11 +362,7 @@ describe("api/editor/comms", function() {
|
||||
|
||||
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server,{
|
||||
settings:{adminAuth:{}},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {adminAuth:{}}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -447,7 +402,7 @@ describe("api/editor/comms", function() {
|
||||
if (received == 1) {
|
||||
msg.should.equal('{"auth":"ok"}');
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
comms.publish('foo', 'correct');
|
||||
connections[0].send('foo', 'correct');
|
||||
} else {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
ws.close();
|
||||
@ -494,11 +449,7 @@ describe("api/editor/comms", function() {
|
||||
before(function(done) {
|
||||
getDefaultUser = sinon.stub(Users,"default",function() { return when.resolve({permissions:"read"});});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {
|
||||
settings:{adminAuth:{}},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
comms.init(server, {adminAuth:{}}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@ -520,7 +471,7 @@ describe("api/editor/comms", function() {
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
setTimeout(function() {
|
||||
comms.publish('foo', 'correct');
|
||||
connections[0].send('foo', 'correct');
|
||||
},200);
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
|
@ -29,64 +29,30 @@ describe('api/editor/credentials', function() {
|
||||
app = express();
|
||||
app.get('/credentials/:type/:id',credentials.get);
|
||||
credentials.init({
|
||||
log:{audit:function(){}},
|
||||
nodes:{
|
||||
getCredentials: function(id) {
|
||||
if (id === "n1") {
|
||||
return {user1:"abc",password1:"123"};
|
||||
flows: {
|
||||
getNodeCredentials: function(opts) {
|
||||
if (opts.type === "known-type" && opts.id === "n1") {
|
||||
return Promise.resolve({
|
||||
user1:"abc",
|
||||
has_password1: true
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getCredentialDefinition:function(type) {
|
||||
if (type === "known-type") {
|
||||
return {user1:{type:"text"},password1:{type:"password"}};
|
||||
} else {
|
||||
return null;
|
||||
var err = new Error("message");
|
||||
err.code = "test_code";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
it('returns empty credentials if unknown type',function(done) {
|
||||
request(app)
|
||||
.get("/credentials/unknown-type/n1")
|
||||
.expect(200)
|
||||
.expect("Content-Type",/json/)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
try {
|
||||
res.body.should.eql({});
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
it('returns empty credentials if none are stored',function(done) {
|
||||
request(app)
|
||||
.get("/credentials/known-type/n2")
|
||||
.expect("Content-Type",/json/)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
try {
|
||||
res.body.should.eql({});
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('returns stored credentials',function(done) {
|
||||
request(app)
|
||||
.get("/credentials/known-type/n1")
|
||||
.expect("Content-Type",/json/)
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
done(err);
|
||||
@ -102,5 +68,26 @@ describe('api/editor/credentials', function() {
|
||||
}
|
||||
})
|
||||
});
|
||||
it('returns any error',function(done) {
|
||||
request(app)
|
||||
.get("/credentials/unknown-type/n2")
|
||||
.expect("Content-Type",/json/)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
try {
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal('message');
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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")
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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/<key_file_name> --- return 404', function(done) {
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null));
|
||||
var errInstance = new Error("Not Found.");
|
||||
errInstance.code = "not_found";
|
||||
errInstance.status = 404;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.settings.getUserKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/NOT_REAL")
|
||||
.expect(404)
|
||||
@ -168,19 +132,19 @@ describe("api/editor/sshkeys", function() {
|
||||
});
|
||||
});
|
||||
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var errInstance = new Error();
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
|
||||
mockRuntime.settings.getUserKeys.returns(p)
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
@ -190,7 +154,7 @@ describe("api/editor/sshkeys", function() {
|
||||
it('GET /settings/user/keys/<key_file_name> --- return content', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n";
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent));
|
||||
mockRuntime.settings.getUserKey.returns(Promise.resolve(fileContent));
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(200)
|
||||
@ -198,7 +162,8 @@ describe("api/editor/sshkeys", function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true();
|
||||
mockRuntime.settings.getUserKey.called.should.be.true();
|
||||
mockRuntime.settings.getUserKey.firstCall.args[0].should.eql({ user: undefined, id: 'test_key' });
|
||||
res.body.should.be.deepEqual({ publickey: fileContent });
|
||||
done();
|
||||
});
|
||||
@ -210,16 +175,16 @@ describe("api/editor/sshkeys", function() {
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
mockRuntime.settings.getUserKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
@ -231,25 +196,25 @@ describe("api/editor/sshkeys", function() {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
mockRuntime.settings.getUserKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
res.body.message.should.be.equal("Messages.....");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
mockRuntime.settings.generateUserKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
@ -262,41 +227,23 @@ describe("api/editor/sshkeys", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return parameter error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal("You need to have body or body.name");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
mockRuntime.settings.generateUserKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
@ -308,26 +255,26 @@ describe("api/editor/sshkeys", function() {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
mockRuntime.settings.generateUserKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
res.body.message.should.be.equal("Messages.....");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true));
|
||||
mockRuntime.settings.removeUserKey.returns(Promise.resolve(true));
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(204)
|
||||
@ -346,16 +293,16 @@ describe("api/editor/sshkeys", function() {
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
mockRuntime.settings.removeUserKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
@ -367,18 +314,18 @@ describe("api/editor/sshkeys", function() {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
mockRuntime.settings.removeUserKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.expect(500)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
res.body.message.should.be.equal('Messages.....');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
|
@ -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);
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
|
@ -17,3 +17,829 @@
|
||||
describe("runtime-api/nodes", function() {
|
||||
it.skip('more tests needed', function(){})
|
||||
});
|
||||
|
||||
/*
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var sinon = require('sinon');
|
||||
var when = require('when');
|
||||
|
||||
var nodes = require("../../../../red/api/admin/nodes");
|
||||
var apiUtil = require("../../../../red/api/util");
|
||||
|
||||
describe("api/admin/nodes", function() {
|
||||
|
||||
var app;
|
||||
function initNodes(runtime) {
|
||||
runtime.log = {
|
||||
audit:function(e){},//console.log(e)},
|
||||
_:function(){},
|
||||
info: function(){},
|
||||
warn: function(){}
|
||||
}
|
||||
runtime.events = {
|
||||
emit: function(){}
|
||||
}
|
||||
nodes.init(runtime);
|
||||
|
||||
}
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/nodes",nodes.getAll);
|
||||
app.post("/nodes",nodes.post);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
|
||||
app.get("/getIcons",nodes.getIcons);
|
||||
app.delete("/nodes/:id",nodes.delete);
|
||||
sinon.stub(apiUtil,"determineLangFromHeaders", function() {
|
||||
return "en-US";
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
apiUtil.determineLangFromHeaders.restore();
|
||||
})
|
||||
|
||||
describe('get nodes', function() {
|
||||
it('returns node list', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeList: function() {
|
||||
return [1,2,3];
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.be.an.Array();
|
||||
res.body.should.have.lengthOf(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns node configs', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeConfigs: function() {
|
||||
return "<script></script>";
|
||||
}
|
||||
},
|
||||
i18n: {
|
||||
determineLangFromHeaders: function(){}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes')
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
.expect("<script></script>")
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns node module info', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getModuleInfo: function(id) {
|
||||
return {"node-red":{name:"node-red"}}[id];
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","node-red");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown module', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getModuleInfo: function(id) {
|
||||
return {"node-red":{name:"node-red"}}[id];
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-blue')
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns individual node info', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeInfo: function(id) {
|
||||
return {"node-red/123":{id:"node-red/123"}}[id];
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red/123')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("id","node-red/123");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns individual node configs', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeConfig: function(id) {
|
||||
return {"node-red/123":"<script></script>"}[id];
|
||||
}
|
||||
},
|
||||
i18n: {
|
||||
determineLangFromHeaders: function(){}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red/123')
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
.expect("<script></script>")
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown node', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeInfo: function(id) {
|
||||
return {"node-red/123":{id:"node-red/123"}}[id];
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red/456')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('install', function() {
|
||||
|
||||
it('returns 400 if settings are unavailable', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return false}}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 400 if request is invalid', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('by module', function() {
|
||||
it('installs the module and returns module info', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return null; },
|
||||
installModule: function() {
|
||||
return when.resolve({
|
||||
name:"foo",
|
||||
nodes:[{id:"123"}]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo'})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","foo");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("id","123");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails the install if already installed', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return {nodes:{id:"123"}}; },
|
||||
installModule: function() {
|
||||
return when.resolve({id:"123"});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo'})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails the install if module error', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return null },
|
||||
installModule: function() {
|
||||
return when.reject(new Error("test error"));
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo'})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("message","Error: test error");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('fails the install if module not found', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return null },
|
||||
installModule: function() {
|
||||
var err = new Error("test error");
|
||||
err.code = 404;
|
||||
return when.reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo'})
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('delete', function() {
|
||||
it('returns 400 if settings are unavailable', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return false}}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.del('/nodes/123')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('by module', function() {
|
||||
it('uninstalls the module', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
|
||||
getNodeInfo: function() { return null },
|
||||
uninstallModule: function() { return when.resolve({id:"123"});}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.del('/nodes/foo')
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails the uninstall if the module is not installed', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return null },
|
||||
getNodeInfo: function() { return null }
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.del('/nodes/foo')
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails the uninstall if the module is not installed', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
|
||||
getNodeInfo: function() { return null },
|
||||
uninstallModule: function() { return when.reject(new Error("test error"));}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.del('/nodes/foo')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("message","Error: test error");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('enable/disable', function() {
|
||||
it('returns 400 if settings are unavailable', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return false}}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/123')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 400 for invalid node payload', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("message","Invalid request");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 400 for invalid module payload', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/foo')
|
||||
.send({})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("message","Invalid request");
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown node', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getNodeInfo: function() { return null }
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:false})
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown module', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function(id) { return null }
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-blue')
|
||||
.send({enabled:false})
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enables disabled node', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getNodeInfo: function() { return {id:"123",enabled: false} },
|
||||
enableNode: function() { return when.resolve({id:"123",enabled: true,types:['a']}); }
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:true})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("id","123");
|
||||
res.body.should.have.property("enabled",true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disables enabled node', function(done) {
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getNodeInfo: function() { return {id:"123",enabled: true} },
|
||||
disableNode: function() { return when.resolve({id:"123",enabled: false,types:['a']}); }
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:false})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("id","123");
|
||||
res.body.should.have.property("enabled",false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('no-ops if already in the right state', function() {
|
||||
function run(state,done) {
|
||||
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
|
||||
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
|
||||
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getNodeInfo: function() { return {id:"123",enabled: state} },
|
||||
enableNode: enableNode,
|
||||
disableNode: disableNode
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:state})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
var enableNodeCalled = enableNode.called;
|
||||
var disableNodeCalled = disableNode.called;
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
enableNodeCalled.should.be.false();
|
||||
disableNodeCalled.should.be.false();
|
||||
res.body.should.have.property("id","123");
|
||||
res.body.should.have.property("enabled",state);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
it('already enabled', function(done) {
|
||||
run(true,done);
|
||||
});
|
||||
it('already disabled', function(done) {
|
||||
run(false,done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('does not no-op if err on node', function() {
|
||||
function run(state,done) {
|
||||
var enableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: true,types:['a']}) });
|
||||
var disableNode = sinon.spy(function() { return when.resolve({id:"123",enabled: false,types:['a']}) });
|
||||
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getNodeInfo: function() { return {id:"123",enabled: state, err:"foo"} },
|
||||
enableNode: enableNode,
|
||||
disableNode: disableNode
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:state})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
var enableNodeCalled = enableNode.called;
|
||||
var disableNodeCalled = disableNode.called;
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
enableNodeCalled.should.be.equal(state);
|
||||
disableNodeCalled.should.be.equal(!state);
|
||||
res.body.should.have.property("id","123");
|
||||
res.body.should.have.property("enabled",state);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
it('already enabled', function(done) {
|
||||
run(true,done);
|
||||
});
|
||||
it('already disabled', function(done) {
|
||||
run(false,done);
|
||||
});
|
||||
});
|
||||
|
||||
it('enables disabled module', function(done) {
|
||||
var n1 = {id:"123",enabled:false,types:['a']};
|
||||
var n2 = {id:"456",enabled:false,types:['b']};
|
||||
var enableNode = sinon.stub();
|
||||
enableNode.onFirstCall().returns((function() {
|
||||
n1.enabled = true;
|
||||
return when.resolve(n1);
|
||||
})());
|
||||
enableNode.onSecondCall().returns((function() {
|
||||
n2.enabled = true;
|
||||
return when.resolve(n2);
|
||||
})());
|
||||
enableNode.returns(null);
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
|
||||
enableNode: enableNode
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({enabled:true})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","node-red");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("enabled",true);
|
||||
res.body.nodes[1].should.have.property("enabled",true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disables enabled module', function(done) {
|
||||
var n1 = {id:"123",enabled:true,types:['a']};
|
||||
var n2 = {id:"456",enabled:true,types:['b']};
|
||||
var disableNode = sinon.stub();
|
||||
disableNode.onFirstCall().returns((function() {
|
||||
n1.enabled = false;
|
||||
return when.resolve(n1);
|
||||
})());
|
||||
disableNode.onSecondCall().returns((function() {
|
||||
n2.enabled = false;
|
||||
return when.resolve(n2);
|
||||
})());
|
||||
disableNode.returns(null);
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} },
|
||||
disableNode: disableNode
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({enabled:false})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","node-red");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("enabled",false);
|
||||
res.body.nodes[1].should.have.property("enabled",false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('no-ops if a node in module already in the right state', function() {
|
||||
function run(state,done) {
|
||||
var node = {id:"123",enabled:state,types:['a']};
|
||||
var enableNode = sinon.spy(function(id) {
|
||||
node.enabled = true;
|
||||
return when.resolve(node);
|
||||
});
|
||||
var disableNode = sinon.spy(function(id) {
|
||||
node.enabled = false;
|
||||
return when.resolve(node);
|
||||
});
|
||||
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
|
||||
enableNode: enableNode,
|
||||
disableNode: disableNode
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({enabled:state})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
var enableNodeCalled = enableNode.called;
|
||||
var disableNodeCalled = disableNode.called;
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
enableNodeCalled.should.be.false();
|
||||
disableNodeCalled.should.be.false();
|
||||
res.body.should.have.property("name","node-red");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("enabled",state);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
it('already enabled', function(done) {
|
||||
run(true,done);
|
||||
});
|
||||
it('already disabled', function(done) {
|
||||
run(false,done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('does not no-op if err on a node in module', function() {
|
||||
function run(state,done) {
|
||||
var node = {id:"123",enabled:state,types:['a'],err:"foo"};
|
||||
var enableNode = sinon.spy(function(id) {
|
||||
node.enabled = true;
|
||||
return when.resolve(node);
|
||||
});
|
||||
var disableNode = sinon.spy(function(id) {
|
||||
node.enabled = false;
|
||||
return when.resolve(node);
|
||||
});
|
||||
|
||||
initNodes({
|
||||
settings:{available:function(){return true}},
|
||||
nodes:{
|
||||
getModuleInfo: function() { return {name:"node-red", nodes:[node]}; },
|
||||
enableNode: enableNode,
|
||||
disableNode: disableNode
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({enabled:state})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
var enableNodeCalled = enableNode.called;
|
||||
var disableNodeCalled = disableNode.called;
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
enableNodeCalled.should.be.equal(state);
|
||||
disableNodeCalled.should.be.equal(!state);
|
||||
res.body.should.have.property("name","node-red");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("enabled",state);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
it('already enabled', function(done) {
|
||||
run(true,done);
|
||||
});
|
||||
it('already disabled', function(done) {
|
||||
run(false,done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get icons', function() {
|
||||
it('returns icon list', function(done) {
|
||||
initNodes({
|
||||
nodes:{
|
||||
getNodeIcons: function() {
|
||||
return {"module":["1.png","2.png","3.png"]};
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/getIcons')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log(res.body);
|
||||
res.body.should.have.property("module");
|
||||
res.body.module.should.be.an.Array();
|
||||
res.body.module.should.have.lengthOf(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
|
@ -17,3 +17,569 @@
|
||||
describe("runtime-api/settings", function() {
|
||||
it.skip('more tests needed', function(){})
|
||||
});
|
||||
/*
|
||||
it('returns the filtered settings', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function(obj) {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return true; },
|
||||
getCredentialKeyType: function() { return "test-key-type"}
|
||||
},
|
||||
log: { error: console.error },
|
||||
storage: {}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
|
||||
res.body.should.have.property("version","testVersion");
|
||||
res.body.should.have.property("paletteCategories",["red","blue","green"]);
|
||||
res.body.should.have.property("editorTheme",{test:456});
|
||||
res.body.should.have.property("testNodeSetting","helloWorld");
|
||||
res.body.should.not.have.property("foo",123);
|
||||
res.body.should.have.property("flowEncryptionType","test-key-type");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('includes project settings if projects available', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function(obj) {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return true; },
|
||||
getCredentialKeyType: function() { return "test-key-type"}
|
||||
},
|
||||
log: { error: console.error },
|
||||
storage: {
|
||||
projects: {
|
||||
getActiveProject: () => 'test-active-project',
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("project","test-active-project");
|
||||
res.body.should.not.have.property("files");
|
||||
res.body.should.have.property("git");
|
||||
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('includes existing files details if projects enabled but no active project and files exist', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function(obj) {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return true; },
|
||||
getCredentialKeyType: function() { return "test-key-type"}
|
||||
},
|
||||
log: { error: console.error },
|
||||
storage: {
|
||||
projects: {
|
||||
flowFileExists: () => true,
|
||||
getActiveProject: () => false,
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.not.have.property("project");
|
||||
res.body.should.have.property("files");
|
||||
res.body.files.should.have.property("flow",'test-flow-file');
|
||||
res.body.files.should.have.property("credentials",'test-creds-file');
|
||||
res.body.should.have.property("git");
|
||||
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('does not include file details if projects enabled but no active project and files do not exist', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function(obj) {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return true; },
|
||||
getCredentialKeyType: function() { return "test-key-type"}
|
||||
},
|
||||
log: { error: console.error },
|
||||
storage: {
|
||||
projects: {
|
||||
flowFileExists: () => false,
|
||||
getActiveProject: () => false,
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.not.have.property("project");
|
||||
res.body.should.not.have.property("files");
|
||||
res.body.should.have.property("git");
|
||||
res.body.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('overrides palette editable if runtime says it is disabled', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function() {}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return false; },
|
||||
getCredentialKeyType: function() { return "test-key-type"}
|
||||
|
||||
},
|
||||
log: { error: console.error },
|
||||
storage: {}
|
||||
|
||||
});
|
||||
request(app)
|
||||
.get("/settings")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
|
||||
res.body.should.have.property("version","testVersion");
|
||||
res.body.should.have.property("paletteCategories",["red","blue","green"]);
|
||||
res.body.should.have.property("editorTheme");
|
||||
res.body.editorTheme.should.have.property("test",456);
|
||||
|
||||
res.body.editorTheme.should.have.property("palette",{editable:false});
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
var editorApi = require("../../../../red/api/editor");
|
||||
var comms = require("../../../../red/api/editor/comms");
|
||||
var info = require("../../../../red/api/editor/settings");
|
||||
var auth = require("../../../../red/api/auth");
|
||||
var sshkeys = require("../../../../red/api/editor/sshkeys");
|
||||
var when = require("when");
|
||||
var bodyParser = require("body-parser");
|
||||
var fs = require("fs-extra");
|
||||
var fspath = require("path");
|
||||
|
||||
|
||||
describe("api/editor/sshkeys", function() {
|
||||
var app;
|
||||
var mockList = [
|
||||
'library','theme','locales','credentials','comms'
|
||||
]
|
||||
var isStarted = true;
|
||||
var errors = [];
|
||||
var session_data = {};
|
||||
|
||||
var mockRuntime = {
|
||||
settings:{
|
||||
httpNodeRoot: true,
|
||||
httpAdminRoot: true,
|
||||
disableEditor: false,
|
||||
exportNodeSettings:function(){},
|
||||
storage: {
|
||||
getSessions: function(){
|
||||
return when.resolve(session_data);
|
||||
},
|
||||
setSessions: function(_session) {
|
||||
session_data = _session;
|
||||
return when.resolve();
|
||||
}
|
||||
}
|
||||
},
|
||||
log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}},
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
init: function(){},
|
||||
listSSHKeys: function(){},
|
||||
getSSHKey: function(){},
|
||||
generateSSHKey: function(){},
|
||||
deleteSSHKey: function(){}
|
||||
}
|
||||
}
|
||||
},
|
||||
events:{on:function(){},removeListener:function(){}},
|
||||
isStarted: function() { return isStarted; },
|
||||
nodes: {paletteEditorEnabled: function() { return false }}
|
||||
};
|
||||
|
||||
before(function() {
|
||||
auth.init(mockRuntime);
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.use(editorApi.init({},mockRuntime));
|
||||
});
|
||||
after(function() {
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey");
|
||||
})
|
||||
afterEach(function() {
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.restore();
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.restore();
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.restore();
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.restore();
|
||||
})
|
||||
|
||||
it('GET /settings/user/keys --- return empty list', function(done) {
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([]));
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('keys');
|
||||
res.body.keys.should.be.empty();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys --- return normal list', function(done) {
|
||||
var fileList = [
|
||||
'test_key01',
|
||||
'test_key02'
|
||||
];
|
||||
var retList = fileList.map(function(elem) {
|
||||
return {
|
||||
name: elem
|
||||
};
|
||||
});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList));
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('keys');
|
||||
for (var item of retList) {
|
||||
res.body.keys.should.containEql(item);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys --- return Error', function(done) {
|
||||
var errInstance = new Error("Messages here.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null));
|
||||
request(app)
|
||||
.get("/settings/user/keys/NOT_REAL")
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return content', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n";
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent));
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true();
|
||||
res.body.should.be.deepEqual({ publickey: fileContent });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return parameter error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal("You need to have body or body.name");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return Unexpected error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true));
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.be.deepEqual({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -110,7 +110,7 @@ describe('Node', function() {
|
||||
p.then(function() {
|
||||
callbacksClosed.should.eql(3);
|
||||
testdone();
|
||||
}).otherwise(function(e) {
|
||||
}).catch(function(e) {
|
||||
testdone(e);
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
})
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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); });
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user