WIP: separate runtime and api components

This commit is contained in:
Nick O'Leary
2015-11-11 22:11:02 +00:00
parent 923a46d304
commit f43738446e
49 changed files with 695 additions and 612 deletions

View File

@@ -24,12 +24,14 @@ var auth = require("../../../../red/api/auth");
var Users = require("../../../../red/api/auth/users");
var Tokens = require("../../../../red/api/auth/tokens");
var settings = require("../../../../red/settings");
describe("api auth middleware",function() {
describe("ensureClientSecret", function() {
before(function() {
auth.init({settings:{},log:{audit:function(){}}})
});
it("leaves client_secret alone if not present",function(done) {
var req = {
body: {
@@ -83,7 +85,7 @@ describe("api auth middleware",function() {
Users.init.restore();
});
it("returns login details - credentials", function(done) {
auth.init({adminAuth:{}},null);
auth.init({settings:{adminAuth:{}},log:{audit:function(){}}})
auth.login(null,{json: function(resp) {
resp.should.have.a.property("type","credentials");
resp.should.have.a.property("prompts");
@@ -92,7 +94,7 @@ describe("api auth middleware",function() {
}});
});
it("returns login details - none", function(done) {
auth.init({},null);
auth.init({settings:{},log:{audit:function(){}}})
auth.login(null,{json: function(resp) {
resp.should.eql({});
done();

View File

@@ -24,8 +24,10 @@ var Tokens = require("../../../../red/api/auth/tokens");
var Clients = require("../../../../red/api/auth/clients");
describe("Auth strategies", function() {
before(function() {
strategies.init({log:{audit:function(){}}})
});
describe("Password Token Exchange", function() {
var userAuthentication;
afterEach(function() {
if (userAuthentication) {
@@ -49,7 +51,7 @@ describe("Auth strategies", function() {
}
});
});
it('Handles scope overreach',function(done) {
userAuthentication = sinon.stub(Users,"authenticate",function(username,password) {
return when.resolve({username:"user",permissions:"read"});

View File

@@ -20,8 +20,6 @@ var express = require('express');
var sinon = require('sinon');
var when = require('when');
var nodeApi = require("../../../red/nodes");
var credentials = require("../../../red/api/credentials");
describe('credentials api', function() {
@@ -30,26 +28,26 @@ describe('credentials api', function() {
before(function() {
app = express();
app.get('/credentials/:type/:id',credentials.get);
sinon.stub(nodeApi,"getCredentials",function(id) {
if (id === "n1") {
return {user1:"abc",password1:"123"};
} else {
return null;
}
});
sinon.stub(nodeApi,"getCredentialDefinition",function(type) {
if (type === "known-type") {
return {user1:{type:"text"},password1:{type:"password"}};
} else {
return null;
credentials.init({
log:{audit:function(){}},
api:{
getCredentials: function(id) {
if (id === "n1") {
return {user1:"abc",password1:"123"};
} else {
return null;
}
},
getCredentialDefinition:function(type) {
if (type === "known-type") {
return {user1:{type:"text"},password1:{type:"password"}};
} else {
return null;
}
}
}
});
});
after(function() {
nodeApi.getCredentials.restore();
nodeApi.getCredentialDefinition.restore();
})
it('returns empty credentials if unknown type',function(done) {
request(app)
.get("/credentials/unknown-type/n1")

View File

@@ -21,8 +21,6 @@ var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');
var redNodes = require("../../../red/nodes");
var flows = require("../../../red/api/flows");
describe("flows api", function() {
@@ -37,15 +35,17 @@ describe("flows api", function() {
});
it('returns flow', function(done) {
var getFlows = sinon.stub(redNodes,'getFlows', function() {
return [1,2,3];
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
api:{
getFlows: function() { return [1,2,3]; }
}
});
request(app)
.get('/flows')
.set('Accept', 'application/json')
.expect(200)
.end(function(err,res) {
getFlows.restore();
if (err) {
throw err;
}
@@ -55,15 +55,17 @@ describe("flows api", function() {
});
it('sets flows', function(done) {
var setFlows = sinon.stub(redNodes,'setFlows', function() {
return when.resolve();
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
api:{
setFlows: function() { return when.resolve(); }
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.expect(204)
.end(function(err,res) {
setFlows.restore();
if (err) {
throw err;
}
@@ -71,15 +73,17 @@ describe("flows api", function() {
});
});
it('returns error when set fails', function(done) {
var setFlows = sinon.stub(redNodes,'setFlows', function() {
return when.reject(new Error("expected error"));
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
api:{
setFlows: function() { return when.reject(new Error("expected error")); }
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.expect(500)
.end(function(err,res) {
setFlows.restore();
if (err) {
throw err;
}

View File

@@ -15,12 +15,12 @@
**/
var should = require("should");
var sinon = require("sinon");
var request = require("supertest");
var express = require("express");
var when = require("when");
var fs = require("fs");
var path = require("path");
var settings = require("../../../red/settings");
var api = require("../../../red/api");
describe("api index", function() {
@@ -28,12 +28,13 @@ describe("api index", function() {
describe("disables editor", function() {
before(function() {
settings.init({disableEditor:true});
app = express();
api.init(app);
});
after(function() {
settings.reset();
api.init(app,{
settings:{disableEditor:true},
api:{
}
});
});
it('does not serve the editor', function(done) {
@@ -54,14 +55,25 @@ describe("api index", function() {
});
describe("can serve auth", function() {
var mockList = [
'ui','nodes','flows','library','info','theme','locales','credentials'
]
before(function() {
//settings.init({disableEditor:true});
settings.init({adminAuth:{type: "credentials",users:[],default:{permissions:"read"}}});
app = express();
api.init(app,{getSessions:function(){return when.resolve({})}});
mockList.forEach(function(m) {
sinon.stub(require("../../../red/api/"+m),"init",function(){});
});
});
after(function() {
settings.reset();
mockList.forEach(function(m) {
require("../../../red/api/"+m).init.restore();
})
});
before(function() {
app = express();
api.init(app,{
settings:{adminAuth:{type: "credentials",users:[],default:{permissions:"read"}}},
storage:{getSessions:function(){return when.resolve({})}}
});
});
it('it now serves auth', function(done) {
@@ -77,15 +89,29 @@ describe("api index", function() {
});
describe("enables editor", function() {
var mockList = [
'nodes','flows','library','info','theme','locales','credentials'
]
before(function() {
settings.init({disableEditor:false});
app = express();
api.init(app);
mockList.forEach(function(m) {
sinon.stub(require("../../../red/api/"+m),"init",function(){});
});
});
after(function() {
settings.reset();
mockList.forEach(function(m) {
require("../../../red/api/"+m).init.restore();
})
});
before(function() {
app = express();
api.init(app,{
log:{audit:function(){}},
settings:{disableEditor:false},
events:{on:function(){},removeListener:function(){}}
});
});
it('serves the editor', function(done) {
request(app)
.get("/")

View File

@@ -21,30 +21,26 @@ var sinon = require('sinon');
var when = require('when');
var app = express();
var settings = require("../../../red/settings");
var info = require("../../../red/api/info");
var theme = require("../../../red/api/theme");
describe("info api", function() {
describe("settings handler", function() {
before(function() {
var userSettings = {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"]
}
settings.init(userSettings);
sinon.stub(theme,"settings",function() { return { test: 456 };});
info.init({
settings: {
foo: 123,
httpNodeRoot: "testHttpNodeRoot",
version: "testVersion",
paletteCategories :["red","blue","green"]
}
})
app = express();
app.get("/settings",info.settings);
});
after(function() {
settings.reset();
theme.settings.restore();
});

View File

@@ -21,19 +21,18 @@ var bodyParser = require('body-parser');
var when = require('when');
var app = express();
var RED = require("../../../red/red.js");
var storage = require("../../../red/storage");
var app;
var library = require("../../../red/api/library");
var auth = require("../../../red/api/auth");
describe("library api", function() {
function initStorage(_flows,_libraryEntries) {
function initLibrary(_flows,_libraryEntries) {
var flows = _flows;
var libraryEntries = _libraryEntries;
storage.init({
storageModule: {
library.init(app,{
log:{audit:function(){},_:function(){},warn:function(){}},
storage: {
init: function() {
return when.resolve();
},
@@ -43,15 +42,29 @@ describe("library api", function() {
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 {
@@ -59,6 +72,11 @@ describe("library api", function() {
}
},
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();
}
@@ -67,8 +85,6 @@ describe("library api", function() {
}
describe("flows", function() {
var app;
before(function() {
app = express();
app.use(bodyParser.json());
@@ -77,7 +93,7 @@ describe("library api", function() {
app.get(new RegExp("/library/flows\/(.*)"),library.get);
});
it('returns empty result', function(done) {
initStorage({},{flows:{}});
initLibrary({},{flows:{}});
request(app)
.get('/library/flows')
.expect(200)
@@ -92,7 +108,7 @@ describe("library api", function() {
});
it('returns 404 for non-existent entry', function(done) {
initStorage({},{flows:{}});
initLibrary({},{flows:{}});
request(app)
.get('/library/flows/foo')
.expect(404)
@@ -101,7 +117,7 @@ describe("library api", function() {
it('can store and retrieve item', function(done) {
initStorage({},{flows:{}});
initLibrary({},{flows:{}});
var flow = '[]';
request(app)
.post('/library/flows/foo')
@@ -125,7 +141,7 @@ describe("library api", function() {
});
it('lists a stored item', function(done) {
initStorage({f:["bar"]});
initLibrary({f:["bar"]});
request(app)
.get('/library/flows')
.expect(200)
@@ -140,7 +156,7 @@ describe("library api", function() {
});
it('returns 403 for malicious get attempt', function(done) {
initStorage({});
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.
@@ -150,7 +166,7 @@ describe("library api", function() {
.end(done);
});
it('returns 403 for malicious post attempt', function(done) {
initStorage({});
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.
@@ -162,18 +178,17 @@ describe("library api", function() {
});
describe("type", function() {
var app;
before(function() {
app = express();
app.use(bodyParser.json());
library.init(app);
auth.init({});
RED.library.register("test");
initLibrary({},{});
auth.init({settings:{}});
library.register("test");
});
it('returns empty result', function(done) {
initStorage({},{'test':{"":[]}});
initLibrary({},{'test':{"":[]}});
request(app)
.get('/library/test')
.expect(200)
@@ -187,7 +202,7 @@ describe("library api", function() {
});
it('returns 404 for non-existent entry', function(done) {
initStorage({},{});
initLibrary({},{});
request(app)
.get('/library/test/foo')
.expect(404)
@@ -195,7 +210,7 @@ describe("library api", function() {
});
it('can store and retrieve item', function(done) {
initStorage({},{'test':{}});
initLibrary({},{'test':{}});
var flow = {text:"test content"};
request(app)
.post('/library/test/foo')
@@ -219,7 +234,7 @@ describe("library api", function() {
});
it('lists a stored item', function(done) {
initStorage({},{'test':{'a':['abc','def']}});
initLibrary({},{'test':{'a':['abc','def']}});
request(app)
.get('/library/test/a')
.expect(200)

View File

@@ -0,0 +1,19 @@
/**
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
describe("locales api", function() {
it.skip("works",function(){});
});

View File

@@ -21,7 +21,6 @@ var bodyParser = require('body-parser');
var sinon = require('sinon');
var when = require('when');
var redNodes = require("../../../red/nodes");
var settings = require("../../../red/settings");
var nodes = require("../../../red/api/nodes");
@@ -29,6 +28,16 @@ var nodes = require("../../../red/api/nodes");
describe("nodes api", function() {
var app;
function initNodes(runtime) {
runtime.log = {
audit:function(e){},//console.log(e)},
_:function(){},
info: function(){},
warn: function(){}
}
runtime.comms = { publish:function(){}}
nodes.init(runtime);
}
before(function() {
app = express();
@@ -44,15 +53,18 @@ describe("nodes api", function() {
describe('get nodes', function() {
it('returns node list', function(done) {
var getNodeList = sinon.stub(redNodes,'getNodeList', function() {
return [1,2,3];
initNodes({
api:{
getNodeList: function() {
return [1,2,3];
}
}
});
request(app)
.get('/nodes')
.set('Accept', 'application/json')
.expect(200)
.end(function(err,res) {
getNodeList.restore();
if (err) {
throw err;
}
@@ -62,8 +74,15 @@ describe("nodes api", function() {
});
it('returns node configs', function(done) {
var getNodeConfigs = sinon.stub(redNodes,'getNodeConfigs', function() {
return "<script></script>";
initNodes({
api:{
getNodeConfigs: function() {
return "<script></script>";
}
},
i18n: {
determineLangFromHeaders: function(){}
}
});
request(app)
.get('/nodes')
@@ -71,7 +90,6 @@ describe("nodes api", function() {
.expect(200)
.expect("<script></script>")
.end(function(err,res) {
getNodeConfigs.restore();
if (err) {
throw err;
}
@@ -80,14 +98,17 @@ describe("nodes api", function() {
});
it('returns node module info', function(done) {
var getNodeInfo = sinon.stub(redNodes,'getModuleInfo', function(id) {
return {"node-red":{name:"node-red"}}[id];
initNodes({
api:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
}
}
});
request(app)
.get('/nodes/node-red')
.expect(200)
.end(function(err,res) {
getNodeInfo.restore();
if (err) {
throw err;
}
@@ -97,14 +118,17 @@ describe("nodes api", function() {
});
it('returns 404 for unknown module', function(done) {
var getNodeInfo = sinon.stub(redNodes,'getModuleInfo', function(id) {
return {"node-red":{name:"node-red"}}[id];
initNodes({
api:{
getModuleInfo: function(id) {
return {"node-red":{name:"node-red"}}[id];
}
}
});
request(app)
.get('/nodes/node-blue')
.expect(404)
.end(function(err,res) {
getNodeInfo.restore();
if (err) {
throw err;
}
@@ -113,15 +137,18 @@ describe("nodes api", function() {
});
it('returns individual node info', function(done) {
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo', function(id) {
return {"node-red/123":{id:"node-red/123"}}[id];
initNodes({
api:{
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) {
getNodeInfo.restore();
if (err) {
throw err;
}
@@ -131,8 +158,15 @@ describe("nodes api", function() {
});
it('returns individual node configs', function(done) {
var getNodeConfig = sinon.stub(redNodes,'getNodeConfig', function(id) {
return {"node-red/123":"<script></script>"}[id];
initNodes({
api:{
getNodeConfig: function(id) {
return {"node-red/123":"<script></script>"}[id];
}
},
i18n: {
determineLangFromHeaders: function(){}
}
});
request(app)
.get('/nodes/node-red/123')
@@ -140,7 +174,6 @@ describe("nodes api", function() {
.expect(200)
.expect("<script></script>")
.end(function(err,res) {
getNodeConfig.restore();
if (err) {
throw err;
}
@@ -149,15 +182,18 @@ describe("nodes api", function() {
});
it('returns 404 for unknown node', function(done) {
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo', function(id) {
return {"node-red/123":{id:"node-red/123"}}[id];
initNodes({
api:{
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) {
getNodeInfo.restore();
if (err) {
throw err;
}
@@ -169,14 +205,13 @@ describe("nodes api", function() {
describe('install', function() {
it('returns 400 if settings are unavailable', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return false;
initNodes({
settings:{available:function(){return false}}
});
request(app)
.post('/nodes')
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
if (err) {
throw err;
}
@@ -185,15 +220,14 @@ describe("nodes api", function() {
});
it('returns 400 if request is invalid', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}}
});
request(app)
.post('/nodes')
.send({})
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
if (err) {
throw err;
}
@@ -203,26 +237,23 @@ describe("nodes api", function() {
describe('by module', function() {
it('installs the module and returns module info', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return null; },
installModule: function() {
return when.resolve({
name:"foo",
nodes:[{id:"123"}]
});
}
}
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo');
getModuleInfo.onCall(0).returns(null);
var installModule = sinon.stub(redNodes,'installModule', function() {
return when.resolve({
name:"foo",
nodes:[{id:"123"}]
});
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(200)
.end(function(err,res) {
settingsAvailable.restore();
getModuleInfo.restore();
installModule.restore();
if (err) {
throw err;
}
@@ -234,24 +265,20 @@ describe("nodes api", function() {
});
it('fails the install if already installed', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return {nodes:{id:"123"}}; },
installModule: function() {
return when.resolve({id:"123"});
}
}
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return {nodes:{id:"123"}};
});
var installModule = sinon.stub(redNodes,'installModule', function() {
return when.resolve({id:"123"});
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
getModuleInfo.restore();
installModule.restore();
if (err) {
throw err;
}
@@ -260,24 +287,20 @@ describe("nodes api", function() {
});
it('fails the install if module error', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return null },
installModule: function() {
return when.reject(new Error("test error"));
}
}
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return null;
});
var installModule = sinon.stub(redNodes,'installModule', function() {
return when.reject(new Error("test error"));
});
request(app)
.post('/nodes')
.send({module: 'foo'})
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
getModuleInfo.restore();
installModule.restore();
if (err) {
throw err;
}
@@ -286,26 +309,22 @@ describe("nodes api", function() {
});
});
it('fails the install if module not found', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return null },
installModule: function() {
var err = new Error("test error");
err.code = 404;
return when.reject(err);
}
}
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return null;
});
var installModule = sinon.stub(redNodes,'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) {
settingsAvailable.restore();
getModuleInfo.restore();
installModule.restore();
if (err) {
throw err;
}
@@ -319,6 +338,10 @@ describe("nodes api", function() {
var settingsAvailable = sinon.stub(settings,'available', function() {
return false;
});
initNodes({
settings:{available:function(){return false}}
});
request(app)
.del('/nodes/123')
.expect(400)
@@ -333,27 +356,18 @@ describe("nodes api", function() {
describe('by module', function() {
it('uninstalls the module', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.resolve({id:"123"});}
}
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return null;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return {nodes:[{id:"123"}]};
});
var uninstallModule = sinon.stub(redNodes,'uninstallModule', function() {
return when.resolve({id:"123"});
});
request(app)
.del('/nodes/foo')
.expect(204)
.end(function(err,res) {
settingsAvailable.restore();
getNodeInfo.restore();
getModuleInfo.restore();
uninstallModule.restore();
if (err) {
throw err;
}
@@ -362,23 +376,17 @@ describe("nodes api", function() {
});
it('fails the uninstall if the module is not installed', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return null },
getNodeInfo: function() { return null }
}
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return null;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return null;
});
request(app)
.del('/nodes/foo')
.expect(404)
.end(function(err,res) {
settingsAvailable.restore();
getNodeInfo.restore();
getModuleInfo.restore();
if (err) {
throw err;
}
@@ -387,27 +395,18 @@ describe("nodes api", function() {
});
it('fails the uninstall if the module is not installed', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return {nodes:[{id:"123"}]} },
getNodeInfo: function() { return null },
uninstallModule: function() { return when.reject(new Error("test error"));}
}
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return null;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return {nodes:[{id:"123"}]};
});
var uninstallModule = sinon.stub(redNodes,'uninstallModule', function() {
return when.reject(new Error("test error"));
});
request(app)
.del('/nodes/foo')
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
getNodeInfo.restore();
getModuleInfo.restore();
uninstallModule.restore();
if (err) {
throw err;
}
@@ -421,14 +420,13 @@ describe("nodes api", function() {
describe('enable/disable', function() {
it('returns 400 if settings are unavailable', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return false;
initNodes({
settings:{available:function(){return false}}
});
request(app)
.put('/nodes/123')
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
if (err) {
throw err;
}
@@ -437,16 +435,14 @@ describe("nodes api", function() {
});
it('returns 400 for invalid node payload', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/node-red/foo')
.send({})
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
if (err) {
throw err;
}
@@ -456,16 +452,14 @@ describe("nodes api", function() {
});
it('returns 400 for invalid module payload', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}}
});
request(app)
.put('/nodes/foo')
.send({})
.expect(400)
.end(function(err,res) {
settingsAvailable.restore();
if (err) {
throw err;
}
@@ -476,11 +470,11 @@ describe("nodes api", function() {
});
it('returns 404 for unknown node', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return null;
initNodes({
settings:{available:function(){return true}},
api:{
getNodeInfo: function() { return null }
}
});
request(app)
@@ -488,8 +482,6 @@ describe("nodes api", function() {
.send({enabled:false})
.expect(404)
.end(function(err,res) {
settingsAvailable.restore();
getNodeInfo.restore();
if (err) {
throw err;
}
@@ -498,11 +490,11 @@ describe("nodes api", function() {
});
it('returns 404 for unknown module', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return null;
initNodes({
settings:{available:function(){return true}},
api:{
getModuleInfo: function(id) { return null }
}
});
request(app)
@@ -510,8 +502,6 @@ describe("nodes api", function() {
.send({enabled:false})
.expect(404)
.end(function(err,res) {
settingsAvailable.restore();
getModuleInfo.restore();
if (err) {
throw err;
}
@@ -520,24 +510,18 @@ describe("nodes api", function() {
});
it('enables disabled node', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getNodeInfo: function() { return {id:"123",enabled: false} },
enableNode: function() { return when.resolve({id:"123",enabled: true,types:['a']}); }
}
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return {id:"123",enabled: false};
});
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
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) {
settingsAvailable.restore();
getNodeInfo.restore();
enableNode.restore();
if (err) {
throw err;
}
@@ -549,24 +533,18 @@ describe("nodes api", function() {
});
it('disables enabled node', function(done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
initNodes({
settings:{available:function(){return true}},
api:{
getNodeInfo: function() { return {id:"123",enabled: true} },
disableNode: function() { return when.resolve({id:"123",enabled: false,types:['a']}); }
}
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return {id:"123",enabled: true};
});
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
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) {
settingsAvailable.restore();
getNodeInfo.restore();
disableNode.restore();
if (err) {
throw err;
}
@@ -579,31 +557,24 @@ describe("nodes api", function() {
describe('no-ops if already in the right state', function() {
function run(state,done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return {id:"123",enabled: state};
});
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
return when.resolve({id:"123",enabled: true,types:['a']});
});
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']}) });
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
return when.resolve({id:"123",enabled: false,types:['a']});
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getNodeInfo.restore();
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
enableNode.restore();
disableNode.restore();
if (err) {
throw err;
}
@@ -625,31 +596,24 @@ describe("nodes api", function() {
describe('does not no-op if err on node', function() {
function run(state,done) {
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
return {id:"123",enabled: state, err:"foo" };
});
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
return when.resolve({id:"123",enabled: true,types:['a']});
});
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']}) });
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
return when.resolve({id:"123",enabled: false,types:['a']});
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getNodeInfo.restore();
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
enableNode.restore();
disableNode.restore();
if (err) {
throw err;
}
@@ -672,14 +636,7 @@ describe("nodes api", function() {
it('enables disabled module', function(done) {
var n1 = {id:"123",enabled:false,types:['a']};
var n2 = {id:"456",enabled:false,types:['b']};
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(name) {
return {name:"node-red", nodes:[n1, n2]};
});
var enableNode = sinon.stub(redNodes,'enableNode');
var enableNode = sinon.stub();
enableNode.onFirstCall().returns((function() {
n1.enabled = true;
return when.resolve(n1);
@@ -689,15 +646,19 @@ describe("nodes api", function() {
return when.resolve(n2);
})());
enableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getModuleInfo.restore();
enableNode.restore();
if (err) {
throw err;
}
@@ -713,14 +674,7 @@ describe("nodes api", function() {
it('disables enabled module', function(done) {
var n1 = {id:"123",enabled:true,types:['a']};
var n2 = {id:"456",enabled:true,types:['b']};
var settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(name) {
return {name:"node-red", nodes:[n1, n2]};
});
var disableNode = sinon.stub(redNodes,'disableNode');
var disableNode = sinon.stub();
disableNode.onFirstCall().returns((function() {
n1.enabled = false;
return when.resolve(n1);
@@ -730,15 +684,19 @@ describe("nodes api", function() {
return when.resolve(n2);
})());
disableNode.returns(null);
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getModuleInfo.restore();
disableNode.restore();
if (err) {
throw err;
}
@@ -754,32 +712,30 @@ describe("nodes api", function() {
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 settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return {name:"node-red", nodes:[node]};
});
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getModuleInfo.restore();
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
enableNode.restore();
disableNode.restore();
if (err) {
throw err;
}
@@ -803,32 +759,31 @@ describe("nodes api", function() {
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 settingsAvailable = sinon.stub(settings,'available', function() {
return true;
});
var getModuleInfo = sinon.stub(redNodes,'getModuleInfo',function(id) {
return {name:"node-red", nodes:[node]};
});
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
var enableNode = sinon.spy(function(id) {
node.enabled = true;
return when.resolve(node);
});
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
var disableNode = sinon.spy(function(id) {
node.enabled = false;
return when.resolve(node);
});
initNodes({
settings:{available:function(){return true}},
api:{
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) {
settingsAvailable.restore();
getModuleInfo.restore();
var enableNodeCalled = enableNode.called;
var disableNodeCalled = disableNode.called;
enableNode.restore();
disableNode.restore();
if (err) {
throw err;
}

View File

@@ -31,13 +31,13 @@ describe("theme handler", function() {
sinon.stub(fs,"statSync",function() { return true; });
});
afterEach(function() {
theme.init({});
theme.init({settings:{}});
fs.statSync.restore();
});
it("applies the default theme", function() {
var result = theme.init({});
var result = theme.init({settings:{}});
should.not.exist(result);
var context = theme.context();
context.should.have.a.property("page");
context.page.should.have.a.property("title","Node-RED");
@@ -45,12 +45,12 @@ describe("theme handler", 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");
should.not.exist(theme.settings());
});
it("picks up custom theme", function() {
var result = theme.init({
var result = theme.init({settings:{
editorTheme: {
page: {
title: "Test Page Title",
@@ -61,13 +61,13 @@ describe("theme handler", function() {
title: "Test Header Title",
image: "/absolute/path/to/header/image" // or null to remove image
},
deployButton: {
type:"simple",
label:"Save",
icon: "/absolute/path/to/deploy/button/image" // or null to remove image
},
menu: { // Hide unwanted menu items by id. see editor/js/main.js:loadEditor for complete list
"menu-item-import-library": false,
"menu-item-export-library": false,
@@ -77,27 +77,27 @@ describe("theme handler", function() {
url: "http://example.com"
}
},
userMenu: false, // Hide the user-menu even if adminAuth is enabled
login: {
image: "/absolute/path/to/login/page/big/image" // a 256x256 image
}
}
}
});
}});
should.exist(result);
var context = theme.context();
context.should.have.a.property("page");
context.page.should.have.a.property("title","Test Page Title");
context.should.have.a.property("header");
context.header.should.have.a.property("title","Test Header Title");
var settings = theme.settings();
settings.should.have.a.property("deployButton");
settings.should.have.a.property("userMenu");
settings.should.have.a.property("menu");
});
});
});

View File

@@ -20,14 +20,17 @@ var express = require("express");
var fs = require("fs");
var path = require("path");
var events = require("../../../red/events");
var EventEmitter = require('events').EventEmitter;
var events = new EventEmitter();
var ui = require("../../../red/api/ui");
describe("ui api", function() {
var app;
before(function() {
ui.init({events:events});
});
describe("slash handler", function() {
before(function() {
app = express();