mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Remove known unused files
This commit is contained in:
@@ -1,234 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var context = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/context");
|
||||
|
||||
describe("api/admin/context", function () {
|
||||
var app = undefined;
|
||||
|
||||
before(function () {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/context/:scope(global)", context.get);
|
||||
app.get("/context/:scope(global)/*", context.get);
|
||||
app.get("/context/:scope(node|flow)/:id", context.get);
|
||||
app.get("/context/:scope(node|flow)/:id/*", context.get);
|
||||
|
||||
app.delete("/context/:scope(global)/*", context.delete);
|
||||
app.delete("/context/:scope(node|flow)/:id/*", context.delete);
|
||||
});
|
||||
|
||||
describe("get", function () {
|
||||
var gContext = {
|
||||
default: { abc: { msg: '111', format: 'number' } },
|
||||
file: { abc: { msg: '222', format: 'number' } }
|
||||
};
|
||||
var fContext = {
|
||||
default: { bool: { msg: 'true', format: 'boolean' } },
|
||||
file: { string: { msg: 'aaaa', format: 'string[7]' } }
|
||||
};
|
||||
var nContext = { msg: "1", format: "number" };
|
||||
var stub = sinon.stub();
|
||||
|
||||
before(function () {
|
||||
context.init({
|
||||
context: {
|
||||
getValue: stub
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
stub.reset();
|
||||
});
|
||||
|
||||
it('should call context.getValue to get global contexts', function (done) {
|
||||
stub.returns(Promise.resolve(gContext));
|
||||
request(app)
|
||||
.get('/context/global')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'global');
|
||||
stub.args[0][0].should.have.property('id', undefined);
|
||||
stub.args[0][0].should.have.property('key', undefined);
|
||||
stub.args[0][0].should.have.property('store', undefined);
|
||||
var body = res.body;
|
||||
body.should.eql(gContext);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call context.getValue to get flow contexts', function (done) {
|
||||
stub.returns(Promise.resolve(fContext));
|
||||
request(app)
|
||||
.get('/context/flow/1234/')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'flow');
|
||||
stub.args[0][0].should.have.property('id', '1234');
|
||||
stub.args[0][0].should.have.property('key', undefined);
|
||||
stub.args[0][0].should.have.property('store', undefined);
|
||||
var body = res.body;
|
||||
body.should.eql(fContext);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call context.getValue to get a node context', function (done) {
|
||||
stub.returns(Promise.resolve(nContext));
|
||||
request(app)
|
||||
.get('/context/node/5678/foo?store=file')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'node');
|
||||
stub.args[0][0].should.have.property('id', '5678');
|
||||
stub.args[0][0].should.have.property('key', 'foo');
|
||||
stub.args[0][0].should.have.property('store', 'file');
|
||||
var body = res.body;
|
||||
body.should.eql(nContext);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle error which context.getValue causes', function (done) {
|
||||
var stubbedResult = Promise.reject('error');
|
||||
stubbedResult.catch(function() {});
|
||||
stub.returns(stubbedResult);
|
||||
request(app)
|
||||
.get('/context/global')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(400)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('code', 'unexpected_error');
|
||||
res.body.should.has.a.property('message', 'error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete", function () {
|
||||
var stub = sinon.stub();
|
||||
|
||||
before(function () {
|
||||
context.init({
|
||||
context: {
|
||||
delete: stub
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
stub.reset();
|
||||
});
|
||||
|
||||
it('should call context.delete to delete a global context', function (done) {
|
||||
stub.returns(Promise.resolve());
|
||||
request(app)
|
||||
.delete('/context/global/abc?store=default')
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'global');
|
||||
stub.args[0][0].should.have.property('id', undefined);
|
||||
stub.args[0][0].should.have.property('key', 'abc');
|
||||
stub.args[0][0].should.have.property('store', 'default');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call context.delete to delete a flow context', function (done) {
|
||||
stub.returns(Promise.resolve());
|
||||
request(app)
|
||||
.delete('/context/flow/1234/abc?store=file')
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'flow');
|
||||
stub.args[0][0].should.have.property('id', '1234');
|
||||
stub.args[0][0].should.have.property('key', 'abc');
|
||||
stub.args[0][0].should.have.property('store', 'file');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call context.delete to delete a node context', function (done) {
|
||||
stub.returns(Promise.resolve());
|
||||
request(app)
|
||||
.delete('/context/node/5678/foo?store=file')
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
stub.args[0][0].should.have.property('user', undefined);
|
||||
stub.args[0][0].should.have.property('scope', 'node');
|
||||
stub.args[0][0].should.have.property('id', '5678');
|
||||
stub.args[0][0].should.have.property('key', 'foo');
|
||||
stub.args[0][0].should.have.property('store', 'file');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle error which context.delete causes', function (done) {
|
||||
var stubbedResult = Promise.reject('error');
|
||||
stubbedResult.catch(function() {});
|
||||
stub.returns(stubbedResult);
|
||||
request(app)
|
||||
.delete('/context/global/abc?store=default')
|
||||
.expect(400)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('code', 'unexpected_error');
|
||||
res.body.should.has.a.property('message', 'error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,248 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var flow = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flow");
|
||||
|
||||
describe("api/admin/flow", function() {
|
||||
|
||||
var app;
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/flow/:id",flow.get);
|
||||
app.post("/flow",flow.post);
|
||||
app.put("/flow/:id",flow.put);
|
||||
app.delete("/flow/:id",flow.delete);
|
||||
});
|
||||
|
||||
describe("get", function() {
|
||||
before(function() {
|
||||
var opts;
|
||||
flow.init({
|
||||
flows: {
|
||||
getFlow: function(_opts) {
|
||||
opts = _opts;
|
||||
if (opts.id === '123') {
|
||||
return Promise.resolve({id:'123'});
|
||||
} else {
|
||||
var err = new Error("message");
|
||||
err.code = "not_found";
|
||||
err.status = 404;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
it('gets a known flow', function(done) {
|
||||
request(app)
|
||||
.get('/flow/123')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('id','123');
|
||||
done();
|
||||
});
|
||||
})
|
||||
it('404s an unknown flow', function(done) {
|
||||
request(app)
|
||||
.get('/flow/456')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(404)
|
||||
.end(done);
|
||||
})
|
||||
});
|
||||
|
||||
describe("add", function() {
|
||||
var opts;
|
||||
before(function() {
|
||||
flow.init({
|
||||
flows: {
|
||||
addFlow: function(_opts) {
|
||||
opts = _opts;
|
||||
if (opts.flow.id === "123") {
|
||||
return Promise.resolve('123')
|
||||
} else {
|
||||
var err = new Error("random error");
|
||||
err.code = "random_error";
|
||||
err.status = 400;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
it('adds a new flow', function(done) {
|
||||
request(app)
|
||||
.post('/flow')
|
||||
.set('Accept', 'application/json')
|
||||
.send({id:'123'})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('id','123');
|
||||
done();
|
||||
});
|
||||
})
|
||||
it('400 an invalid flow', function(done) {
|
||||
request(app)
|
||||
.post('/flow')
|
||||
.set('Accept', 'application/json')
|
||||
.send({id:'error'})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('code','random_error');
|
||||
res.body.should.has.a.property('message','random error');
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", function() {
|
||||
|
||||
var opts;
|
||||
before(function() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
it('updates an existing flow', function(done) {
|
||||
request(app)
|
||||
.put('/flow/123')
|
||||
.set('Accept', 'application/json')
|
||||
.send({id:'123'})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('id','123');
|
||||
opts.should.have.property('id','123');
|
||||
opts.should.have.property('flow',{id:'123'})
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('400 an invalid flow', function(done) {
|
||||
request(app)
|
||||
.put('/flow/456')
|
||||
.set('Accept', 'application/json')
|
||||
.send({id:'456'})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('code','random_error');
|
||||
res.body.should.has.a.property('message','random error');
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", function() {
|
||||
|
||||
var opts;
|
||||
before(function() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
it('deletes an existing flow', function(done) {
|
||||
request(app)
|
||||
.del('/flow/123')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
opts.should.have.property('id','123');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('400 an invalid flow', function(done) {
|
||||
request(app)
|
||||
.del('/flow/456')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.has.a.property('code','random_error');
|
||||
res.body.should.has.a.property('message','random error');
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
@@ -1,211 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var flows = NR_TEST_UTILS.require("@node-red/editor-api/lib/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({
|
||||
flows:{
|
||||
getFlows: function() { return Promise.resolve({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({
|
||||
flows:{
|
||||
getFlows: function() { return Promise.resolve({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 Promise.resolve();});
|
||||
flows.init({
|
||||
flows:{
|
||||
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[0].should.have.property('deploymentType','full');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('sets flows - non-default - v1', function(done) {
|
||||
var setFlows = sinon.spy(function() { return Promise.resolve();});
|
||||
flows.init({
|
||||
flows:{
|
||||
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[0].should.have.property('deploymentType','nodes');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('set flows - rejects mismatched revision - v2', function(done) {
|
||||
flows.init({
|
||||
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)
|
||||
.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('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 setFlows = sinon.spy(function() { return Promise.resolve();});
|
||||
flows.init({
|
||||
flows:{
|
||||
setFlows: setFlows
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
setFlows.called.should.be.true();
|
||||
setFlows.lastCall.args[0].should.not.have.property('flows');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,458 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var adminApi = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin");
|
||||
var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth");
|
||||
var nodes = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/nodes");
|
||||
var flows = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flows");
|
||||
var flow = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/flow");
|
||||
var context = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/context");
|
||||
|
||||
/**
|
||||
* Ensure all API routes are correctly mounted, with the expected permissions checks
|
||||
*/
|
||||
describe("api/admin/index", function() {
|
||||
describe("Ensure all API routes are correctly mounted, with the expected permissions checks", function() {
|
||||
var app;
|
||||
var mockList = [
|
||||
flows,flow,nodes,context
|
||||
];
|
||||
var permissionChecks = {};
|
||||
var lastRequest;
|
||||
var stubApp = function(req,res,next) {
|
||||
lastRequest = req;
|
||||
res.status(200).end();
|
||||
};
|
||||
before(function() {
|
||||
mockList.forEach(function(m) {
|
||||
sinon.stub(m,"init").callsFake(function(){});
|
||||
});
|
||||
sinon.stub(auth,"needsPermission").callsFake(function(permission) {
|
||||
return function(req,res,next) {
|
||||
permissionChecks[permission] = (permissionChecks[permission]||0)+1;
|
||||
next();
|
||||
};
|
||||
});
|
||||
|
||||
sinon.stub(flows,"get").callsFake(stubApp);
|
||||
sinon.stub(flows,"post").callsFake(stubApp);
|
||||
|
||||
sinon.stub(flow,"get").callsFake(stubApp);
|
||||
sinon.stub(flow,"post").callsFake(stubApp);
|
||||
sinon.stub(flow,"delete").callsFake(stubApp);
|
||||
sinon.stub(flow,"put").callsFake(stubApp);
|
||||
|
||||
sinon.stub(nodes,"getAll").callsFake(stubApp);
|
||||
sinon.stub(nodes,"post").callsFake(stubApp);
|
||||
sinon.stub(nodes,"getModule").callsFake(stubApp);
|
||||
sinon.stub(nodes,"putModule").callsFake(stubApp);
|
||||
sinon.stub(nodes,"delete").callsFake(stubApp);
|
||||
sinon.stub(nodes,"getSet").callsFake(stubApp);
|
||||
sinon.stub(nodes,"putSet").callsFake(stubApp);
|
||||
sinon.stub(nodes,"getModuleCatalog").callsFake(stubApp);
|
||||
sinon.stub(nodes,"getModuleCatalogs").callsFake(stubApp);
|
||||
|
||||
sinon.stub(context,"get").callsFake(stubApp);
|
||||
sinon.stub(context,"delete").callsFake(stubApp);
|
||||
});
|
||||
after(function() {
|
||||
mockList.forEach(function(m) {
|
||||
m.init.restore();
|
||||
});
|
||||
auth.needsPermission.restore();
|
||||
|
||||
flows.get.restore();
|
||||
flows.post.restore();
|
||||
flow.get.restore();
|
||||
flow.post.restore();
|
||||
flow.delete.restore();
|
||||
flow.put.restore();
|
||||
nodes.getAll.restore();
|
||||
nodes.post.restore();
|
||||
nodes.getModule.restore();
|
||||
nodes.putModule.restore();
|
||||
nodes.delete.restore();
|
||||
nodes.getSet.restore();
|
||||
nodes.putSet.restore();
|
||||
nodes.getModuleCatalog.restore();
|
||||
nodes.getModuleCatalogs.restore();
|
||||
context.get.restore();
|
||||
context.delete.restore();
|
||||
|
||||
});
|
||||
|
||||
before(function() {
|
||||
app = adminApi.init({},{});
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
permissionChecks = {};
|
||||
});
|
||||
|
||||
it('GET /flows', function(done) {
|
||||
request(app).get("/flows").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.read',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /flows', function(done) {
|
||||
request(app).post("/flows").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.write',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /flow/1234', function(done) {
|
||||
request(app).get("/flow/1234").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.read',1);
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /flow', function(done) {
|
||||
request(app).post("/flow").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.write',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /flow/1234', function(done) {
|
||||
request(app).del("/flow/1234").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.write',1);
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT /flow/1234', function(done) {
|
||||
request(app).put("/flow/1234").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('flows.write',1);
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes', function(done) {
|
||||
request(app).get("/nodes").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /nodes', function(done) {
|
||||
request(app).post("/nodes").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/module', function(done) {
|
||||
request(app).get("/nodes/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/@scope/module', function(done) {
|
||||
request(app).get("/nodes/@scope/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT /nodes/module', function(done) {
|
||||
request(app).put("/nodes/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT /nodes/@scope/module', function(done) {
|
||||
request(app).put("/nodes/@scope/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /nodes/module', function(done) {
|
||||
request(app).del("/nodes/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /nodes/@scope/module', function(done) {
|
||||
request(app).del("/nodes/@scope/module").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/module/set', function(done) {
|
||||
request(app).get("/nodes/module/set").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'module');
|
||||
lastRequest.params.should.have.property(2,'set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/@scope/module/set', function(done) {
|
||||
request(app).get("/nodes/@scope/module/set").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module');
|
||||
lastRequest.params.should.have.property(2,'set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT /nodes/module/set', function(done) {
|
||||
request(app).put("/nodes/module/set").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'module');
|
||||
lastRequest.params.should.have.property(2,'set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT /nodes/@scope/module/set', function(done) {
|
||||
request(app).put("/nodes/@scope/module/set").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.write',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module');
|
||||
lastRequest.params.should.have.property(2,'set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/messages', function(done) {
|
||||
request(app).get("/nodes/messages").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/module/set/messages', function(done) {
|
||||
request(app).get("/nodes/module/set/messages").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'module/set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /nodes/@scope/module/set/messages', function(done) {
|
||||
request(app).get("/nodes/@scope/module/set/messages").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('nodes.read',1);
|
||||
lastRequest.params.should.have.property(0,'@scope/module/set');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/global', function(done) {
|
||||
request(app).get("/context/global").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','global');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/global/key?store=memory', function(done) {
|
||||
request(app).get("/context/global/key?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','global');
|
||||
lastRequest.params.should.have.property(0,'key');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/flow/1234', function(done) {
|
||||
request(app).get("/context/flow/1234").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','flow');
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/flow/1234/key?store=memory', function(done) {
|
||||
request(app).get("/context/flow/1234/key?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','flow');
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
lastRequest.params.should.have.property(0,'key');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/node/5678', function(done) {
|
||||
request(app).get("/context/node/5678").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','node');
|
||||
lastRequest.params.should.have.property('id','5678');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /context/node/5678/foo?store=memory', function(done) {
|
||||
request(app).get("/context/node/5678/foo?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.read',1);
|
||||
lastRequest.params.should.have.property('scope','node');
|
||||
lastRequest.params.should.have.property('id','5678');
|
||||
lastRequest.params.should.have.property(0,'foo');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /context/global/key?store=memory', function(done) {
|
||||
request(app).del("/context/global/key?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.write',1);
|
||||
lastRequest.params.should.have.property('scope','global');
|
||||
lastRequest.params.should.have.property(0,'key');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /context/flow/1234/key?store=memory', function(done) {
|
||||
request(app).del("/context/flow/1234/key?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.write',1);
|
||||
lastRequest.params.should.have.property('scope','flow');
|
||||
lastRequest.params.should.have.property('id','1234');
|
||||
lastRequest.params.should.have.property(0,'key');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /context/node/5678/foo?store=memory', function(done) {
|
||||
request(app).del("/context/node/5678/foo?store=memory").expect(200).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
permissionChecks.should.have.property('context.write',1);
|
||||
lastRequest.params.should.have.property('scope','node');
|
||||
lastRequest.params.should.have.property('id','5678');
|
||||
lastRequest.params.should.have.property(0,'foo');
|
||||
lastRequest.query.should.have.property('store','memory');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,497 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var nodes = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/nodes");
|
||||
var apiUtil = NR_TEST_UTILS.require("@node-red/editor-api/lib/util");
|
||||
|
||||
describe("api/admin/nodes", function() {
|
||||
|
||||
var app;
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/nodes",nodes.getAll);
|
||||
app.post("/nodes",nodes.post);
|
||||
app.get(/\/nodes\/messages/,nodes.getModuleCatalogs);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,nodes.getModuleCatalog);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
|
||||
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
|
||||
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
|
||||
app.get("/getIcons",nodes.getIcons);
|
||||
app.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.delete);
|
||||
sinon.stub(apiUtil,"determineLangFromHeaders").callsFake(function() {
|
||||
return "en-US";
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
apiUtil.determineLangFromHeaders.restore();
|
||||
})
|
||||
|
||||
describe('get nodes', function() {
|
||||
it('returns node list', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getNodeList: function() {
|
||||
return Promise.resolve([1,2,3]);
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.be.an.Array();
|
||||
res.body.should.have.lengthOf(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns node configs', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getNodeConfigs: function() {
|
||||
return Promise.resolve("<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) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getModuleInfo: function(opts) {
|
||||
return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]);
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","node-red");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 for unknown module', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getModuleInfo: function(opts) {
|
||||
var errInstance = new Error("Not Found");
|
||||
errInstance.code = "not_found";
|
||||
errInstance.status = 404;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-blue')
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns individual node info', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getNodeInfo: function(opts) {
|
||||
return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]);
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red/123')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("id","node-red/123");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns individual node configs', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getNodeConfig: function(opts) {
|
||||
return Promise.resolve({"node-red/123":"<script></script>"}[opts.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) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getNodeInfo: function(opts) {
|
||||
var errInstance = new Error("Not Found");
|
||||
errInstance.code = "not_found";
|
||||
errInstance.status = 404;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/node-red/456')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('install', function() {
|
||||
it('installs the module and returns module info', function(done) {
|
||||
var opts;
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
addModule: function(_opts) {
|
||||
opts = _opts;
|
||||
return Promise.resolve({
|
||||
name:"foo",
|
||||
nodes:[{id:"123"}]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","foo");
|
||||
res.body.should.have.property("nodes");
|
||||
res.body.nodes[0].should.have.property("id","123");
|
||||
opts.should.have.property("module","foo");
|
||||
opts.should.have.property("version","1.2.3");
|
||||
opts.should.have.property("url","https://example/foo-1.2.3.tgz");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns error', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
addModule: function(opts) {
|
||||
var errInstance = new Error("Message");
|
||||
errInstance.code = "random_error";
|
||||
errInstance.status = 400;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post('/nodes')
|
||||
.send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.a.property('code','random_error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('delete', function() {
|
||||
it('uninstalls the module', function(done) {
|
||||
var opts;
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
removeModule: function(_opts) {
|
||||
opts = _opts;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.del('/nodes/123')
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
opts.should.have.property("module","123");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns error', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
removeModule: function(opts) {
|
||||
var errInstance = new Error("Message");
|
||||
errInstance.code = "random_error";
|
||||
errInstance.status = 400;
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.del('/nodes/123')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.a.property('code','random_error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('enable/disable node set', function() {
|
||||
it('returns 400 for invalid request payload', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
setNodeSetState: function(opts) {return Promise.resolve()}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("code","invalid_request");
|
||||
res.body.should.have.property("message","Invalid request");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets node state and returns node info', function(done) {
|
||||
var opts;
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
setNodeSetState: function(_opts) {
|
||||
opts = _opts;
|
||||
return Promise.resolve({id:"123",enabled: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red/foo')
|
||||
.send({enabled:true})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("id","123");
|
||||
res.body.should.have.property("enabled",true);
|
||||
opts.should.have.property("enabled",true);
|
||||
opts.should.have.property("id","node-red/foo");
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('enable/disable module' ,function() {
|
||||
it('returns 400 for invalid request payload', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
setModuleState: function(opts) {return Promise.resolve()}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({})
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("code","invalid_request");
|
||||
res.body.should.have.property("message","Invalid request");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('sets module state and returns module info', function(done) {
|
||||
var opts;
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
setModuleState: function(_opts) {
|
||||
opts = _opts;
|
||||
return Promise.resolve({name:"node-red"});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/nodes/node-red')
|
||||
.send({enabled:true})
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("name","node-red");
|
||||
opts.should.have.property("enabled",true);
|
||||
opts.should.have.property("module","node-red");
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get icons', function() {
|
||||
it('returns icon list', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getIconList: function() {
|
||||
return Promise.resolve({module:[1,2,3]});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/getIcons')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property("module");
|
||||
res.body.module.should.be.an.Array();
|
||||
res.body.module.should.have.lengthOf(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get module messages', function() {
|
||||
it('returns message catalog', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getModuleCatalog: function(opts) {
|
||||
return Promise.resolve({a:123});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/module/set/messages')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.eql({a:123});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns all node catalogs', function(done) {
|
||||
nodes.init({
|
||||
settings: {},
|
||||
nodes:{
|
||||
getModuleCatalogs: function(opts) {
|
||||
return Promise.resolve({a:1});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/nodes/messages')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.eql({a:1});
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -1,111 +0,0 @@
|
||||
const should = require("should");
|
||||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const bodyParser = require("body-parser");
|
||||
|
||||
var app;
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var plugins = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/plugins");
|
||||
|
||||
describe("api/editor/plugins", function() {
|
||||
const pluginList = [
|
||||
{
|
||||
"id": "test-module/test-set",
|
||||
"enabled": true,
|
||||
"local": false,
|
||||
"plugins": [
|
||||
{
|
||||
"type": "foo",
|
||||
"id": "a-plugin",
|
||||
"module": "test-module"
|
||||
},
|
||||
{
|
||||
"type": "bar",
|
||||
"id": "a-plugin2",
|
||||
"module": "test-module"
|
||||
},
|
||||
{
|
||||
"type": "foo",
|
||||
"id": "a-plugin3",
|
||||
"module": "test-module"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test-module/test-disabled-set",
|
||||
"enabled": false,
|
||||
"local": false,
|
||||
"plugins": []
|
||||
}
|
||||
];
|
||||
const pluginConfigs = `
|
||||
<!-- --- [red-plugin:test-module/test-set] --- -->
|
||||
test-module-config`;
|
||||
|
||||
const pluginCatalogs = { "test-module": {"foo": "bar"}};
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/plugins",plugins.getAll);
|
||||
app.get("/plugins/messages",plugins.getCatalogs);
|
||||
|
||||
plugins.init({
|
||||
plugins: {
|
||||
getPluginList: async function() { return pluginList },
|
||||
getPluginConfigs: async function() { return pluginConfigs },
|
||||
getPluginCatalogs: async function() { return pluginCatalogs }
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('returns the list of plugins', function(done) {
|
||||
request(app)
|
||||
.get("/plugins")
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
try {
|
||||
JSON.stringify(res.body).should.eql(JSON.stringify(pluginList));
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
});
|
||||
it('returns the plugin configs', function(done) {
|
||||
request(app)
|
||||
.get("/plugins")
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
.expect(pluginConfigs)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns the plugin catalogs', function(done) {
|
||||
request(app)
|
||||
.get("/plugins/messages")
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
try {
|
||||
JSON.stringify(res.body).should.eql(JSON.stringify(pluginCatalogs));
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require("body-parser");
|
||||
var sinon = require('sinon');
|
||||
|
||||
var app;
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/settings");
|
||||
var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme");
|
||||
|
||||
describe("api/editor/settings", function() {
|
||||
before(function() {
|
||||
sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };});
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/settings",info.runtimeSettings);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
theme.settings.restore();
|
||||
});
|
||||
|
||||
it('returns the runtime settings', function(done) {
|
||||
info.init({},{
|
||||
settings: {
|
||||
getRuntimeSettings: function(opts) {
|
||||
return Promise.resolve({
|
||||
a:1,
|
||||
b:2,
|
||||
editorTheme: { existing: 789 }
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
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",{existing: 789, test:456});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns the runtime settings - disableEditor true', function(done) {
|
||||
info.init({disableEditor: true},{
|
||||
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("a",1);
|
||||
res.body.should.have.property("b",2);
|
||||
// no editorTheme if disabledEditor true
|
||||
res.body.should.not.have.property("editorTheme");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients");
|
||||
|
||||
describe("api/auth/clients", function() {
|
||||
it('finds the known editor client',function(done) {
|
||||
Clients.get("node-red-editor").then(function(client) {
|
||||
client.should.have.property("id","node-red-editor");
|
||||
client.should.have.property("secret","not_available");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('finds the known admin client',function(done) {
|
||||
Clients.get("node-red-admin").then(function(client) {
|
||||
client.should.have.property("id","node-red-admin");
|
||||
client.should.have.property("secret","not_available");
|
||||
done();
|
||||
}).catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
it('returns null for unknown client',function(done) {
|
||||
Clients.get("unknown-client").then(function(client) {
|
||||
should.not.exist(client);
|
||||
done();
|
||||
}).catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,216 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
|
||||
var passport = require("passport");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth");
|
||||
var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users");
|
||||
var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens");
|
||||
var Permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions");
|
||||
|
||||
describe("api/auth/index",function() {
|
||||
|
||||
|
||||
|
||||
describe("ensureClientSecret", function() {
|
||||
before(function() {
|
||||
auth.init({},{})
|
||||
});
|
||||
it("leaves client_secret alone if not present",function(done) {
|
||||
var req = {
|
||||
body: {
|
||||
client_secret: "test_value"
|
||||
}
|
||||
};
|
||||
auth.ensureClientSecret(req,null,function() {
|
||||
req.body.should.have.a.property("client_secret","test_value");
|
||||
done();
|
||||
})
|
||||
});
|
||||
it("applies a default client_secret if not present",function(done) {
|
||||
var req = {
|
||||
body: { }
|
||||
};
|
||||
auth.ensureClientSecret(req,null,function() {
|
||||
req.body.should.have.a.property("client_secret","not_available");
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("revoke", function() {
|
||||
it("revokes a token", function(done) {
|
||||
var revokeToken = sinon.stub(Tokens,"revoke").callsFake(function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
var req = { body: { token: "abcdef" } };
|
||||
|
||||
var res = { status: function(resp) {
|
||||
revokeToken.restore();
|
||||
|
||||
resp.should.equal(200);
|
||||
return {
|
||||
end: done
|
||||
}
|
||||
}};
|
||||
|
||||
auth.revoke(req,res);
|
||||
});
|
||||
});
|
||||
|
||||
describe("login", function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(Tokens,"init").callsFake(function(){});
|
||||
sinon.stub(Users,"init").callsFake(function(){});
|
||||
});
|
||||
afterEach(function() {
|
||||
Tokens.init.restore();
|
||||
Users.init.restore();
|
||||
});
|
||||
it("returns login details - credentials", function(done) {
|
||||
auth.init({adminAuth:{type:"credentials"}},{})
|
||||
auth.login(null,{json: function(resp) {
|
||||
resp.should.have.a.property("type","credentials");
|
||||
resp.should.have.a.property("prompts");
|
||||
resp.prompts.should.have.a.lengthOf(2);
|
||||
done();
|
||||
}});
|
||||
});
|
||||
it("returns login details - none", function(done) {
|
||||
auth.init({},{})
|
||||
auth.login(null,{json: function(resp) {
|
||||
resp.should.eql({});
|
||||
done();
|
||||
}});
|
||||
});
|
||||
it("returns login details - strategy", function(done) {
|
||||
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");
|
||||
resp.prompts.should.have.a.lengthOf(1);
|
||||
resp.prompts[0].should.have.a.property("type","button");
|
||||
resp.prompts[0].should.have.a.property("label","test-strategy");
|
||||
resp.prompts[0].should.have.a.property("icon","test-icon");
|
||||
|
||||
done();
|
||||
}});
|
||||
});
|
||||
|
||||
});
|
||||
describe("needsPermission", function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(Tokens,"init").callsFake(function(){});
|
||||
sinon.stub(Users,"init").callsFake(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").callsFake(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").callsFake(function(scopes,opts) {
|
||||
return function(req,res,next) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
sinon.stub(Permissions,"hasPermission").callsFake(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").callsFake(function(scopes,opts) {
|
||||
return function(req,res,next) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
sinon.stub(Permissions,"hasPermission").callsFake(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").callsFake(function(scopes,opts) {
|
||||
return function(req,res,next) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
sinon.stub(Permissions,"hasPermission").callsFake(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"))
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions");
|
||||
|
||||
describe("api/auth/permissions", function() {
|
||||
describe("hasPermission", function() {
|
||||
it('a user with no permissions',function() {
|
||||
permissions.hasPermission([],"*").should.be.false();
|
||||
});
|
||||
it('a user with global permissions',function() {
|
||||
permissions.hasPermission("*","read").should.be.true();
|
||||
permissions.hasPermission(["*"],"write").should.be.true();
|
||||
});
|
||||
it('a user with read permissions',function() {
|
||||
permissions.hasPermission(["read"],"read").should.be.true();
|
||||
permissions.hasPermission(["read"],"node.read").should.be.true();
|
||||
permissions.hasPermission(["read"],"write").should.be.false();
|
||||
permissions.hasPermission(["read"],"node.write").should.be.false();
|
||||
permissions.hasPermission(["*.read"],"read").should.be.true();
|
||||
permissions.hasPermission(["*.read"],"node.read").should.be.true();
|
||||
permissions.hasPermission(["*.read"],"write").should.be.false();
|
||||
permissions.hasPermission(["*.read"],"node.write").should.be.false();
|
||||
});
|
||||
it('a user with foo permissions',function() {
|
||||
permissions.hasPermission("foo","foo").should.be.true();
|
||||
});
|
||||
it('an array of permissions', function() {
|
||||
permissions.hasPermission(["*"],["foo.read","foo.write"]).should.be.true();
|
||||
permissions.hasPermission("read",["foo.read","foo.write"]).should.be.false();
|
||||
permissions.hasPermission("read",["foo.read","bar.read"]).should.be.true();
|
||||
permissions.hasPermission(["flows.read"],["flows.read"]).should.be.true();
|
||||
permissions.hasPermission(["flows.read"],["flows.write"]).should.be.false();
|
||||
permissions.hasPermission(["flows.read","nodes.write"],["flows.write"]).should.be.false();
|
||||
permissions.hasPermission(["flows.read","nodes.write"],["nodes.write"]).should.be.true();
|
||||
});
|
||||
it('permits an empty permission', function() {
|
||||
permissions.hasPermission("*","").should.be.true();
|
||||
permissions.hasPermission("read",[""]).should.be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,327 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies");
|
||||
var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users");
|
||||
var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens");
|
||||
var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients");
|
||||
|
||||
describe("api/auth/strategies", function() {
|
||||
describe("Password Token Exchange", function() {
|
||||
var userAuthentication;
|
||||
afterEach(function() {
|
||||
if (userAuthentication) {
|
||||
userAuthentication.restore();
|
||||
userAuthentication = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('Handles authentication failure',function(done) {
|
||||
userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
strategies.passwordTokenExchange({},"user","password","scope",function(err,token) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
token.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Handles scope overreach',function(done) {
|
||||
userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) {
|
||||
return Promise.resolve({username:"user",permissions:"read"});
|
||||
});
|
||||
|
||||
strategies.passwordTokenExchange({},"user","password","*",function(err,token) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
token.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Creates new token on authentication success',function(done) {
|
||||
userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) {
|
||||
return Promise.resolve({username:"user",permissions:"*"});
|
||||
});
|
||||
var tokenDetails = {};
|
||||
var tokenCreate = sinon.stub(Tokens,"create").callsFake(function(username,client,scope) {
|
||||
tokenDetails.username = username;
|
||||
tokenDetails.client = client;
|
||||
tokenDetails.scope = scope;
|
||||
return Promise.resolve({accessToken: "123456"});
|
||||
});
|
||||
|
||||
strategies.passwordTokenExchange({id:"myclient"},"user","password","read",function(err,token) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
token.should.equal("123456");
|
||||
tokenDetails.should.have.property("username","user");
|
||||
tokenDetails.should.have.property("client","myclient");
|
||||
tokenDetails.should.have.property("scope","read");
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
tokenCreate.restore();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("Anonymous Strategy", function() {
|
||||
it('Succeeds if anon user enabled',function(done) {
|
||||
var userDefault = sinon.stub(Users,"default").callsFake(function() {
|
||||
return Promise.resolve("anon");
|
||||
});
|
||||
strategies.anonymousStrategy._success = strategies.anonymousStrategy.success;
|
||||
strategies.anonymousStrategy.success = function(user) {
|
||||
user.should.equal("anon");
|
||||
strategies.anonymousStrategy.success = strategies.anonymousStrategy._success;
|
||||
delete strategies.anonymousStrategy._success;
|
||||
done();
|
||||
};
|
||||
strategies.anonymousStrategy.authenticate({});
|
||||
});
|
||||
it('Fails if anon user not enabled',function(done) {
|
||||
var userDefault = sinon.stub(Users,"default").callsFake(function() {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
strategies.anonymousStrategy._fail = strategies.anonymousStrategy.fail;
|
||||
strategies.anonymousStrategy.fail = function(err) {
|
||||
err.should.equal(401);
|
||||
strategies.anonymousStrategy.fail = strategies.anonymousStrategy._fail;
|
||||
delete strategies.anonymousStrategy._fail;
|
||||
done();
|
||||
};
|
||||
strategies.anonymousStrategy.authenticate({});
|
||||
});
|
||||
afterEach(function() {
|
||||
Users.default.restore();
|
||||
})
|
||||
});
|
||||
|
||||
describe("Tokens Strategy", function() {
|
||||
it('Succeeds if tokens user enabled custom header',function(done) {
|
||||
var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) {
|
||||
return Promise.resolve("tokens-"+token);
|
||||
});
|
||||
var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) {
|
||||
return "x-test-token";
|
||||
});
|
||||
strategies.tokensStrategy._success = strategies.tokensStrategy.success;
|
||||
strategies.tokensStrategy.success = function(user) {
|
||||
user.should.equal("tokens-1234");
|
||||
strategies.tokensStrategy.success = strategies.tokensStrategy._success;
|
||||
delete strategies.tokensStrategy._success;
|
||||
done();
|
||||
};
|
||||
strategies.tokensStrategy.authenticate({headers:{"x-test-token":"1234"}});
|
||||
});
|
||||
it('Succeeds if tokens user enabled default header',function(done) {
|
||||
var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) {
|
||||
return Promise.resolve("tokens-"+token);
|
||||
});
|
||||
var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) {
|
||||
return "authorization";
|
||||
});
|
||||
strategies.tokensStrategy._success = strategies.tokensStrategy.success;
|
||||
strategies.tokensStrategy.success = function(user) {
|
||||
user.should.equal("tokens-1234");
|
||||
strategies.tokensStrategy.success = strategies.tokensStrategy._success;
|
||||
delete strategies.tokensStrategy._success;
|
||||
done();
|
||||
};
|
||||
strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}});
|
||||
});
|
||||
it('Fails if tokens user not enabled',function(done) {
|
||||
var userTokens = sinon.stub(Users,"tokens").callsFake(function() {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) {
|
||||
return "authorization";
|
||||
});
|
||||
strategies.tokensStrategy._fail = strategies.tokensStrategy.fail;
|
||||
strategies.tokensStrategy.fail = function(err) {
|
||||
err.should.equal(401);
|
||||
strategies.tokensStrategy.fail = strategies.tokensStrategy._fail;
|
||||
delete strategies.tokensStrategy._fail;
|
||||
done();
|
||||
};
|
||||
strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}});
|
||||
});
|
||||
afterEach(function() {
|
||||
Users.tokens.restore();
|
||||
Users.tokenHeader.restore();
|
||||
})
|
||||
});
|
||||
|
||||
describe("Bearer Strategy", function() {
|
||||
it('Rejects invalid token',function(done) {
|
||||
var getToken = sinon.stub(Tokens,"get").callsFake(function(token) {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
strategies.bearerStrategy("1234",function(err,user) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
user.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getToken.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
it('Accepts valid token',function(done) {
|
||||
var getToken = sinon.stub(Tokens,"get").callsFake(function(token) {
|
||||
return Promise.resolve({user:"user",scope:"scope"});
|
||||
});
|
||||
var getUser = sinon.stub(Users,"get").callsFake(function(username) {
|
||||
return Promise.resolve("aUser");
|
||||
});
|
||||
|
||||
strategies.bearerStrategy("1234",function(err,user,opts) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
user.should.equal("aUser");
|
||||
opts.should.have.a.property("scope","scope");
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getToken.restore();
|
||||
getUser.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
it('Fail if no user for token',function(done) {
|
||||
var getToken = sinon.stub(Tokens,"get").callsFake(function(token) {
|
||||
return Promise.resolve({user:"user",scope:"scope"});
|
||||
});
|
||||
var getUser = sinon.stub(Users,"get").callsFake(function(username) {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
strategies.bearerStrategy("1234",function(err,user,opts) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
user.should.equal(false);
|
||||
should.not.exist(opts);
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getToken.restore();
|
||||
getUser.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Client Password Strategy", function() {
|
||||
it('Accepts valid client',function(done) {
|
||||
var testClient = {id:"node-red-editor",secret:"not_available"};
|
||||
var getClient = sinon.stub(Clients,"get").callsFake(function(client) {
|
||||
return Promise.resolve(testClient);
|
||||
});
|
||||
|
||||
strategies.clientPasswordStrategy(testClient.id,testClient.secret,function(err,client) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
client.should.eql(testClient);
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getClient.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
it('Rejects invalid client secret',function(done) {
|
||||
var testClient = {id:"node-red-editor",secret:"not_available"};
|
||||
var getClient = sinon.stub(Clients,"get").callsFake(function(client) {
|
||||
return Promise.resolve(testClient);
|
||||
});
|
||||
|
||||
strategies.clientPasswordStrategy(testClient.id,"invalid_secret",function(err,client) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
client.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getClient.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
it('Rejects invalid client id',function(done) {
|
||||
var getClient = sinon.stub(Clients,"get").callsFake(function(client) {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
strategies.clientPasswordStrategy("invalid_id","invalid_secret",function(err,client) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
client.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
getClient.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var userAuthentication;
|
||||
it('Blocks after 5 failures',function(done) {
|
||||
userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
for (var z=0; z<5; z++) {
|
||||
strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) {
|
||||
});
|
||||
}
|
||||
strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) {
|
||||
try {
|
||||
err.toString().should.equal("Error: Too many login attempts. Wait 10 minutes and try again");
|
||||
token.should.be.false();
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
} finally {
|
||||
userAuthentication.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,180 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens");
|
||||
|
||||
|
||||
describe("api/auth/tokens", function() {
|
||||
describe("#init",function() {
|
||||
it('loads sessions', function(done) {
|
||||
Tokens.init({}).then(done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("#get",function() {
|
||||
it('returns a valid token', function(done) {
|
||||
Tokens.init({},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}});
|
||||
}
|
||||
}).then(function() {
|
||||
Tokens.get("1234").then(function(token) {
|
||||
try {
|
||||
token.should.have.a.property("user","fred");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null for an invalid token', function(done) {
|
||||
Tokens.init({},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}).then(function() {
|
||||
Tokens.get("1234").then(function(token) {
|
||||
try {
|
||||
should.not.exist(token);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
it('returns null for an expired token', function(done) {
|
||||
var saveSessions = sinon.stub().returns(Promise.resolve());
|
||||
var expiryTime = Date.now()+50;
|
||||
Tokens.init({},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({"1234":{"user":"fred","expires":expiryTime}});
|
||||
},
|
||||
saveSessions: saveSessions
|
||||
}).then(function() {
|
||||
Tokens.get("1234").then(function(token) {
|
||||
try {
|
||||
should.exist(token);
|
||||
setTimeout(function() {
|
||||
Tokens.get("1234").then(function(token) {
|
||||
try {
|
||||
should.not.exist(token);
|
||||
saveSessions.calledOnce.should.be.true();
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
},100);
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a valid api token', function(done) {
|
||||
Tokens.init({
|
||||
tokens: [{
|
||||
token: "1234",
|
||||
user: "fred",
|
||||
}]
|
||||
},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}).then(function() {
|
||||
Tokens.get("1234").then(function(token) {
|
||||
try {
|
||||
token.should.have.a.property("user","fred");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("#create",function() {
|
||||
it('creates a token', function(done) {
|
||||
var savedSession;
|
||||
Tokens.init({sessionExpiryTime: 10},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({});
|
||||
},
|
||||
saveSessions:function(sess) {
|
||||
savedSession = sess;
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
var expectedExpiryTime = Date.now()+10000;
|
||||
|
||||
|
||||
Tokens.create("user","client","scope").then(function(token) {
|
||||
try {
|
||||
should.exist(savedSession);
|
||||
var sessionKeys = Object.keys(savedSession);
|
||||
sessionKeys.should.have.lengthOf(1);
|
||||
|
||||
token.should.have.a.property('accessToken',sessionKeys[0]);
|
||||
savedSession[sessionKeys[0]].should.have.a.property('user','user');
|
||||
savedSession[sessionKeys[0]].should.have.a.property('client','client');
|
||||
savedSession[sessionKeys[0]].should.have.a.property('scope','scope');
|
||||
savedSession[sessionKeys[0]].should.have.a.property('expires');
|
||||
savedSession[sessionKeys[0]].expires.should.be.within(expectedExpiryTime-200,expectedExpiryTime+200);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#revoke", function() {
|
||||
it('revokes a token', function(done) {
|
||||
var savedSession;
|
||||
Tokens.init({},{
|
||||
getSessions:function() {
|
||||
return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}});
|
||||
},
|
||||
saveSessions:function(sess) {
|
||||
savedSession = sess;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}).then(function() {
|
||||
Tokens.revoke("1234").then(function() {
|
||||
try {
|
||||
savedSession.should.not.have.a.property("1234");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,276 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users");
|
||||
|
||||
describe("api/auth/users", function() {
|
||||
after(function() {
|
||||
Users.init({});
|
||||
})
|
||||
describe('Initalised with a credentials object, no anon',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"credentials",
|
||||
users:{
|
||||
username:"fred",
|
||||
password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK',
|
||||
// 'password' -> require('bcryptjs').hashSync('password', 8);
|
||||
permissions:"*"
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('#get',function() {
|
||||
it('returns known user',function(done) {
|
||||
Users.get("fred").then(function(user) {
|
||||
try {
|
||||
user.should.have.a.property("username","fred");
|
||||
user.should.have.a.property("permissions","*");
|
||||
user.should.not.have.a.property("password");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null for unknown user', function(done) {
|
||||
Users.get("barney").then(function(user) {
|
||||
try {
|
||||
should.not.exist(user);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#default',function() {
|
||||
it('returns null for default user', function(done) {
|
||||
Users.default().then(function(user) {
|
||||
try {
|
||||
should.not.exist(user);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#authenticate',function() {
|
||||
it('authenticates a known user', function(done) {
|
||||
Users.authenticate('fred','password').then(function(user) {
|
||||
try {
|
||||
user.should.have.a.property("username","fred");
|
||||
user.should.have.a.property("permissions","*");
|
||||
user.should.not.have.a.property("password");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('rejects invalid password for a known user', function(done) {
|
||||
Users.authenticate('fred','wrong').then(function(user) {
|
||||
try {
|
||||
should.not.exist(user);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects invalid user', function(done) {
|
||||
Users.authenticate('barney','wrong').then(function(user) {
|
||||
try {
|
||||
should.not.exist(user);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initalised with a credentials object including anon',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"credentials",
|
||||
users:[],
|
||||
default: { permissions: "*" }
|
||||
});
|
||||
});
|
||||
describe('#default',function() {
|
||||
it('returns default user', function(done) {
|
||||
Users.default().then(function(user) {
|
||||
try {
|
||||
user.should.have.a.property('anonymous',true);
|
||||
user.should.have.a.property('permissions','*');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialised with a credentials object with user functions',function() {
|
||||
var authUsername = '';
|
||||
var authPassword = '';
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"credentials",
|
||||
users:function(username) {
|
||||
return Promise.resolve({'username':'dave','permissions':'read'});
|
||||
},
|
||||
authenticate: function(username,password) {
|
||||
authUsername = username;
|
||||
authPassword = password;
|
||||
return Promise.resolve({'username':'pete','permissions':'write'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#get',function() {
|
||||
it("returns null for tokenHeader", function() {
|
||||
should.not.exist(Users.tokenHeader());
|
||||
});
|
||||
|
||||
it('delegates get user',function(done) {
|
||||
Users.get('dave').then(function(user) {
|
||||
try {
|
||||
user.should.have.a.property("username","dave");
|
||||
user.should.have.a.property("permissions","read");
|
||||
user.should.not.have.a.property("password");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('delegates authenticate user',function(done) {
|
||||
Users.authenticate('pete','secret').then(function(user) {
|
||||
try {
|
||||
user.should.have.a.property("username","pete");
|
||||
user.should.have.a.property("permissions","write");
|
||||
user.should.not.have.a.property("password");
|
||||
authUsername.should.equal('pete');
|
||||
authPassword.should.equal('secret');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialised with bad settings to test else cases',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"foo",
|
||||
users:{
|
||||
username:"fred",
|
||||
password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK',
|
||||
permissions:"*"
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('#get',function() {
|
||||
it('should fail to return user fred',function(done) {
|
||||
Users.get("fred").then(function(userf) {
|
||||
try {
|
||||
should.not.exist(userf);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialised with default set as function',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"credentials",
|
||||
default: function() { return("Done"); }
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
Users.init({});
|
||||
});
|
||||
describe('#default',function() {
|
||||
it('handles api.default being a function',function(done) {
|
||||
Users.should.have.property('default').which.is.a.Function();
|
||||
(Users.default()).should.equal("Done");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialised with tokens set as function',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"strategy",
|
||||
tokens: function(token) { return("Done-"+token); }
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
Users.init({});
|
||||
});
|
||||
describe('#tokens',function() {
|
||||
it('handles api.tokens being a function',function(done) {
|
||||
Users.should.have.property('tokens').which.is.a.Function();
|
||||
(Users.tokens("1234")).should.equal("Done-1234");
|
||||
(Users.tokenHeader()).should.equal("authorization");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Initialised with tokens set as function and tokenHeader set as token header name',function() {
|
||||
before(function() {
|
||||
Users.init({
|
||||
type:"strategy",
|
||||
tokens: function(token) { return("Done-"+token); },
|
||||
tokenHeader: "X-TEST-TOKEN"
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
Users.init({});
|
||||
});
|
||||
describe('#tokens',function() {
|
||||
it('handles api.tokens being a function and api.tokenHeader being a header name',function(done) {
|
||||
Users.should.have.property('tokens').which.is.a.Function();
|
||||
(Users.tokens("1234")).should.equal("Done-1234");
|
||||
Users.should.have.property('tokenHeader').which.is.a.Function();
|
||||
(Users.tokenHeader()).should.equal("x-test-token");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,618 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
const stoppable = require('stoppable');
|
||||
|
||||
var http = require('http');
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var WebSocket = require('ws');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms");
|
||||
var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users");
|
||||
var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens");
|
||||
var Strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies");
|
||||
var address = '127.0.0.1';
|
||||
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;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
it('accepts connection', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
connections.length.should.eql(0);
|
||||
ws.on('open', function() {
|
||||
try {
|
||||
connections.length.should.eql(1);
|
||||
ws.close();
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('publishes message after subscription', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic1"}');
|
||||
connections.length.should.eql(1);
|
||||
connections[0].send('topic1', 'foo');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('[{"topic":"topic1","data":"foo"}]');
|
||||
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"}');
|
||||
connections[0].send('topic3', 'correct');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('[{"topic":"topic3","data":"correct"}]');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("disabled editor", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {disableEditor:true}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("non-default httpAdminRoot set: /adminPath", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {httpAdminRoot:"/adminPath"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/adminPath/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("non-default httpAdminRoot set: /adminPath/", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {httpAdminRoot:"/adminPath/"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/adminPath/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("non-default httpAdminRoot set: adminPath", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {httpAdminRoot:"adminPath"}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/adminPath/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("keep alives", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {webSocketKeepAliveTime: 100}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
after(function(done) {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
it('are sent', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
ws.on('message', function(data) {
|
||||
var msg = JSON.parse(data)[0];
|
||||
msg.should.have.property('topic','hb');
|
||||
msg.should.have.property('data').be.a.Number();
|
||||
count++;
|
||||
if (count == 3) {
|
||||
ws.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
it('are not sent if other messages are sent', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
var interval;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
interval = setInterval(function() {
|
||||
connections[0].send('foo', 'bar');
|
||||
}, 50);
|
||||
});
|
||||
ws.on('message', function(data) {
|
||||
var msg = JSON.parse(data)[0];
|
||||
// It is possible a heartbeat message may arrive - so ignore them
|
||||
if (msg.topic != "hb") {
|
||||
msg.should.have.property('topic', 'foo');
|
||||
msg.should.have.property('data', 'bar');
|
||||
count++;
|
||||
if (count == 5) {
|
||||
clearInterval(interval);
|
||||
ws.close();
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('authentication required, no anonymous',function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
var getDefaultUser;
|
||||
var getUser;
|
||||
var getToken;
|
||||
var getUserToken;
|
||||
var getUserTokenHeader;
|
||||
var authenticateUserToken;
|
||||
var onSessionExpiry;
|
||||
var onSessionExpiryCallback;
|
||||
|
||||
before(function(done) {
|
||||
getDefaultUser = sinon.stub(Users,"default").callsFake(function() { return Promise.resolve(null);});
|
||||
getUser = sinon.stub(Users,"get").callsFake(function(username) {
|
||||
if (username == "fred") {
|
||||
return Promise.resolve({permissions:"read"});
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
getUserToken = sinon.stub(Users,"tokens").callsFake(function(token) {
|
||||
if (token == "abcde") {
|
||||
return Promise.resolve({user:"wilma", permissions:"*"})
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
getToken = sinon.stub(Tokens,"get").callsFake(function(token) {
|
||||
if (token == "1234") {
|
||||
return Promise.resolve({user:"fred",scope:["*"]});
|
||||
} else if (token == "5678") {
|
||||
return Promise.resolve({user:"barney",scope:["*"]});
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
getUserTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function() {
|
||||
return "custom-header"
|
||||
})
|
||||
authenticateUserToken = sinon.stub(Strategies, "authenticateUserToken").callsFake(async function(req) {
|
||||
var token = req.headers['custom-header'];
|
||||
if (token === "knock-knock") {
|
||||
return {user:"fred",scope:["*"]}
|
||||
}
|
||||
throw new Error("Invalid user");
|
||||
})
|
||||
onSessionExpiry = sinon.stub(Tokens,"onSessionExpiry").callsFake(function(cb) {
|
||||
onSessionExpiryCallback = cb;
|
||||
});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {adminAuth:{}}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
after(function(done) {
|
||||
getDefaultUser.restore();
|
||||
getUser.restore();
|
||||
getToken.restore();
|
||||
getUserToken.restore();
|
||||
getUserTokenHeader.restore();
|
||||
authenticateUserToken.restore();
|
||||
onSessionExpiry.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
it('prevents connections that do not authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
var interval;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
});
|
||||
ws.on('close', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('allows connections that do authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"auth":"1234"}');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
received++;
|
||||
if (received == 1) {
|
||||
msg.should.equal('{"auth":"ok"}');
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
connections[0].send('foo', 'correct');
|
||||
} else {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
received.should.equal(2);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('allows connections that do authenticate - header-provided-token',function(done) {
|
||||
var ws = new WebSocket(url,{
|
||||
headers: { "custom-header": "knock-knock" }
|
||||
});
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
connections.should.have.length(1);
|
||||
connections[0].send('foo', 'correct');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
received++;
|
||||
if (received == 1) {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
received.should.equal(1);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('allows connections that do authenticate - user-provided-token',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"auth":"abcde"}');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
received++;
|
||||
if (received == 1) {
|
||||
msg.should.equal('{"auth":"ok"}');
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
connections[0].send('foo', 'correct');
|
||||
} else {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
received.should.equal(2);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('rejects connections for non-existant token',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"auth":"2345"}');
|
||||
});
|
||||
ws.on('close', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects connections for invalid token',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"auth":"5678"}');
|
||||
});
|
||||
ws.on('close', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects connections for invalid token - header-provided-token',function(done) {
|
||||
var ws = new WebSocket(url,{
|
||||
headers: { "custom-header": "bad token" }
|
||||
});
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
});
|
||||
ws.on('error', function() {
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it("expires websocket sessions", function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"auth":"1234"}');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
received++;
|
||||
if (received == 3) {
|
||||
msg.should.equal('{"auth":"fail"}');
|
||||
} else if (received == 1) {
|
||||
msg.should.equal('{"auth":"ok"}');
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
connections[0].send('foo', 'correct');
|
||||
} else {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
setTimeout(function() {
|
||||
onSessionExpiryCallback({accessToken:"1234"})
|
||||
},50);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
received.should.equal(3);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe('authentication required, anonymous enabled',function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
var getDefaultUser;
|
||||
before(function(done) {
|
||||
getDefaultUser = sinon.stub(Users,"default").callsFake(function() { return Promise.resolve({permissions:"read"});});
|
||||
server = stoppable(http.createServer(function(req,res){app(req,res)}));
|
||||
comms.init(server, {adminAuth:{}}, {comms: mockComms});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
url = 'http://' + address + ':' + port + '/comms';
|
||||
comms.start();
|
||||
done();
|
||||
});
|
||||
});
|
||||
after(function(done) {
|
||||
getDefaultUser.restore();
|
||||
comms.stop();
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
it('allows anonymous connections that do not authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
var interval;
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"foo"}');
|
||||
setTimeout(function() {
|
||||
connections[0].send('foo', 'correct');
|
||||
},200);
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('[{"topic":"foo","data":"correct"}]');
|
||||
count++;
|
||||
ws.close();
|
||||
});
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
count.should.equal(1);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var credentials = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/credentials");
|
||||
|
||||
describe('api/editor/credentials', function() {
|
||||
var app;
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.get('/credentials/:type/:id',credentials.get);
|
||||
credentials.init({
|
||||
flows: {
|
||||
getNodeCredentials: function(opts) {
|
||||
if (opts.type === "known-type" && opts.id === "n1") {
|
||||
return Promise.resolve({
|
||||
user1:"abc",
|
||||
has_password1: true
|
||||
});
|
||||
} else {
|
||||
var err = new Error("message");
|
||||
err.code = "test_code";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
} else {
|
||||
try {
|
||||
res.body.should.have.a.property("user1","abc");
|
||||
res.body.should.not.have.a.property("password1");
|
||||
res.body.should.have.a.property("has_password1",true);
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
it('returns any error',function(done) {
|
||||
request(app)
|
||||
.get("/credentials/unknown-type/n2")
|
||||
.expect("Content-Type",/json/)
|
||||
.expect(400)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,132 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var editorApi = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor");
|
||||
var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms");
|
||||
var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings");
|
||||
var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth");
|
||||
|
||||
var log = NR_TEST_UTILS.require("@node-red/util").log;
|
||||
|
||||
describe("api/editor/index", function() {
|
||||
var app;
|
||||
describe("disabled the editor", function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(comms,'init').callsFake(function(){});
|
||||
sinon.stub(info,'init').callsFake(function(){});
|
||||
});
|
||||
afterEach(function() {
|
||||
comms.init.restore();
|
||||
info.init.restore();
|
||||
});
|
||||
it("disables the editor", function() {
|
||||
var editorApp = editorApi.init({},{disableEditor:true},{});
|
||||
should.not.exist(editorApp);
|
||||
comms.init.called.should.be.false();
|
||||
info.init.called.should.be.false();
|
||||
});
|
||||
});
|
||||
describe("enables the editor", function() {
|
||||
var mockList = [
|
||||
'library','theme','locales','credentials','comms',"settings"
|
||||
]
|
||||
var isStarted = true;
|
||||
var errors = [];
|
||||
var session_data = {};
|
||||
before(function() {
|
||||
sinon.stub(auth,'needsPermission').callsFake(function(permission) {
|
||||
return function(req,res,next) { next(); }
|
||||
});
|
||||
mockList.forEach(function(m) {
|
||||
sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m),"init").callsFake(function(){});
|
||||
});
|
||||
sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"),"app").callsFake(function(){ return express()});
|
||||
});
|
||||
after(function() {
|
||||
mockList.forEach(function(m) {
|
||||
NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m).init.restore();
|
||||
})
|
||||
NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme").app.restore();
|
||||
auth.needsPermission.restore();
|
||||
log.error.restore();
|
||||
});
|
||||
|
||||
before(function() {
|
||||
sinon.stub(log,"error").callsFake(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) {
|
||||
request(app)
|
||||
.get("/")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
// Index page should probably mention Node-RED somewhere
|
||||
res.text.indexOf("Node-RED").should.not.eql(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('serves icons', function(done) {
|
||||
request(app)
|
||||
.get("/red/images/icons/arrow-in.svg")
|
||||
.expect(200)
|
||||
.expect("Content-Type", /image\/svg\+xml/)
|
||||
.end(function(err,res) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
it('handles page not there', function(done) {
|
||||
request(app)
|
||||
.get("/foo")
|
||||
.expect(404,done)
|
||||
});
|
||||
it('warns if runtime not started', function(done) {
|
||||
isStarted = false;
|
||||
request(app)
|
||||
.get("/")
|
||||
.expect(503)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.text.should.eql("Not started");
|
||||
errors.should.have.lengthOf(1);
|
||||
errors[0].should.eql("Node-RED runtime not started");
|
||||
done();
|
||||
});
|
||||
});
|
||||
// it.skip('GET /settings', function(done) {
|
||||
// request(app).get("/settings").expect(200).end(function(err,res) {
|
||||
// if (err) {
|
||||
// return done(err);
|
||||
// }
|
||||
// // permissionChecks.should.have.property('settings.read',1);
|
||||
// done();
|
||||
// })
|
||||
// });
|
||||
});
|
||||
});
|
||||
@@ -1,263 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var library = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/library");
|
||||
|
||||
var app;
|
||||
|
||||
describe("api/editor/library", function() {
|
||||
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,library.getEntry);
|
||||
app.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,library.saveEntry);
|
||||
});
|
||||
after(function() {
|
||||
});
|
||||
|
||||
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}');
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/library/local/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('library','local');
|
||||
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});
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/library/local/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('library','local');
|
||||
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}');
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get('/library/local/non-flow/abc')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
opts.should.have.property('library','local');
|
||||
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/local/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('library','local');
|
||||
opts.should.have.property('type','non-flow');
|
||||
opts.should.have.property('path','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/local/flows/123')
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
opts.should.have.property('library','local');
|
||||
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('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/local/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('library','local');
|
||||
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('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/local/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('library','local');
|
||||
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 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/local/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('library','local');
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,165 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var locales = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/locales");
|
||||
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
|
||||
|
||||
describe("api/editor/locales", function() {
|
||||
beforeEach(function() {
|
||||
})
|
||||
afterEach(function() {
|
||||
})
|
||||
describe('get named resource catalog',function() {
|
||||
var app;
|
||||
before(function() {
|
||||
// locales.init({
|
||||
// i18n: {
|
||||
// i: {
|
||||
// language: function() { return 'en-US'},
|
||||
// changeLanguage: function(lang,callback) {
|
||||
// if (callback) {
|
||||
// callback();
|
||||
// }
|
||||
// },
|
||||
// getResourceBundle: function(lang, namespace) {
|
||||
// return {namespace:namespace, lang:lang};
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
// });
|
||||
locales.init({});
|
||||
|
||||
// bit of a mess of internal workings
|
||||
sinon.stub(i18n.i,'changeLanguage').callsFake(function(lang,callback) { if (callback) {callback();}});
|
||||
if (i18n.i.getResourceBundle) {
|
||||
sinon.stub(i18n.i,'getResourceBundle').callsFake(function(lang, namespace) {return {namespace:namespace, lang:lang};});
|
||||
} else {
|
||||
// If i18n.init has not been called, then getResourceBundle isn't
|
||||
// defined - so hardcode a stub
|
||||
i18n.i.getResourceBundle = function(lang, namespace) {return {namespace:namespace, lang:lang};};
|
||||
i18n.i.getResourceBundle.restore = function() { delete i18n.i.getResourceBundle };
|
||||
}
|
||||
app = express();
|
||||
app.get(/locales\/(.+)\/?$/,locales.get);
|
||||
});
|
||||
after(function() {
|
||||
i18n.i.changeLanguage.restore();
|
||||
i18n.i.getResourceBundle.restore();
|
||||
})
|
||||
it('returns with default language', function(done) {
|
||||
request(app)
|
||||
.get("/locales/message-catalog")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('namespace','message-catalog');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns with selected language', function(done) {
|
||||
request(app)
|
||||
.get("/locales/message-catalog?lng=fr-FR")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('namespace','message-catalog');
|
||||
res.body.should.have.property('lang','fr-FR');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns for locale defined only with primary tag ', function(done) {
|
||||
var orig = i18n.i.getResourceBundle;
|
||||
i18n.i.getResourceBundle = function (lang, ns) {
|
||||
if (lang === "ja-JP") {
|
||||
return undefined;
|
||||
}
|
||||
return orig(lang, ns);
|
||||
};
|
||||
request(app)
|
||||
// returns `ja` instead of `ja-JP`
|
||||
.get("/locales/message-catalog?lng=ja-JP")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
i18n.i.getResourceBundle = orig;
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('namespace','message-catalog');
|
||||
res.body.should.have.property('lang','ja');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// describe('get all node resource catalogs',function() {
|
||||
// var app;
|
||||
// before(function() {
|
||||
// // bit of a mess of internal workings
|
||||
// sinon.stub(i18n,'catalog').callsFake(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(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")
|
||||
// .expect(200)
|
||||
// .end(function(err,res) {
|
||||
// if (err) {
|
||||
// return done(err);
|
||||
// }
|
||||
// res.body.should.eql({
|
||||
// 'test-module-a-id': 'test-module-a-catalog',
|
||||
// 'test-module-b-id': 'test-module-b-catalog'
|
||||
// });
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
describe("api/editor/projects", function() {
|
||||
it.skip("NEEDS TESTS WRITING",function() {});
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
var bodyParser = require("body-parser");
|
||||
var sinon = require('sinon');
|
||||
|
||||
var app;
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings");
|
||||
var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme");
|
||||
|
||||
describe("api/editor/settings", function() {
|
||||
before(function() {
|
||||
sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };});
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings);
|
||||
app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
theme.settings.restore();
|
||||
});
|
||||
|
||||
it('returns the user settings', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
getUserSettings: function(opts) {
|
||||
if (opts.user !== "fred") {
|
||||
return Promise.reject(new Error("Unknown user"));
|
||||
}
|
||||
return Promise.resolve({
|
||||
c:3,
|
||||
d:4
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings/user")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.eql({c:3,d:4});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('updates the user settings', function(done) {
|
||||
var update;
|
||||
info.init({
|
||||
settings: {
|
||||
updateUserSettings: function(opts) {
|
||||
if (opts.user !== "fred") {
|
||||
return Promise.reject(new Error("Unknown user"));
|
||||
}
|
||||
update = opts.settings;
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post("/settings/user")
|
||||
.send({
|
||||
e:4,
|
||||
f:5
|
||||
})
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
update.should.eql({e:4,f:5});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,333 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
var sshkeys = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/sshkeys");
|
||||
var bodyParser = require("body-parser");
|
||||
|
||||
|
||||
describe("api/editor/sshkeys", function() {
|
||||
var app;
|
||||
var mockRuntime = {
|
||||
settings: {
|
||||
getUserKeys: function() {},
|
||||
getUserKey: function() {},
|
||||
generateUserKey: function() {},
|
||||
removeUserKey: function() {}
|
||||
}
|
||||
}
|
||||
before(function() {
|
||||
sshkeys.init(mockRuntime);
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.use("/settings/user/keys", sshkeys.app());
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
sinon.stub(mockRuntime.settings, "getUserKeys");
|
||||
sinon.stub(mockRuntime.settings, "getUserKey");
|
||||
sinon.stub(mockRuntime.settings, "generateUserKey");
|
||||
sinon.stub(mockRuntime.settings, "removeUserKey");
|
||||
})
|
||||
afterEach(function() {
|
||||
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.settings.getUserKeys.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.settings.getUserKeys.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.settings.getUserKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
|
||||
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)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
|
||||
var errInstance = new Error();
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.settings.getUserKeys.returns(p)
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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.settings.getUserKey.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.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();
|
||||
});
|
||||
});
|
||||
|
||||
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.settings.getUserKey.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('code');
|
||||
res.body.code.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.settings.getUserKey.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('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal("Messages.....");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.settings.generateUserKey.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 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.settings.generateUserKey.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('code');
|
||||
res.body.code.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.settings.generateUserKey.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('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
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.settings.removeUserKey.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.settings.removeUserKey.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('code');
|
||||
res.body.code.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.settings.removeUserKey.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('code');
|
||||
res.body.code.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal('Messages.....');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,275 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require("supertest");
|
||||
var express = require('express');
|
||||
var sinon = require('sinon');
|
||||
var fs = require("fs");
|
||||
|
||||
var app = express();
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme");
|
||||
|
||||
describe("api/editor/theme", function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(fs, "statSync").callsFake(function () { return true; });
|
||||
});
|
||||
afterEach(function () {
|
||||
theme.init({settings: {}});
|
||||
fs.statSync.restore();
|
||||
});
|
||||
it("applies the default theme", async function () {
|
||||
var result = theme.init({});
|
||||
should.not.exist(result);
|
||||
|
||||
var context = await theme.context();
|
||||
context.should.have.a.property("page");
|
||||
context.page.should.have.a.property("title", "Node-RED");
|
||||
context.page.should.have.a.property("favicon", "favicon.ico");
|
||||
context.page.should.have.a.property("tabicon");
|
||||
context.page.tabicon.should.have.a.property("icon", "red/images/node-red-icon-black.svg");
|
||||
context.page.tabicon.should.have.a.property("colour", "#8f0000");
|
||||
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.svg");
|
||||
context.should.have.a.property("asset");
|
||||
context.asset.should.have.a.property("red", "red/red.min.js");
|
||||
context.asset.should.have.a.property("main", "red/main.min.js");
|
||||
context.asset.should.have.a.property("vendorMonaco", "");
|
||||
|
||||
should.not.exist(theme.settings());
|
||||
});
|
||||
|
||||
it("uses non-minified js files when in dev mode", async function () {
|
||||
const previousEnv = process.env.NODE_ENV;
|
||||
try {
|
||||
process.env.NODE_ENV = 'development'
|
||||
theme.init({});
|
||||
var context = await theme.context();
|
||||
context.asset.should.have.a.property("red", "red/red.js");
|
||||
context.asset.should.have.a.property("main", "red/main.js");
|
||||
} finally {
|
||||
process.env.NODE_ENV = previousEnv;
|
||||
}
|
||||
});
|
||||
|
||||
it("Adds monaco bootstrap when enabled", async function () {
|
||||
theme.init({
|
||||
editorTheme: {
|
||||
codeEditor: {
|
||||
lib: 'monaco'
|
||||
}
|
||||
}
|
||||
});
|
||||
var context = await theme.context();
|
||||
context.asset.should.have.a.property("vendorMonaco", "vendor/monaco/monaco-bootstrap.js");
|
||||
});
|
||||
|
||||
it("picks up custom theme", async function () {
|
||||
theme.init({
|
||||
editorTheme: {
|
||||
page: {
|
||||
title: "Test Page Title",
|
||||
favicon: "/absolute/path/to/theme/favicon",
|
||||
tabicon: {
|
||||
icon: "/absolute/path/to/theme/tabicon",
|
||||
colour: "#8f008f"
|
||||
},
|
||||
css: [
|
||||
"/absolute/path/to/custom/css/file.css"
|
||||
],
|
||||
scripts: "/absolute/path/to/script.js"
|
||||
},
|
||||
header: {
|
||||
title: "Test Header Title",
|
||||
url: "http://nodered.org",
|
||||
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,
|
||||
"menu-item-keyboard-shortcuts": false,
|
||||
"menu-item-help": {
|
||||
label: "Alternative Help Link Text",
|
||||
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
|
||||
},
|
||||
|
||||
palette: {
|
||||
editable: true,
|
||||
catalogues: ['https://catalogue.nodered.org/catalogue.json'],
|
||||
theme: [{ category: ".*", type: ".*", color: "#f0f" }]
|
||||
},
|
||||
|
||||
projects: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
theme.app();
|
||||
|
||||
var context = await theme.context();
|
||||
context.should.have.a.property("page");
|
||||
context.page.should.have.a.property("title", "Test Page Title");
|
||||
context.page.should.have.a.property("favicon", "theme/favicon/favicon");
|
||||
context.page.should.have.a.property("tabicon")
|
||||
context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon");
|
||||
context.page.tabicon.should.have.a.property("colour", "#8f008f")
|
||||
context.should.have.a.property("header");
|
||||
context.header.should.have.a.property("title", "Test Header Title");
|
||||
context.header.should.have.a.property("url", "http://nodered.org");
|
||||
context.header.should.have.a.property("image", "theme/header/image");
|
||||
context.page.should.have.a.property("css");
|
||||
context.page.css.should.have.lengthOf(1);
|
||||
context.page.css[0].should.eql('theme/css/file.css');
|
||||
context.page.should.have.a.property("scripts");
|
||||
context.page.scripts.should.have.lengthOf(1);
|
||||
context.page.scripts[0].should.eql('theme/scripts/script.js');
|
||||
context.should.have.a.property("login");
|
||||
context.login.should.have.a.property("image", "theme/login/image");
|
||||
|
||||
var settings = theme.settings();
|
||||
settings.should.have.a.property("deployButton");
|
||||
settings.deployButton.should.have.a.property("type", "simple");
|
||||
settings.deployButton.should.have.a.property("label", "Save");
|
||||
settings.deployButton.should.have.a.property("icon", "theme/deploy/image");
|
||||
settings.should.have.a.property("userMenu");
|
||||
settings.userMenu.should.be.eql(false);
|
||||
settings.should.have.a.property("menu");
|
||||
settings.menu.should.have.a.property("menu-item-import-library", false);
|
||||
settings.menu.should.have.a.property("menu-item-export-library", false);
|
||||
settings.menu.should.have.a.property("menu-item-keyboard-shortcuts", false);
|
||||
settings.menu.should.have.a.property("menu-item-help", { label: "Alternative Help Link Text", url: "http://example.com" });
|
||||
settings.should.have.a.property("palette");
|
||||
settings.palette.should.have.a.property("editable", true);
|
||||
settings.palette.should.have.a.property("catalogues", ['https://catalogue.nodered.org/catalogue.json']);
|
||||
settings.palette.should.have.a.property("theme", [{ category: ".*", type: ".*", color: "#f0f" }]);
|
||||
settings.should.have.a.property("projects");
|
||||
settings.projects.should.have.a.property("enabled", false);
|
||||
});
|
||||
|
||||
it("picks up backwards compatible tabicon setting", async function () {
|
||||
theme.init({
|
||||
editorTheme: {
|
||||
page: {
|
||||
tabicon: "/absolute/path/to/theme/tabicon",
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
theme.app();
|
||||
|
||||
var context = await theme.context();
|
||||
context.should.have.a.property("page");
|
||||
context.page.should.have.a.property("tabicon");
|
||||
context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon");
|
||||
// The colour property should remain as default in this case as the
|
||||
// legacy format for defining tabicon doesn't allow specifying a colour
|
||||
context.page.tabicon.should.have.a.property("colour", "#8f0000");
|
||||
|
||||
});
|
||||
|
||||
it("test explicit userMenu set to true in theme setting", function () {
|
||||
theme.init({
|
||||
editorTheme: {
|
||||
userMenu: true,
|
||||
}
|
||||
});
|
||||
|
||||
theme.app();
|
||||
|
||||
var settings = theme.settings();
|
||||
settings.should.have.a.property("userMenu");
|
||||
settings.userMenu.should.be.eql(true);
|
||||
|
||||
});
|
||||
|
||||
|
||||
it("includes list of plugin themes", function(done) {
|
||||
theme.init({},{
|
||||
plugins: { getPluginsByType: _ => [{id:"theme-plugin"}] }
|
||||
});
|
||||
const app = theme.app();
|
||||
request(app)
|
||||
.get("/")
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
try {
|
||||
const response = JSON.parse(res.text);
|
||||
response.should.have.property("themes");
|
||||
response.themes.should.eql(["theme-plugin"])
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("includes theme plugin settings", async function () {
|
||||
theme.init({
|
||||
editorTheme: {
|
||||
theme: 'test-theme'
|
||||
}
|
||||
},{
|
||||
plugins: { getPlugin: t => {
|
||||
return ({'test-theme':{
|
||||
path: '/abosolute/path/to/plugin',
|
||||
css: [
|
||||
"path/to/custom/css/file1.css",
|
||||
"/invalid/path/to/file2.css",
|
||||
"../another/invalid/path/file3.css"
|
||||
],
|
||||
scripts: [
|
||||
"path/to/custom/js/file1.js",
|
||||
"/invalid/path/to/file2.js",
|
||||
"../another/invalid/path/file3.js"
|
||||
]
|
||||
}})[t.id];
|
||||
} }
|
||||
});
|
||||
|
||||
theme.app();
|
||||
|
||||
var context = await theme.context();
|
||||
context.should.have.a.property("page");
|
||||
context.page.should.have.a.property("css");
|
||||
context.page.css.should.have.lengthOf(1);
|
||||
context.page.css[0].should.eql('theme/css/file1.css');
|
||||
context.page.should.have.a.property("scripts");
|
||||
context.page.scripts.should.have.lengthOf(1);
|
||||
context.page.scripts[0].should.eql('theme/scripts/file1.js');
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,210 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var ui = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/ui");
|
||||
|
||||
|
||||
describe("api/editor/ui", function() {
|
||||
var app;
|
||||
|
||||
before(function() {
|
||||
ui.init({
|
||||
nodes: {
|
||||
getIcon: function(opts) {
|
||||
return new Promise(function(resolve,reject) {
|
||||
if (opts.icon === "icon.png") {
|
||||
fs.readFile(NR_TEST_UTILS.resolve("@node-red/editor-client/src/images/icons/arrow-in.svg"), function(err,data) {
|
||||
resolve(data);
|
||||
})
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
getModuleResource: async function(opts) {
|
||||
if (opts.module !== "test-module" || opts.path !== "a/path/text.txt") {
|
||||
return null;
|
||||
} else {
|
||||
return "Some text data";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
describe("slash handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.get("/foo",ui.ensureSlash,function(req,res) { res.sendStatus(200);});
|
||||
});
|
||||
it('redirects if the path does not end in a slash',function(done) {
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(301,done);
|
||||
});
|
||||
it('redirects if the path, with query string, does not end in a slash',function(done) {
|
||||
request(app)
|
||||
.get('/foo?abc=def')
|
||||
.expect(301)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.header['location'].should.equal("/foo/?abc=def");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not redirect if the path ends in a slash',function(done) {
|
||||
request(app)
|
||||
.get('/foo/')
|
||||
.expect(200,done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("icon handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.get("/icons/:module/:icon",ui.icon);
|
||||
});
|
||||
|
||||
function binaryParser(res, callback) {
|
||||
res.setEncoding('binary');
|
||||
res.data = '';
|
||||
res.on('data', function (chunk) {
|
||||
res.data += chunk;
|
||||
});
|
||||
res.on('end', function () {
|
||||
callback(null, Buffer.from(res.data, 'binary'));
|
||||
});
|
||||
}
|
||||
function compareBuffers(b1,b2) {
|
||||
b1.length.should.equal(b2.length);
|
||||
for (var i=0;i<b1.length;i++) {
|
||||
b1[i].should.equal(b2[i]);
|
||||
}
|
||||
}
|
||||
it('returns the requested icon', function(done) {
|
||||
var defaultIcon = fs.readFileSync(NR_TEST_UTILS.resolve("@node-red/editor-client/src/images/icons/arrow-in.svg"));
|
||||
request(app)
|
||||
.get("/icons/module/icon.png")
|
||||
.expect("Content-Type", /image\/png/)
|
||||
.expect(200)
|
||||
.parse(binaryParser)
|
||||
.end(function(err,res) {
|
||||
if (err){
|
||||
return done(err);
|
||||
}
|
||||
Buffer.isBuffer(res.body).should.be.true();
|
||||
compareBuffers(res.body,defaultIcon);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
it('returns the default icon for invalid paths', function(done) {
|
||||
var defaultIcon = fs.readFileSync(NR_TEST_UTILS.resolve("@node-red/editor-client/src/images/icons/arrow-in.svg"));
|
||||
request(app)
|
||||
.get("/icons/module/unreal.png")
|
||||
.expect("Content-Type", /image\/svg/)
|
||||
.expect(200)
|
||||
.parse(binaryParser)
|
||||
.end(function(err,res) {
|
||||
if (err){
|
||||
return done(err);
|
||||
}
|
||||
Buffer.isBuffer(res.body).should.be.true();
|
||||
compareBuffers(res.body,defaultIcon);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
describe("module resource handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.get(/^\/resources\/((?:@[^\/]+\/)?[^\/]+)\/(.+)$/,ui.moduleResource);
|
||||
});
|
||||
|
||||
it('returns the requested resource', function(done) {
|
||||
request(app)
|
||||
.get("/resources/test-module/a/path/text.txt")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.text.should.eql('Some text data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('404s invalid paths', function(done) {
|
||||
request(app)
|
||||
.get("/resources/test-module/../a/path/text.txt")
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("editor ui handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use("/",ui.editor);
|
||||
});
|
||||
it('serves the editor', function(done) {
|
||||
request(app)
|
||||
.get("/")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
// Index page should probably mention Node-RED somewhere
|
||||
res.text.indexOf("Node-RED").should.not.eql(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("editor ui resource handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.use("/",ui.editorResources);
|
||||
});
|
||||
it('serves the editor resources', function(done) {
|
||||
request(app)
|
||||
.get("/favicon.ico")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
const auth = require("basic-auth");
|
||||
|
||||
var api = NR_TEST_UTILS.require("@node-red/editor-api");
|
||||
|
||||
var apiAuth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth");
|
||||
var apiEditor = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor");
|
||||
var apiAdmin = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin");
|
||||
|
||||
|
||||
describe("api/index", function() {
|
||||
var beforeEach = function() {
|
||||
sinon.stub(apiAuth,"init").callsFake(function(){});
|
||||
sinon.stub(apiEditor,"init").callsFake(function(){
|
||||
var app = express();
|
||||
app.get("/editor",function(req,res) { res.status(200).end(); });
|
||||
return app;
|
||||
});
|
||||
sinon.stub(apiAdmin,"init").callsFake(function(){
|
||||
var app = express();
|
||||
app.get("/admin",function(req,res) { res.status(200).end(); });
|
||||
return app;
|
||||
});
|
||||
sinon.stub(apiAuth,"login").callsFake(function(req,res){
|
||||
res.status(200).end();
|
||||
});
|
||||
};
|
||||
var afterEach = function() {
|
||||
apiAuth.init.restore();
|
||||
apiAuth.login.restore();
|
||||
apiEditor.init.restore();
|
||||
apiAdmin.init.restore();
|
||||
};
|
||||
|
||||
beforeEach(beforeEach);
|
||||
afterEach(afterEach);
|
||||
|
||||
it("does not setup admin api if httpAdminRoot is false", function(done) {
|
||||
api.init({ httpAdminRoot: false },{},{},{});
|
||||
should.not.exist(api.httpAdmin);
|
||||
done();
|
||||
});
|
||||
describe('initalises admin api without adminAuth', function(done) {
|
||||
before(function() {
|
||||
beforeEach();
|
||||
api.init({},{},{},{});
|
||||
});
|
||||
after(afterEach);
|
||||
it('exposes the editor',function(done) {
|
||||
request(api.httpAdmin).get("/editor").expect(200).end(done);
|
||||
})
|
||||
it('exposes the admin api',function(done) {
|
||||
request(api.httpAdmin).get("/admin").expect(200).end(done);
|
||||
})
|
||||
it('exposes the auth api',function(done) {
|
||||
request(api.httpAdmin).get("/auth/login").expect(200).end(done);
|
||||
})
|
||||
});
|
||||
|
||||
describe('initalises admin api without editor', function(done) {
|
||||
before(function() {
|
||||
beforeEach();
|
||||
api.init({ disableEditor: true },{},{},{});
|
||||
});
|
||||
after(afterEach);
|
||||
it('does not expose the editor',function(done) {
|
||||
request(api.httpAdmin).get("/editor").expect(404).end(done);
|
||||
})
|
||||
it('exposes the admin api',function(done) {
|
||||
request(api.httpAdmin).get("/admin").expect(200).end(done);
|
||||
})
|
||||
it('exposes the auth api',function(done) {
|
||||
request(api.httpAdmin).get("/auth/login").expect(200).end(done)
|
||||
})
|
||||
});
|
||||
|
||||
describe('initialises api with admin middleware', function(done) {
|
||||
it('ignores non-function values',function(done) {
|
||||
api.init({ httpAdminRoot: true, httpAdminMiddleware: undefined },{},{},{});
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware')
|
||||
should(middlewareFound).be.empty();
|
||||
done();
|
||||
});
|
||||
|
||||
it('only accepts functions as middleware',function(done) {
|
||||
const testMiddleware = function(req, res, next){ next(); };
|
||||
api.init({ httpAdminRoot: true, httpAdminMiddleware: testMiddleware },{},{},{});
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware')
|
||||
should(middlewareFound).be.length(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialises api with authentication enabled', function(done) {
|
||||
|
||||
it('enables an oauth/openID based authentication mechanism',function(done) {
|
||||
const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){});
|
||||
const adminAuth = { type: 'strategy', strategy: {} }
|
||||
api.init({ httpAdminRoot: true, adminAuth },{},{},{});
|
||||
should(stub.called).be.ok();
|
||||
stub.restore();
|
||||
done();
|
||||
});
|
||||
|
||||
it('enables password protection',function(done) {
|
||||
const adminAuth = { type: 'credentials' }
|
||||
api.init({ httpAdminRoot: true, adminAuth },{},{},{});
|
||||
|
||||
// is the name ("initialize") of the passport middleware present
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'initialize')
|
||||
should(middlewareFound).be.length(1);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('initialises api with custom cors config', function (done) {
|
||||
const httpAdminCors = {
|
||||
origin: "*",
|
||||
methods: "GET,PUT,POST,DELETE"
|
||||
};
|
||||
|
||||
it('uses default cors middleware when user settings absent', function(done){
|
||||
api.init({ httpAdminRoot: true }, {}, {}, {});
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware')
|
||||
should(middlewareFound).be.length(1);
|
||||
done();
|
||||
})
|
||||
|
||||
it('enables custom cors middleware when settings present', function(done){
|
||||
api.init({ httpAdminRoot: true, httpAdminCors }, {}, {}, {});
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware')
|
||||
should(middlewareFound).be.length(2);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
describe('editor start', function (done) {
|
||||
|
||||
it('cannot be started when editor is disabled', function (done) {
|
||||
const stub = sinon.stub(apiEditor, 'start').callsFake(function () {
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
api.init({ httpAdminRoot: true, disableEditor: true }, {}, {}, {});
|
||||
should(api.start()).resolvedWith(true);
|
||||
stub.restore();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can be started when editor enabled', function (done) {
|
||||
const stub = sinon.stub(apiEditor, 'start');
|
||||
api.init({ httpAdminRoot: true, disableEditor: false }, {}, {}, {});
|
||||
api.start();
|
||||
should(stub.called).be.true();
|
||||
stub.restore();
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,110 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require('supertest');
|
||||
var express = require('express');
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
|
||||
var apiUtil = NR_TEST_UTILS.require("@node-red/editor-api/lib/util");
|
||||
|
||||
var log = NR_TEST_UTILS.require("@node-red/util").log;
|
||||
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
|
||||
|
||||
describe("api/util", function() {
|
||||
describe("errorHandler", function() {
|
||||
var loggedError = null;
|
||||
var loggedEvent = null;
|
||||
var app;
|
||||
before(function() {
|
||||
app = express();
|
||||
sinon.stub(log,'error').callsFake(function(msg) {loggedError = msg;});
|
||||
sinon.stub(log,'audit').callsFake(function(event) {loggedEvent = event;});
|
||||
app.get("/tooLarge", function(req,res) {
|
||||
var err = new Error();
|
||||
err.message = "request entity too large";
|
||||
throw err;
|
||||
},apiUtil.errorHandler)
|
||||
app.get("/stack", function(req,res) {
|
||||
var err = new Error();
|
||||
err.message = "stacktrace";
|
||||
throw err;
|
||||
},apiUtil.errorHandler)
|
||||
});
|
||||
after(function() {
|
||||
log.error.restore();
|
||||
log.audit.restore();
|
||||
})
|
||||
beforeEach(function() {
|
||||
loggedError = null;
|
||||
loggedEvent = null;
|
||||
})
|
||||
it("logs an error for request entity too large", function(done) {
|
||||
request(app).get("/tooLarge").expect(400).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("error","unexpected_error");
|
||||
res.body.should.have.property("message","Error: request entity too large");
|
||||
|
||||
loggedError.should.have.property("message","request entity too large");
|
||||
|
||||
loggedEvent.should.have.property("event","api.error");
|
||||
loggedEvent.should.have.property("error","unexpected_error");
|
||||
loggedEvent.should.have.property("message","Error: request entity too large");
|
||||
done();
|
||||
});
|
||||
})
|
||||
it("logs an error plus stack for other errors", function(done) {
|
||||
request(app).get("/stack").expect(400).end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("error","unexpected_error");
|
||||
res.body.should.have.property("message","Error: stacktrace");
|
||||
|
||||
/Error: stacktrace\s*at.*util_spec.js/m.test(loggedError).should.be.true();
|
||||
|
||||
loggedEvent.should.have.property("event","api.error");
|
||||
loggedEvent.should.have.property("error","unexpected_error");
|
||||
loggedEvent.should.have.property("message","Error: stacktrace");
|
||||
|
||||
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
describe('determineLangFromHeaders', function() {
|
||||
var oldDefaultLang;
|
||||
before(function() {
|
||||
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");
|
||||
})
|
||||
it('returns the first language accepted', function() {
|
||||
apiUtil.determineLangFromHeaders(['fr-FR','en-GB']).should.eql("fr-FR");
|
||||
})
|
||||
})
|
||||
});
|
||||
Reference in New Issue
Block a user