node-red
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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 comms = NR_TEST_UTILS.require("@node-red/runtime/lib/api/comms");
|
||||
var events = NR_TEST_UTILS.require("@node-red/util/lib/events");
|
||||
|
||||
describe("runtime-api/comms", function() {
|
||||
describe("listens for events", function() {
|
||||
var messages = [];
|
||||
var clientConnection = {
|
||||
send: function(topic,data) {
|
||||
messages.push({topic,data})
|
||||
}
|
||||
}
|
||||
var eventHandlers = {};
|
||||
before(function(done) {
|
||||
sinon.stub(events,"removeListener").callsFake(function() {})
|
||||
sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler })
|
||||
comms.init({
|
||||
log: {
|
||||
trace: function(){}
|
||||
}
|
||||
})
|
||||
comms.addConnection({client: clientConnection}).then(done);
|
||||
})
|
||||
after(function(done) {
|
||||
comms.removeConnection({client: clientConnection}).then(done);
|
||||
events.removeListener.restore();
|
||||
events.on.restore();
|
||||
})
|
||||
afterEach(function() {
|
||||
messages = [];
|
||||
})
|
||||
|
||||
it('runtime events',function(){
|
||||
eventHandlers.should.have.property('runtime-event');
|
||||
eventHandlers['runtime-event']({
|
||||
id: "my-event",
|
||||
payload: "my-payload"
|
||||
})
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","notification/my-event");
|
||||
messages[0].should.have.property("data","my-payload")
|
||||
})
|
||||
it('status events',function(){
|
||||
eventHandlers.should.have.property('node-status');
|
||||
eventHandlers['node-status']({
|
||||
id: "my-event",
|
||||
status: {text:"my-status",badProperty:"should be filtered"}
|
||||
})
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","status/my-event");
|
||||
messages[0].should.have.property("data");
|
||||
messages[0].data.should.have.property("text","my-status");
|
||||
messages[0].data.should.not.have.property("badProperty");
|
||||
|
||||
})
|
||||
it('comms events',function(){
|
||||
eventHandlers.should.have.property('runtime-event');
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","my-topic");
|
||||
messages[0].should.have.property("data","my-payload")
|
||||
})
|
||||
});
|
||||
describe("manages connections", function() {
|
||||
var eventHandlers = {};
|
||||
var messages = [];
|
||||
var clientConnection1 = {
|
||||
send: function(topic,data) {
|
||||
messages.push({topic,data})
|
||||
}
|
||||
}
|
||||
var clientConnection2 = {
|
||||
send: function(topic,data) {
|
||||
messages.push({topic,data})
|
||||
}
|
||||
}
|
||||
before(function() {
|
||||
sinon.stub(events,"removeListener").callsFake(function() {})
|
||||
sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler })
|
||||
comms.init({
|
||||
log: {
|
||||
trace: function(){}
|
||||
}
|
||||
})
|
||||
})
|
||||
after(function() {
|
||||
events.removeListener.restore();
|
||||
events.on.restore();
|
||||
})
|
||||
afterEach(function(done) {
|
||||
comms.removeConnection({client: clientConnection1}).then(function() {
|
||||
comms.removeConnection({client: clientConnection2}).then(done);
|
||||
});
|
||||
messages = [];
|
||||
})
|
||||
it('adds new connections',function(done){
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection1}).then(function() {
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(1);
|
||||
comms.addConnection({client: clientConnection2}).then(function() {
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(3);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
it('removes connections',function(done){
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection1}).then(function() {
|
||||
comms.addConnection({client: clientConnection2}).then(function() {
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(2);
|
||||
comms.removeConnection({client: clientConnection1}).then(function() {
|
||||
eventHandlers['comms']({
|
||||
topic: "my-topic",
|
||||
data: "my-payload"
|
||||
})
|
||||
messages.should.have.length(3);
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe("subscriptions", function() {
|
||||
var messages = [];
|
||||
var clientConnection = {
|
||||
send: function(topic,data) {
|
||||
messages.push({topic,data})
|
||||
}
|
||||
}
|
||||
var clientConnection2 = {
|
||||
send: function(topic,data) {
|
||||
messages.push({topic,data})
|
||||
}
|
||||
}
|
||||
var eventHandlers = {};
|
||||
before(function() {
|
||||
sinon.stub(events,"removeListener").callsFake(function() {})
|
||||
sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler })
|
||||
comms.init({
|
||||
log: {
|
||||
trace: function(){}
|
||||
}
|
||||
})
|
||||
})
|
||||
after(function() {
|
||||
events.removeListener.restore();
|
||||
events.on.restore();
|
||||
})
|
||||
afterEach(function(done) {
|
||||
messages = [];
|
||||
comms.removeConnection({client: clientConnection}).then(done);
|
||||
})
|
||||
|
||||
it('subscribe triggers retained messages',function(done){
|
||||
eventHandlers['comms']({
|
||||
topic: "my-event",
|
||||
data: "my-payload",
|
||||
retain: true
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","my-event");
|
||||
messages[0].should.have.property("data","my-payload");
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
it('retains non-blank status message',function(done){
|
||||
eventHandlers['node-status']({
|
||||
id: "node1234",
|
||||
status: {text:"hello"}
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","status/node1234");
|
||||
messages[0].should.have.property("data",{text:"hello", fill: undefined, shape: undefined});
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
it('does not retain blank status message',function(done){
|
||||
eventHandlers['node-status']({
|
||||
id: "node1234",
|
||||
status: {}
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
|
||||
messages.should.have.length(0);
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
it('does not send blank status if first status',function(done){
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
|
||||
eventHandlers['node-status']({
|
||||
id: "node5678",
|
||||
status: {}
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
done()
|
||||
})
|
||||
}).catch(done);
|
||||
});
|
||||
it('sends blank status if replacing retained',function(done){
|
||||
eventHandlers['node-status']({
|
||||
id: "node5678",
|
||||
status: {text:"hello"}
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
|
||||
messages.should.have.length(1);
|
||||
eventHandlers['node-status']({
|
||||
id: "node5678",
|
||||
status: {}
|
||||
})
|
||||
messages.should.have.length(2);
|
||||
done()
|
||||
})
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('does not retain initial status blank message',function(done){
|
||||
eventHandlers['node-status']({
|
||||
id: "my-event",
|
||||
status: {}
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","my-event");
|
||||
messages[0].should.have.property("data","my-payload");
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
it('retained messages get cleared',function(done) {
|
||||
eventHandlers['comms']({
|
||||
topic: "my-event",
|
||||
data: "my-payload",
|
||||
retain: true
|
||||
})
|
||||
messages.should.have.length(0);
|
||||
comms.addConnection({client: clientConnection}).then(function() {
|
||||
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
|
||||
messages.should.have.length(1);
|
||||
messages[0].should.have.property("topic","my-event");
|
||||
messages[0].should.have.property("data","my-payload");
|
||||
// Now we have a retained message, clear it
|
||||
eventHandlers['comms']({
|
||||
topic: "my-event",
|
||||
data: "my-payload-cleared"
|
||||
});
|
||||
messages.should.have.length(2);
|
||||
messages[1].should.have.property("topic","my-event");
|
||||
messages[1].should.have.property("data","my-payload-cleared");
|
||||
// Now add a second client and subscribe - no message should arrive
|
||||
return comms.addConnection({client: clientConnection2}).then(function() {
|
||||
return comms.subscribe({client: clientConnection2, topic: "my-event"}).then(function() {
|
||||
messages.should.have.length(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
@@ -0,0 +1,353 @@
|
||||
/**
|
||||
* 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 context = NR_TEST_UTILS.require("@node-red/runtime/lib/api/context");
|
||||
|
||||
var mockLog = () => ({
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc";}
|
||||
});
|
||||
|
||||
var mockContext = function(contents) {
|
||||
return {
|
||||
get: function(key,store,callback) {
|
||||
if (contents.hasOwnProperty(store) && contents[store].hasOwnProperty(key)) {
|
||||
callback(null,contents[store][key]);
|
||||
} else {
|
||||
callback(null,undefined);
|
||||
}
|
||||
},
|
||||
set: function (key, value, store, callback) {
|
||||
if (contents.hasOwnProperty(store)) {
|
||||
if (!value) {
|
||||
delete contents[store][key];
|
||||
callback(null);
|
||||
}
|
||||
} else {
|
||||
callback("err store");
|
||||
}
|
||||
},
|
||||
keys: function (store, callback) {
|
||||
if (contents.hasOwnProperty(store)) {
|
||||
callback(null, Object.keys(contents[store]));
|
||||
} else {
|
||||
callback("err store");
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
describe("runtime-api/context", function() {
|
||||
var globalContext, flowContext, nodeContext, contexts;
|
||||
|
||||
beforeEach(function() {
|
||||
globalContext = { default: { abc: 111 }, file: { abc: 222 } };
|
||||
flowContext = { default: { abc: 333 }, file: { abc: 444 } };
|
||||
nodeContext = { default: { abc: 555 }, file: { abc: 666 } };
|
||||
contexts = {
|
||||
global: mockContext(globalContext),
|
||||
flow1: mockContext(flowContext)
|
||||
};
|
||||
context.init({
|
||||
nodes: {
|
||||
listContextStores: function() {
|
||||
return { default: 'default', stores: [ 'default', 'file' ] };
|
||||
},
|
||||
getContext: function(id) {
|
||||
return contexts[id];
|
||||
},
|
||||
getNode: function(id) {
|
||||
if (id === 'known') {
|
||||
return {
|
||||
context: function() { return mockContext(nodeContext); }
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
functionGlobalContext: {
|
||||
fgc:1234
|
||||
}
|
||||
},
|
||||
log: mockLog()
|
||||
});
|
||||
});
|
||||
|
||||
describe("getValue", function() {
|
||||
it('gets global value of default store', function() {
|
||||
return context.getValue({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','111');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('gets global value of specified store', function() {
|
||||
return context.getValue({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','222');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('gets flow value of default store', function() {
|
||||
return context.getValue({
|
||||
scope: 'flow',
|
||||
id: 'flow1',
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','333');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('gets flow value of specified store', function() {
|
||||
return context.getValue({
|
||||
scope: 'flow',
|
||||
id: 'flow1',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','444');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('gets node value of default store', function() {
|
||||
return context.getValue({
|
||||
scope: 'node',
|
||||
id: 'known',
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','555');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('gets node value of specified store', function() {
|
||||
return context.getValue({
|
||||
scope: 'node',
|
||||
id: 'known',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.have.property('msg','666');
|
||||
result.should.have.property('format','number');
|
||||
});
|
||||
});
|
||||
|
||||
it('404s for unknown store', function(done) {
|
||||
context.getValue({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: 'unknown',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
done("getValue for unknown store should not resolve");
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','not_found');
|
||||
err.should.have.property('status',404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('gets all global value properties', function() {
|
||||
return context.getValue({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: undefined, // use default
|
||||
key: undefined, //
|
||||
}).then(function(result) {
|
||||
result.should.eql({
|
||||
default: { abc: { msg: '111', format: 'number' } },
|
||||
file: { abc: { msg: '222', format: 'number' } }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets all flow value properties', function() {
|
||||
return context.getValue({
|
||||
scope: 'flow',
|
||||
id: 'flow1',
|
||||
store: undefined, // use default
|
||||
key: undefined, //
|
||||
}).then(function(result) {
|
||||
result.should.eql({
|
||||
default: { abc: { msg: '333', format: 'number' } },
|
||||
file: { abc: { msg: '444', format: 'number' } }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets all node value properties', function() {
|
||||
return context.getValue({
|
||||
scope: 'node',
|
||||
id: 'known',
|
||||
store: undefined, // use default
|
||||
key: undefined, //
|
||||
}).then(function(result) {
|
||||
result.should.eql({
|
||||
default: { abc: { msg: '555', format: 'number' } },
|
||||
file: { abc: { msg: '666', format: 'number' } }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets empty object when specified context doesn\'t exist', function() {
|
||||
return context.getValue({
|
||||
scope: 'node',
|
||||
id: 'non-existent',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
result.should.be.an.Object();
|
||||
result.should.be.empty();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete", function () {
|
||||
it('deletes global value of default store', function () {
|
||||
return context.delete({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
globalContext.should.eql({
|
||||
default: {}, file: { abc: 222 }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes global value of specified store', function () {
|
||||
return context.delete({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
globalContext.should.eql({
|
||||
default: { abc: 111 }, file: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes flow value of default store', function () {
|
||||
return context.delete({
|
||||
scope: 'flow',
|
||||
id: 'flow1',
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
flowContext.should.eql({
|
||||
default: {}, file: { abc: 444 }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes flow value of specified store', function () {
|
||||
return context.delete({
|
||||
scope: 'flow',
|
||||
id: 'flow1',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
flowContext.should.eql({
|
||||
default: { abc: 333 }, file: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes node value of default store', function () {
|
||||
return context.delete({
|
||||
scope: 'node',
|
||||
id: 'known',
|
||||
store: undefined, // use default
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
nodeContext.should.eql({
|
||||
default: {}, file: { abc: 666 }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes node value of specified store', function () {
|
||||
return context.delete({
|
||||
scope: 'node',
|
||||
id: 'known',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
nodeContext.should.eql({
|
||||
default: { abc: 555 }, file: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does nothing when specified context doesn\'t exist', function() {
|
||||
return context.delete({
|
||||
scope: 'node',
|
||||
id: 'non-existent',
|
||||
store: 'file',
|
||||
key: 'abc'
|
||||
}).then(function(result) {
|
||||
should.not.exist(result);
|
||||
nodeContext.should.eql({
|
||||
default: { abc: 555 }, file: { abc: 666 }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('404s for unknown store', function (done) {
|
||||
context.delete({
|
||||
scope: 'global',
|
||||
id: undefined,
|
||||
store: 'unknown',
|
||||
key: 'abc'
|
||||
}).then(function () {
|
||||
done("delete for unknown store should not resolve");
|
||||
}).catch(function (err) {
|
||||
err.should.have.property('code', 'not_found');
|
||||
err.should.have.property('status', 404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
var diagnostics = NR_TEST_UTILS.require("@node-red/runtime/lib/api/diagnostics")
|
||||
|
||||
var mockLog = () => ({
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc"}
|
||||
})
|
||||
|
||||
describe("runtime-api/diagnostics", function() {
|
||||
|
||||
describe("get", function() {
|
||||
before(function() {
|
||||
diagnostics.init({
|
||||
isStarted: () => true,
|
||||
nodes: {
|
||||
getNodeList: () => [{module:"node-red", version:"9.9.9"},{module:"node-red-node-inject", version:"8.8.8"}]
|
||||
},
|
||||
settings: {
|
||||
version: "7.7.7",
|
||||
available: () => true,
|
||||
//apiMaxLength: xxx, deliberately left blank. Should arrive in report as "UNSET"
|
||||
debugMaxLength: 1111,
|
||||
disableEditor: false,
|
||||
flowFile: "flows.json",
|
||||
mqttReconnectTime: 321,
|
||||
serialReconnectTime: 432,
|
||||
socketReconnectTime: 2222,
|
||||
socketTimeout: 3333,
|
||||
tcpMsgQueueSize: 4444,
|
||||
inboundWebSocketTimeout: 5555,
|
||||
runtimeState: {enabled: true, ui: false},
|
||||
adminAuth: {},//should be sanitised to "SET"
|
||||
httpAdminRoot: "/admin/root/",
|
||||
httpAdminCors: {},//should be sanitised to "SET"
|
||||
httpNodeAuth: {},//should be sanitised to "SET"
|
||||
httpNodeRoot: "/node/root/",
|
||||
httpNodeCors: {},//should be sanitised to "SET"
|
||||
httpStatic: "/var/static/",//should be sanitised to "SET"
|
||||
httpStaticRoot: "/static/root/",
|
||||
httpStaticCors: {},//should be sanitised to "SET"
|
||||
uiHost: "something.secret.com",//should be sanitised to "SET"
|
||||
uiPort: 1337,//should be sanitised to "SET"
|
||||
userDir: "/var/super/secret/",//should be sanitised to "SET",
|
||||
nodesDir: "/var/super/secret/",//should be sanitised to "SET",
|
||||
contextStorage: {
|
||||
default : { module: "memory" },
|
||||
file: { module: "localfilesystem" },
|
||||
secured: { module: "secure_store", user: "fred", pass: "super-duper-secret" },
|
||||
},
|
||||
editorTheme: {}
|
||||
},
|
||||
log: mockLog()
|
||||
});
|
||||
})
|
||||
it("returns basic user settings", function() {
|
||||
return diagnostics.get({scope:"fake_scope"}).then(result => {
|
||||
should(result).be.type("object");
|
||||
|
||||
//result.xxxxx
|
||||
Object.keys(result)
|
||||
const reportPropCount = Object.keys(result).length;
|
||||
reportPropCount.should.eql(7);//ensure no more than 7 keys are present in the report (avoid leakage of extra info)
|
||||
result.should.have.property("report","diagnostics");
|
||||
result.should.have.property("scope","fake_scope");
|
||||
result.should.have.property("time").type("object");
|
||||
result.should.have.property("intl").type("object");
|
||||
result.should.have.property("nodejs").type("object");
|
||||
result.should.have.property("os").type("object");
|
||||
result.should.have.property("runtime").type("object");
|
||||
|
||||
//result.runtime.xxxxx
|
||||
const runtimeCount = Object.keys(result.runtime).length;
|
||||
runtimeCount.should.eql(5);//ensure 5 keys are present in runtime
|
||||
result.runtime.should.have.property('isStarted',true)
|
||||
result.runtime.should.have.property('flows')
|
||||
result.runtime.should.have.property('modules').type("object");
|
||||
result.runtime.should.have.property('settings').type("object");
|
||||
result.runtime.should.have.property('version','7.7.7');
|
||||
|
||||
//result.runtime.modules.xxxxx
|
||||
const moduleCount = Object.keys(result.runtime.modules).length;
|
||||
moduleCount.should.eql(2);//ensure no more than the 2 modules specified are present
|
||||
result.runtime.modules.should.have.property('node-red','9.9.9');
|
||||
result.runtime.modules.should.have.property('node-red-node-inject','8.8.8');
|
||||
|
||||
//result.runtime.settings.xxxxx
|
||||
const settingsCount = Object.keys(result.runtime.settings).length;
|
||||
settingsCount.should.eql(27);//ensure no more than the 21 settings listed below are present in the settings object
|
||||
result.runtime.settings.should.have.property('available',true);
|
||||
result.runtime.settings.should.have.property('apiMaxLength', "UNSET");//deliberately disabled to ensure UNSET is returned
|
||||
result.runtime.settings.should.have.property('debugMaxLength', 1111);
|
||||
result.runtime.settings.should.have.property('disableEditor', false);
|
||||
result.runtime.settings.should.have.property('editorTheme', {});
|
||||
result.runtime.settings.should.have.property('flowFile', "flows.json");
|
||||
result.runtime.settings.should.have.property('mqttReconnectTime', 321);
|
||||
result.runtime.settings.should.have.property('serialReconnectTime', 432);
|
||||
result.runtime.settings.should.have.property('socketReconnectTime', 2222);
|
||||
result.runtime.settings.should.have.property('socketTimeout', 3333);
|
||||
result.runtime.settings.should.have.property('tcpMsgQueueSize', 4444);
|
||||
result.runtime.settings.should.have.property('inboundWebSocketTimeout', 5555);
|
||||
result.runtime.settings.should.have.property('runtimeState', {enabled: true, ui: false});
|
||||
result.runtime.settings.should.have.property("adminAuth", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("httpAdminCors", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('httpAdminRoot', "/admin/root/");
|
||||
result.runtime.settings.should.have.property("httpNodeAuth", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("httpNodeCors", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('httpNodeRoot', "/node/root/");
|
||||
result.runtime.settings.should.have.property("httpStatic", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('httpStaticRoot', "/static/root/");
|
||||
result.runtime.settings.should.have.property("httpStaticCors", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("uiHost", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("uiPort", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("userDir", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('contextStorage').type("object");
|
||||
result.runtime.settings.should.have.property('nodesDir', "SET")
|
||||
|
||||
//result.runtime.settings.contextStorage.xxxxx
|
||||
const contextCount = Object.keys(result.runtime.settings.contextStorage).length;
|
||||
contextCount.should.eql(3);//ensure no more than the 3 settings listed below are present in the contextStorage object
|
||||
result.runtime.settings.contextStorage.should.have.property('default', {module:"memory"});
|
||||
result.runtime.settings.contextStorage.should.have.property('file', {module:"localfilesystem"});
|
||||
result.runtime.settings.contextStorage.should.have.property('secured', {module:"secure_store"}); //only module should be present, other fields are dropped for security
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* 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 flows = NR_TEST_UTILS.require("@node-red/runtime/lib/api/flows")
|
||||
|
||||
var mockLog = () => ({
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc"}
|
||||
})
|
||||
|
||||
describe("runtime-api/flows", function() {
|
||||
describe("getFlows", function() {
|
||||
it("returns the current flow configuration", function(done) {
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
getFlows: function() { return [1,2,3] }
|
||||
}
|
||||
});
|
||||
flows.getFlows({}).then(function(result) {
|
||||
result.should.eql([1,2,3]);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setFlows", function() {
|
||||
var setFlows;
|
||||
var loadFlows;
|
||||
var reloadError = false;
|
||||
beforeEach(function() {
|
||||
setFlows = sinon.spy(function(flows,credentials,type) {
|
||||
if (flows[0] === "error") {
|
||||
var err = new Error("error");
|
||||
err.code = "error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve("newRev");
|
||||
});
|
||||
loadFlows = sinon.spy(function() {
|
||||
if (!reloadError) {
|
||||
return Promise.resolve("newLoadRev");
|
||||
} else {
|
||||
var err = new Error("error");
|
||||
err.code = "error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
})
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
getFlows: function() { return {rev:"currentRev",flows:[]} },
|
||||
setFlows: setFlows,
|
||||
loadFlows: loadFlows
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
it("defaults to full deploy", function(done) {
|
||||
flows.setFlows({
|
||||
flows: {flows:[4,5,6]}
|
||||
}).then(function(result) {
|
||||
result.should.eql({rev:"newRev"});
|
||||
setFlows.called.should.be.true();
|
||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||
setFlows.lastCall.args[2].should.eql("full");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("includes credentials when part of the request", function(done) {
|
||||
flows.setFlows({
|
||||
flows: {flows:[4,5,6], credentials: {$:"creds"}},
|
||||
}).then(function(result) {
|
||||
result.should.eql({rev:"newRev"});
|
||||
setFlows.called.should.be.true();
|
||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||
setFlows.lastCall.args[1].should.eql({$:"creds"});
|
||||
setFlows.lastCall.args[2].should.eql("full");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("passes through other deploy types", function(done) {
|
||||
flows.setFlows({
|
||||
deploymentType: "nodes",
|
||||
flows: {flows:[4,5,6]}
|
||||
}).then(function(result) {
|
||||
result.should.eql({rev:"newRev"});
|
||||
setFlows.called.should.be.true();
|
||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||
setFlows.lastCall.args[2].should.eql("nodes");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("triggers a flow reload", function(done) {
|
||||
flows.setFlows({
|
||||
deploymentType: "reload"
|
||||
}).then(function(result) {
|
||||
result.should.eql({rev:"newLoadRev"});
|
||||
setFlows.called.should.be.false();
|
||||
loadFlows.called.should.be.true();
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("allows update when revision matches", function(done) {
|
||||
flows.setFlows({
|
||||
deploymentType: "nodes",
|
||||
flows: {flows:[4,5,6],rev:"currentRev"}
|
||||
}).then(function(result) {
|
||||
result.should.eql({rev:"newRev"});
|
||||
setFlows.called.should.be.true();
|
||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||
setFlows.lastCall.args[2].should.eql("nodes");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects update when revision does not match", function(done) {
|
||||
flows.setFlows({
|
||||
deploymentType: "nodes",
|
||||
flows: {flows:[4,5,6],rev:"notTheCurrentRev"}
|
||||
}).then(function(result) {
|
||||
done(new Error("Did not reject rev mismatch"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','version_mismatch');
|
||||
err.should.have.property('status',409);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when reload fails",function(done) {
|
||||
reloadError = true;
|
||||
flows.setFlows({
|
||||
deploymentType: "reload"
|
||||
}).then(function(result) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','error');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when update fails",function(done) {
|
||||
flows.setFlows({
|
||||
deploymentType: "full",
|
||||
flows: {flows:["error",5,6]}
|
||||
}).then(function(result) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','error');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addFlow", function() {
|
||||
var addFlow;
|
||||
beforeEach(function() {
|
||||
addFlow = sinon.spy(function(flow) {
|
||||
if (flow === "error") {
|
||||
var err = new Error("error");
|
||||
err.code = "error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve("newId");
|
||||
});
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
addFlow: addFlow
|
||||
}
|
||||
});
|
||||
})
|
||||
it("adds a flow", function(done) {
|
||||
flows.addFlow({flow:{a:"123"}}).then(function(id) {
|
||||
addFlow.called.should.be.true();
|
||||
addFlow.lastCall.args[0].should.eql({a:"123"});
|
||||
id.should.eql("newId");
|
||||
done()
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when add fails", function(done) {
|
||||
flows.addFlow({flow:"error"}).then(function(id) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','error');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
describe("getFlow", function() {
|
||||
var getFlow;
|
||||
beforeEach(function() {
|
||||
getFlow = sinon.spy(function(flow) {
|
||||
if (flow === "unknown") {
|
||||
return null;
|
||||
}
|
||||
return [1,2,3];
|
||||
});
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
getFlow: getFlow
|
||||
}
|
||||
});
|
||||
})
|
||||
it("gets a flow", function(done) {
|
||||
flows.getFlow({id:"123"}).then(function(flow) {
|
||||
flow.should.eql([1,2,3]);
|
||||
done()
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when flow not found", function(done) {
|
||||
flows.getFlow({id:"unknown"}).then(function(flow) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','not_found');
|
||||
err.should.have.property('status',404);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateFlow", function() {
|
||||
var updateFlow;
|
||||
beforeEach(function() {
|
||||
updateFlow = sinon.spy(function(id,flow) {
|
||||
if (id === "unknown") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = 404;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (id === "error") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = "error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
updateFlow: updateFlow
|
||||
}
|
||||
});
|
||||
})
|
||||
it("updates a flow", function(done) {
|
||||
flows.updateFlow({id:"123",flow:[1,2,3]}).then(function(id) {
|
||||
id.should.eql("123");
|
||||
updateFlow.called.should.be.true();
|
||||
updateFlow.lastCall.args[0].should.eql("123");
|
||||
updateFlow.lastCall.args[1].should.eql([1,2,3]);
|
||||
done()
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when flow not found", function(done) {
|
||||
flows.updateFlow({id:"unknown"}).then(function(flow) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','not_found');
|
||||
err.should.have.property('status',404);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when update fails", function(done) {
|
||||
flows.updateFlow({id:"error"}).then(function(flow) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','error');
|
||||
err.should.have.property('status',400);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("deleteFlow", function() {
|
||||
var removeFlow;
|
||||
beforeEach(function() {
|
||||
removeFlow = sinon.spy(function(flow) {
|
||||
if (flow === "unknown") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = 404;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (flow === "error") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = "error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
flows: {
|
||||
removeFlow: removeFlow
|
||||
}
|
||||
});
|
||||
})
|
||||
it("deletes a flow", function(done) {
|
||||
flows.deleteFlow({id:"123"}).then(function() {
|
||||
removeFlow.called.should.be.true();
|
||||
removeFlow.lastCall.args[0].should.eql("123");
|
||||
done()
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when flow not found", function(done) {
|
||||
flows.deleteFlow({id:"unknown"}).then(function(flow) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','not_found');
|
||||
err.should.have.property('status',404);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("rejects when delete fails", function(done) {
|
||||
flows.deleteFlow({id:"error"}).then(function(flow) {
|
||||
done(new Error("Did not return internal error"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property('code','error');
|
||||
err.should.have.property('status',400);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getNodeCredentials", function() {
|
||||
beforeEach(function() {
|
||||
flows.init({
|
||||
log: mockLog(),
|
||||
nodes: {
|
||||
getCredentials: function(id) {
|
||||
if (id === "unknown") {
|
||||
return undefined;
|
||||
} else if (id === "known") {
|
||||
return {
|
||||
username: "abc",
|
||||
password: "123"
|
||||
}
|
||||
} else if (id === "known2") {
|
||||
return {
|
||||
username: "abc",
|
||||
password: ""
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
getCredentialDefinition: function(type) {
|
||||
if (type === "node") {
|
||||
return {
|
||||
username: {type:"text"},
|
||||
password: {type:"password"}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
it("returns an empty object for an unknown node", function(done) {
|
||||
flows.getNodeCredentials({id:"unknown", type:"node"}).then(function(result) {
|
||||
result.should.eql({});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("gets the filtered credentials for a known node with password", function(done) {
|
||||
flows.getNodeCredentials({id:"known", type:"node"}).then(function(result) {
|
||||
result.should.eql({
|
||||
username: "abc",
|
||||
has_password: true
|
||||
});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("gets the filtered credentials for a known node without password", function(done) {
|
||||
flows.getNodeCredentials({id:"known2", type:"node"}).then(function(result) {
|
||||
result.should.eql({
|
||||
username: "abc",
|
||||
has_password: false
|
||||
});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it("gets the empty credentials for a known node without a registered definition", function(done) {
|
||||
flows.getNodeCredentials({id:"known2", type:"unknown-type"}).then(function(result) {
|
||||
result.should.eql({});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("flow run state", function() {
|
||||
var startFlows, stopFlows, runtime;
|
||||
beforeEach(function() {
|
||||
let flowsStarted = true;
|
||||
let flowsState = "start";
|
||||
startFlows = sinon.spy(function(type) {
|
||||
if (type !== "full") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = 400;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
flowsStarted = true;
|
||||
flowsState = "start";
|
||||
return Promise.resolve();
|
||||
});
|
||||
stopFlows = sinon.spy(function(type) {
|
||||
if (type !== "full") {
|
||||
var err = new Error();
|
||||
// TODO: quirk of internal api - uses .code for .status
|
||||
err.code = 400;
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
flowsStarted = false;
|
||||
flowsState = "stop";
|
||||
return Promise.resolve();
|
||||
});
|
||||
runtime = {
|
||||
log: mockLog(),
|
||||
settings: {
|
||||
runtimeState: {
|
||||
enabled: true,
|
||||
ui: true,
|
||||
},
|
||||
},
|
||||
flows: {
|
||||
get started() {
|
||||
return flowsStarted;
|
||||
},
|
||||
startFlows,
|
||||
stopFlows,
|
||||
getFlows: function() { return {rev:"currentRev",flows:[]} },
|
||||
state: function() { return flowsState}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("gets flows run state", async function() {
|
||||
flows.init(runtime);
|
||||
const state = await flows.getState({})
|
||||
state.should.have.property("state", "start")
|
||||
});
|
||||
it("permits getting flows run state when setting disabled", async function() {
|
||||
runtime.settings.runtimeState.enabled = false;
|
||||
flows.init(runtime);
|
||||
const state = await flows.getState({})
|
||||
state.should.have.property("state", "start")
|
||||
});
|
||||
it("start flows", async function() {
|
||||
flows.init(runtime);
|
||||
const state = await flows.setState({state:"start"})
|
||||
state.should.have.property("state", "start")
|
||||
stopFlows.called.should.not.be.true();
|
||||
startFlows.called.should.be.true();
|
||||
});
|
||||
it("stop flows", async function() {
|
||||
flows.init(runtime);
|
||||
const state = await flows.setState({state:"stop"})
|
||||
state.should.have.property("state", "stop")
|
||||
stopFlows.called.should.be.true();
|
||||
startFlows.called.should.not.be.true();
|
||||
});
|
||||
it("rejects starting flows when setting disabled", async function() {
|
||||
let err;
|
||||
runtime.settings.runtimeState.enabled = false;
|
||||
flows.init(runtime);
|
||||
try {
|
||||
await flows.setState({state:"start"})
|
||||
} catch (error) {
|
||||
err = error
|
||||
}
|
||||
stopFlows.called.should.not.be.true();
|
||||
startFlows.called.should.not.be.true();
|
||||
should(err).have.property("code", "not_allowed")
|
||||
should(err).have.property("status", 405)
|
||||
});
|
||||
it("rejects stopping flows when setting disabled", async function() {
|
||||
let err;
|
||||
runtime.settings.runtimeState.enabled = false;
|
||||
flows.init(runtime);
|
||||
try {
|
||||
await flows.setState({state:"stop"})
|
||||
} catch (error) {
|
||||
err = error
|
||||
}
|
||||
stopFlows.called.should.not.be.true();
|
||||
startFlows.called.should.not.be.true();
|
||||
should(err).have.property("code", "not_allowed")
|
||||
should(err).have.property("status", 405)
|
||||
});
|
||||
it("rejects setting invalid flows run state", async function() {
|
||||
let err;
|
||||
flows.init(runtime);
|
||||
try {
|
||||
await flows.setState({state:"bad-state"})
|
||||
} catch (error) {
|
||||
err = error
|
||||
}
|
||||
stopFlows.called.should.not.be.true();
|
||||
startFlows.called.should.not.be.true();
|
||||
should(err).have.property("code", "invalid_run_state")
|
||||
should(err).have.property("status", 400)
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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 index = NR_TEST_UTILS.require("@node-red/runtime/lib/api/index");
|
||||
|
||||
|
||||
describe("runtime-api/index", function() {
|
||||
before(function() {
|
||||
["comms","flows","nodes","settings","library","projects"].forEach(n => {
|
||||
sinon.stub(NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`),"init").callsFake(()=>{});
|
||||
})
|
||||
});
|
||||
after(function() {
|
||||
["comms","flows","nodes","settings","library","projects"].forEach(n => {
|
||||
NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`).init.restore()
|
||||
})
|
||||
})
|
||||
it('isStarted', function(done) {
|
||||
index.init({
|
||||
isStarted: ()=>true
|
||||
});
|
||||
index.isStarted({}).then(function(started) {
|
||||
started.should.be.true();
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
it('isStarted', function(done) {
|
||||
index.init({
|
||||
version: ()=>"1.2.3.4"
|
||||
});
|
||||
index.version({}).then(function(version) {
|
||||
version.should.eql("1.2.3.4");
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
});
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 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 library = NR_TEST_UTILS.require("@node-red/runtime/lib/api/library")
|
||||
|
||||
var mockLog = {
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc"}
|
||||
}
|
||||
|
||||
describe("runtime-api/library", function() {
|
||||
describe("getEntry", function() {
|
||||
before(function() {
|
||||
library.init({
|
||||
log: mockLog,
|
||||
library: {
|
||||
getEntry: function(library, type,path) {
|
||||
if (type === "known") {
|
||||
return Promise.resolve("known");
|
||||
} else if (type === "forbidden") {
|
||||
var err = new Error("forbidden");
|
||||
err.code = "forbidden";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (type === "not_found") {
|
||||
var err = new Error("forbidden");
|
||||
err.code = "not_found";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (type === "error") {
|
||||
var err = new Error("error");
|
||||
err.code = "unknown_error";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (type === "blank") {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
it("returns a known entry", function(done) {
|
||||
library.getEntry({library: "local",type: "known", path: "/abc"}).then(function(result) {
|
||||
result.should.eql("known")
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects a forbidden entry", function(done) {
|
||||
library.getEntry({library: "local",type: "forbidden", path: "/abc"}).then(function(result) {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("code","forbidden");
|
||||
err.should.have.property("status",403);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects an unknown entry", function(done) {
|
||||
library.getEntry({library: "local",type: "not_found", path: "/abc"}).then(function(result) {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("code","not_found");
|
||||
err.should.have.property("status",404);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects a blank (unknown) entry", function(done) {
|
||||
library.getEntry({library: "local",type: "blank", path: "/abc"}).then(function(result) {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("code","not_found");
|
||||
err.should.have.property("status",404);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects unexpected error", function(done) {
|
||||
library.getEntry({library: "local",type: "error", path: "/abc"}).then(function(result) {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("status",400);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
describe("saveEntry", function() {
|
||||
var opts;
|
||||
before(function() {
|
||||
library.init({
|
||||
log: mockLog,
|
||||
library: {
|
||||
saveEntry: function(library,type,path,meta,body) {
|
||||
opts = {type,path,meta,body};
|
||||
if (type === "known") {
|
||||
return Promise.resolve();
|
||||
} else if (type === "forbidden") {
|
||||
var err = new Error("forbidden");
|
||||
err.code = "forbidden";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (type === "not_found") {
|
||||
var err = new Error("forbidden");
|
||||
err.code = "not_found";
|
||||
var p = Promise.reject(err);
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("saves an entry", function(done) {
|
||||
library.saveEntry({library: "local",type: "known", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||
opts.should.have.property("type","known");
|
||||
opts.should.have.property("path","/abc");
|
||||
opts.should.have.property("meta",{a:1});
|
||||
opts.should.have.property("body","123");
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects a forbidden entry", function(done) {
|
||||
library.saveEntry({library: "local",type: "forbidden", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("code","forbidden");
|
||||
err.should.have.property("status",403);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
it("rejects an unknown entry", function(done) {
|
||||
library.saveEntry({library: "local",type: "not_found", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||
done(new Error("did not reject"));
|
||||
}).catch(function(err) {
|
||||
err.should.have.property("status",400);
|
||||
done();
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
const should = require("should");
|
||||
const sinon = require("sinon");
|
||||
|
||||
const NR_TEST_UTILS = require("nr-test-utils");
|
||||
const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/api/plugins")
|
||||
|
||||
const mockLog = () => ({
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc"}
|
||||
})
|
||||
|
||||
describe("runtime-api/plugins", function() {
|
||||
const pluginList = [{id:"one",module:'test-module'},{id:"two",module:"node-red"}];
|
||||
const pluginConfigs = "123";
|
||||
|
||||
describe("getPluginList", function() {
|
||||
it("gets the plugin list", function() {
|
||||
plugins.init({
|
||||
log: mockLog(),
|
||||
plugins: {
|
||||
getPluginList: function() { return pluginList}
|
||||
}
|
||||
});
|
||||
return plugins.getPluginList({}).then(function(result) {
|
||||
result.should.eql(pluginList);
|
||||
})
|
||||
});
|
||||
});
|
||||
describe("getPluginConfigs", function() {
|
||||
it("gets the plugin configs", function() {
|
||||
plugins.init({
|
||||
log: mockLog(),
|
||||
plugins: {
|
||||
getPluginConfigs: function() { return pluginConfigs}
|
||||
}
|
||||
});
|
||||
return plugins.getPluginConfigs({}).then(function(result) {
|
||||
result.should.eql(pluginConfigs);
|
||||
})
|
||||
});
|
||||
});
|
||||
describe("getPluginCatalogs", function() {
|
||||
it("gets the plugin catalogs", function() {
|
||||
plugins.init({
|
||||
log: mockLog(),
|
||||
plugins: {
|
||||
getPluginList: function() { return pluginList}
|
||||
},
|
||||
i18n: {
|
||||
i: {
|
||||
changeLanguage: function(lang,done) { done && done() },
|
||||
getResourceBundle: function(lang, id) { return {lang,id}}
|
||||
}
|
||||
}
|
||||
});
|
||||
return plugins.getPluginCatalogs({lang: "en-US"}).then(function(result) {
|
||||
JSON.stringify(result).should.eql(JSON.stringify({ one: { lang: "en-US", id: "one" } }))
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,994 @@
|
||||
/**
|
||||
* 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 clone = require("clone");
|
||||
|
||||
var NR_TEST_UTILS = require("nr-test-utils");
|
||||
var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/api/settings")
|
||||
|
||||
var mockLog = () => ({
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
trace: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
info: sinon.stub(),
|
||||
metric: sinon.stub(),
|
||||
audit: sinon.stub(),
|
||||
_: function() { return "abc"}
|
||||
})
|
||||
|
||||
describe("runtime-api/settings", function() {
|
||||
describe("getRuntimeSettings", function() {
|
||||
it("gets the runtime settings", function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => ["lib1"] },
|
||||
storage: {},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({}).then(result => {
|
||||
result.should.have.property("httpNodeRoot","testHttpNodeRoot");
|
||||
result.should.have.property("version","testVersion");
|
||||
result.should.have.property("paletteCategories",["red","blue","green"]);
|
||||
result.should.have.property("libraries",["lib1"]);
|
||||
result.should.have.property("testNodeSetting","helloWorld");
|
||||
result.should.have.property("testPluginSettings","helloPluginWorld");
|
||||
result.should.not.have.property("foo",123);
|
||||
result.should.have.property("flowEncryptionType","test-key-type");
|
||||
result.should.not.have.property("user");
|
||||
result.should.have.property("externalModules");
|
||||
result.externalModules.should.eql({palette:{allowInstall:false, allowUpload: false}});
|
||||
|
||||
})
|
||||
});
|
||||
it("gets the filtered user settings", function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => { ["lib1"]} },
|
||||
storage: {},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({
|
||||
user: {
|
||||
username: "nick",
|
||||
anonymous: false,
|
||||
image: "http://example.com",
|
||||
permissions: "*",
|
||||
private: "secret"
|
||||
}
|
||||
}).then(result => {
|
||||
result.should.have.property("user");
|
||||
result.user.should.have.property("username","nick");
|
||||
result.user.should.have.property("permissions","*");
|
||||
result.user.should.have.property("image","http://example.com");
|
||||
result.user.should.have.property("anonymous",false);
|
||||
result.user.should.not.have.property("private");
|
||||
})
|
||||
});
|
||||
it("gets the filtered settings when editor disabled ", function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
disableEditor: true,
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => { ["lib1"]} },
|
||||
storage: {
|
||||
projects: {
|
||||
getActiveProject: () => 'test-active-project',
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({
|
||||
user: {
|
||||
username: "nick",
|
||||
anonymous: false,
|
||||
image: "http://example.com",
|
||||
permissions: "*",
|
||||
private: "secret"
|
||||
}
|
||||
}).then(result => {
|
||||
result.should.have.property("user");
|
||||
result.user.should.have.property("username","nick");
|
||||
result.user.should.have.property("permissions","*");
|
||||
result.user.should.have.property("image","http://example.com");
|
||||
result.user.should.have.property("anonymous",false);
|
||||
result.user.should.not.have.property("private");
|
||||
|
||||
// Filtered out when disableEditor is true
|
||||
result.should.not.have.property("paletteCategories",["red","blue","green"]);
|
||||
result.should.not.have.property("testNodeSetting","helloWorld");
|
||||
result.should.not.have.property("foo",123);
|
||||
result.should.not.have.property("flowEncryptionType","test-key-type");
|
||||
result.should.not.have.property("project");
|
||||
result.should.not.have.property("git");
|
||||
|
||||
})
|
||||
});
|
||||
it('includes project settings if projects available', function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => { ["lib1"]} },
|
||||
storage: {
|
||||
projects: {
|
||||
getActiveProject: () => 'test-active-project',
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({
|
||||
user: {
|
||||
username: "nick",
|
||||
anonymous: false,
|
||||
image: "http://example.com",
|
||||
permissions: "*",
|
||||
private: "secret"
|
||||
}
|
||||
}).then(result => {
|
||||
result.should.have.property("project","test-active-project");
|
||||
result.should.not.have.property("files");
|
||||
result.should.have.property("git");
|
||||
result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
});
|
||||
});
|
||||
|
||||
it('includes existing files details if projects enabled but no active project and files exist', function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => { ["lib1"]} },
|
||||
storage: {
|
||||
projects: {
|
||||
flowFileExists: () => true,
|
||||
getActiveProject: () => false,
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({
|
||||
user: {
|
||||
username: "nick",
|
||||
anonymous: false,
|
||||
image: "http://example.com",
|
||||
permissions: "*",
|
||||
private: "secret"
|
||||
}
|
||||
}).then(result => {
|
||||
result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
result.should.not.have.property("project");
|
||||
result.should.have.property("files");
|
||||
result.files.should.have.property("flow",'test-flow-file');
|
||||
result.files.should.have.property("credentials",'test-creds-file');
|
||||
result.should.have.property("git");
|
||||
result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not include file details if projects enabled but no active project and files do not exist', function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: (obj) => {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
exportPluginSettings: (obj) => {
|
||||
obj.testPluginSettings = "helloPluginWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
listContextStores: () => { return {stores:["file","memory"], default: "file"} },
|
||||
installerEnabled: () => false,
|
||||
getCredentialKeyType: () => "test-key-type"
|
||||
},
|
||||
library: {getLibraries: () => { ["lib1"]} },
|
||||
storage: {
|
||||
projects: {
|
||||
flowFileExists: () => false,
|
||||
getActiveProject: () => false,
|
||||
getFlowFilename: () => 'test-flow-file',
|
||||
getCredentialsFilename: () => 'test-creds-file',
|
||||
getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}}
|
||||
}
|
||||
},
|
||||
telemetry: { isEnabled: () => true }
|
||||
})
|
||||
return settings.getRuntimeSettings({
|
||||
user: {
|
||||
username: "nick",
|
||||
anonymous: false,
|
||||
image: "http://example.com",
|
||||
permissions: "*",
|
||||
private: "secret"
|
||||
}
|
||||
}).then(result => {
|
||||
result.should.not.have.property("project");
|
||||
result.should.not.have.property("files");
|
||||
result.should.have.property("git");
|
||||
result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("getUserSettings", function() {
|
||||
before(function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
getUserSettings: username => username
|
||||
}
|
||||
});
|
||||
})
|
||||
it("returns default user settings", function() {
|
||||
return settings.getUserSettings({}).then(result => {
|
||||
result.should.eql("_");
|
||||
})
|
||||
})
|
||||
it("returns default user settings for anonymous", function() {
|
||||
return settings.getUserSettings({user:{anonymous:true}}).then(result => {
|
||||
result.should.eql("_");
|
||||
})
|
||||
})
|
||||
it("returns user settings", function() {
|
||||
return settings.getUserSettings({user:{username:'nick'}}).then(result => {
|
||||
result.should.eql("nick");
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
describe("updateUserSettings", function() {
|
||||
var userSettings;
|
||||
before(function() {
|
||||
settings.init({
|
||||
settings: {
|
||||
getUserSettings: username => clone(userSettings[username]),
|
||||
setUserSettings: (username, settings) => {
|
||||
if (username === 'error') {
|
||||
var p = Promise.reject(new Error("unknown user"));
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (username === 'throw') {
|
||||
throw new Error("thrown error");
|
||||
}
|
||||
userSettings[username] = clone(settings);
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
log: mockLog()
|
||||
});
|
||||
})
|
||||
beforeEach(function() {
|
||||
userSettings = {
|
||||
"_": { abc: 123 },
|
||||
"nick": {abc: 456}
|
||||
}
|
||||
})
|
||||
it('sets default user settings', function() {
|
||||
return settings.updateUserSettings({settings:{abc:789}}).then(function() {
|
||||
userSettings._.abc.should.eql(789)
|
||||
})
|
||||
})
|
||||
it('merges user settings', function() {
|
||||
return settings.updateUserSettings({settings:{def:789}}).then(function() {
|
||||
userSettings._.abc.should.eql(123)
|
||||
userSettings._.def.should.eql(789)
|
||||
})
|
||||
})
|
||||
it('sets default user settings for anonymous user', function() {
|
||||
return settings.updateUserSettings({user:{anonymous:true},settings:{def:789}}).then(function() {
|
||||
userSettings._.abc.should.eql(123)
|
||||
userSettings._.def.should.eql(789)
|
||||
})
|
||||
})
|
||||
it('sets named user settings', function() {
|
||||
return settings.updateUserSettings({user:{username:'nick'},settings:{def:789}}).then(function() {
|
||||
userSettings.nick.abc.should.eql(456)
|
||||
userSettings.nick.def.should.eql(789)
|
||||
})
|
||||
})
|
||||
it('rejects with suitable error', function(done) {
|
||||
settings.updateUserSettings({user:{username:'error'},settings:{def:789}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
it('rejects with suitable error - thrown', function(done) {
|
||||
settings.updateUserSettings({user:{username:'throw'},settings:{def:789}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
});
|
||||
describe("getUserKeys", function() {
|
||||
before(function() {
|
||||
settings.init({
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
listSSHKeys: username => {
|
||||
if (username === 'error') {
|
||||
var p = Promise.reject(new Error("unknown user"));
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve([username])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
it('returns the default users keys', function() {
|
||||
return settings.getUserKeys({}).then(result => {
|
||||
result.should.eql(['__default']);
|
||||
})
|
||||
})
|
||||
it('returns the default users keys for anonymous', function() {
|
||||
return settings.getUserKeys({user:{anonymous:true}}).then(result => {
|
||||
result.should.eql(['__default']);
|
||||
})
|
||||
})
|
||||
it('returns the users keys', function() {
|
||||
return settings.getUserKeys({user:{username:'nick'}}).then(result => {
|
||||
result.should.eql(['nick']);
|
||||
})
|
||||
})
|
||||
it('rejects with suitable error', function(done) {
|
||||
settings.getUserKeys({user:{username:'error'}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
});
|
||||
|
||||
describe("getUserKey", function() {
|
||||
before(function() {
|
||||
settings.init({
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
getSSHKey: (username, id) => {
|
||||
if (username === 'error') {
|
||||
var p = Promise.reject(new Error("unknown user"));
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
} else if (username === '404') {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return Promise.resolve({username,id})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
it('returns the default user key', function() {
|
||||
return settings.getUserKey({id:'keyid'}).then(result => {
|
||||
result.should.eql({id:'keyid',username:"__default"});
|
||||
})
|
||||
})
|
||||
it('returns the default user key - anonymous', function() {
|
||||
return settings.getUserKey({user:{anonymous:true},id:'keyid'}).then(result => {
|
||||
result.should.eql({id:'keyid',username:"__default"});
|
||||
})
|
||||
})
|
||||
it('returns the user key', function() {
|
||||
return settings.getUserKey({user:{username:'nick'},id:'keyid'}).then(result => {
|
||||
result.should.eql({id:'keyid',username:"nick"});
|
||||
})
|
||||
})
|
||||
it('404s for unknown key', function(done) {
|
||||
settings.getUserKey({user:{username:'404'},id:'keyid'}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 404);
|
||||
err.should.have.property('code', 'not_found');
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
it('rejects with suitable error', function(done) {
|
||||
settings.getUserKey({user:{username:'error'}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
});
|
||||
describe("generateUserKey", function() {
|
||||
before(function() {
|
||||
settings.init({
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
generateSSHKey: (username, opts) => {
|
||||
if (username === 'error') {
|
||||
var p = Promise.reject(new Error("unknown user"));
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
return Promise.resolve(JSON.stringify({username,opts}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
it('generates for the default user', function() {
|
||||
return settings.generateUserKey({id:'keyid'}).then(result => {
|
||||
var data = JSON.parse(result);
|
||||
data.should.eql({opts:{id:'keyid'},username:"__default"});
|
||||
})
|
||||
})
|
||||
it('generates for the default user - anonymous', function() {
|
||||
return settings.generateUserKey({user:{anonymous:true},id:'keyid'}).then(result => {
|
||||
var data = JSON.parse(result);
|
||||
data.should.eql({opts:{user:{anonymous:true},id:'keyid'},username:"__default"});
|
||||
})
|
||||
})
|
||||
it('generates for the user', function() {
|
||||
return settings.generateUserKey({user:{username:'nick'},id:'keyid'}).then(result => {
|
||||
var data = JSON.parse(result);
|
||||
data.should.eql({opts:{user:{username:'nick'},id:'keyid'},username:"nick"});
|
||||
})
|
||||
})
|
||||
it('rejects with suitable error', function(done) {
|
||||
settings.generateUserKey({user:{username:'error'}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
});
|
||||
describe("removeUserKey", function() {
|
||||
var received = {};
|
||||
before(function() {
|
||||
settings.init({
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
deleteSSHKey: (username, id) => {
|
||||
if (username === 'error') {
|
||||
var p = Promise.reject(new Error("unknown user"));
|
||||
p.catch(()=>{});
|
||||
return p;
|
||||
}
|
||||
received.username = username;
|
||||
received.id = id;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
beforeEach(function() {
|
||||
received.username = "";
|
||||
received.id = "";
|
||||
})
|
||||
it('removes for the default user', function() {
|
||||
return settings.removeUserKey({id:'keyid'}).then(() => {
|
||||
received.username.should.eql("__default");
|
||||
received.id.should.eql("keyid");
|
||||
})
|
||||
})
|
||||
it('removes for the default user key - anonymous', function() {
|
||||
return settings.removeUserKey({user:{anonymous:true},id:'keyid'}).then(() => {
|
||||
received.username.should.eql("__default");
|
||||
received.id.should.eql("keyid");
|
||||
})
|
||||
})
|
||||
it('returns the user key', function() {
|
||||
return settings.removeUserKey({user:{username:'nick'},id:'keyid'}).then(() => {
|
||||
received.username.should.eql("nick");
|
||||
received.id.should.eql("keyid");
|
||||
})
|
||||
})
|
||||
it('rejects with suitable error', function(done) {
|
||||
settings.removeUserKey({user:{username:'error'}}).then(result => {
|
||||
done("Unexpected resolve for error case");
|
||||
}).catch(err => {
|
||||
err.should.have.property('status', 400);
|
||||
done();
|
||||
}).catch(done);
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var request = require("supertest");
|
||||
var express = require("express");
|
||||
var editorApi = require("../../../../red/api/editor");
|
||||
var comms = require("../../../../red/api/editor/comms");
|
||||
var info = require("../../../../red/api/editor/settings");
|
||||
var auth = require("../../../../red/api/auth");
|
||||
var sshkeys = require("../../../../red/api/editor/sshkeys");
|
||||
var bodyParser = require("body-parser");
|
||||
var fs = require("fs-extra");
|
||||
var fspath = require("path");
|
||||
|
||||
|
||||
describe("api/editor/sshkeys", function() {
|
||||
var app;
|
||||
var mockList = [
|
||||
'library','theme','locales','credentials','comms'
|
||||
]
|
||||
var isStarted = true;
|
||||
var errors = [];
|
||||
var session_data = {};
|
||||
|
||||
var mockRuntime = {
|
||||
settings:{
|
||||
httpNodeRoot: true,
|
||||
httpAdminRoot: true,
|
||||
disableEditor: false,
|
||||
exportNodeSettings:function(){},
|
||||
storage: {
|
||||
getSessions: function(){
|
||||
return Promise.resolve(session_data);
|
||||
},
|
||||
setSessions: function(_session) {
|
||||
session_data = _session;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
},
|
||||
log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}},
|
||||
storage: {
|
||||
projects: {
|
||||
ssh: {
|
||||
init: function(){},
|
||||
listSSHKeys: function(){},
|
||||
getSSHKey: function(){},
|
||||
generateSSHKey: function(){},
|
||||
deleteSSHKey: function(){}
|
||||
}
|
||||
}
|
||||
},
|
||||
events:{on:function(){},removeListener:function(){}},
|
||||
isStarted: function() { return isStarted; },
|
||||
nodes: {installerEnabled: function() { return false }}
|
||||
};
|
||||
|
||||
before(function() {
|
||||
auth.init(mockRuntime);
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.use(editorApi.init({},mockRuntime));
|
||||
});
|
||||
after(function() {
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey");
|
||||
sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey");
|
||||
})
|
||||
afterEach(function() {
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.restore();
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.restore();
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.restore();
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.restore();
|
||||
})
|
||||
|
||||
it('GET /settings/user/keys --- return empty list', function(done) {
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([]));
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('keys');
|
||||
res.body.keys.should.be.empty();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys --- return normal list', function(done) {
|
||||
var fileList = [
|
||||
'test_key01',
|
||||
'test_key02'
|
||||
];
|
||||
var retList = fileList.map(function(elem) {
|
||||
return {
|
||||
name: elem
|
||||
};
|
||||
});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList));
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('keys');
|
||||
for (var item of retList) {
|
||||
res.body.keys.should.containEql(item);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys --- return Error', function(done) {
|
||||
var errInstance = new Error("Messages here.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return 404', function(done) {
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null));
|
||||
request(app)
|
||||
.get("/settings/user/keys/NOT_REAL")
|
||||
.expect(404)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('GET /settings/user/keys --- return Unexpected Error', function(done) {
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.listSSHKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return content', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n";
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent));
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true();
|
||||
res.body.should.be.deepEqual({ publickey: fileContent });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal(errInstance.code);
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.getSSHKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return parameter error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name));
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal("You need to have body or body.name");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /settings/user/keys --- return Unexpected error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.generateSSHKey.returns(p);
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- success', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true));
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(204)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.be.deepEqual({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- return Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
errInstance.code = "test_code";
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("test_code");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE /settings/user/keys/<key_file_name> --- return Unexpected Error', function(done) {
|
||||
var key_file_name = "test_key";
|
||||
var errInstance = new Error("Messages.....");
|
||||
var p = Promise.reject(errInstance);
|
||||
p.catch(()=>{});
|
||||
mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property('error');
|
||||
res.body.error.should.be.equal("unexpected_error");
|
||||
res.body.should.have.property('message');
|
||||
res.body.message.should.be.equal(errInstance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
Reference in New Issue
Block a user