Merge branch 'dev' into adding-timeout-to-functio-node

This commit is contained in:
Nick O'Leary
2023-05-25 17:33:41 +01:00
committed by GitHub
302 changed files with 52090 additions and 12244 deletions

View File

@@ -0,0 +1,47 @@
var should = require("should");
var config = require("nr-test-utils").require("@node-red/nodes/core/common/91-global-config.js");
var inject = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js");
var helper = require("node-red-node-test-helper");
describe('unknown Node', function() {
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"global-config", name: "XYZ" }];
helper.load(config, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property("name", "XYZ");
done();
});
});
it('should access global environment variable', function(done) {
var flow = [{id:"n1", type:"global-config", name: "XYZ",
env: [ {
name: "X",
type: "string",
value: "foo"
}]
},
{id: "n2", type: "inject", topic: "t1", payload: "X", payloadType: "env", wires: [["n3"]], z: "flow"},
{id: "n3", type: "helper"}
];
helper.load([config, inject], flow, function() {
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
n3.on("input", (msg) => {
try {
msg.should.have.property("payload", "foo");
done();
} catch (err) {
done(err);
}
});
n2.receive({});
});
});
});

View File

@@ -106,6 +106,27 @@ describe('range Node', function() {
genericRangeTest("clamp", 0, 10, 0, 1000, false, -1, 0, done);
});
it('drops msg if in drop mode and input outside range', function(done) {
var flow = [{"id":"rangeNode1","type":"range","minin":2,"maxin":8,"minout":20,"maxout":80,"action":"drop","round":true,"name":"rangeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(rangeNode, flow, function() {
var rangeNode1 = helper.getNode("rangeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.should.have.property('payload');
msg.payload.should.equal(50);
done();
} catch(err) {
done(err);
}
});
rangeNode1.receive({payload:1});
rangeNode1.receive({payload:9});
rangeNode1.receive({payload:5});
});
});
it('just passes on msg if payload not present', function(done) {
var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":100,"minout":0,"maxout":100,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];

View File

@@ -817,6 +817,105 @@ describe('delay Node', function() {
});
});
it('can part flush and reset rate limit queue', function(done) {
this.timeout(2000);
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":1,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(delayNode, flow, function() {
var delayNode1 = helper.getNode("delayNode1");
var helperNode1 = helper.getNode("helperNode1");
var t = Date.now();
var c = 0;
helperNode1.on("input", function(msg) {
// console.log("GOT",Date.now() - t,msg)
msg.should.have.a.property('payload');
msg.should.have.a.property('topic');
try {
if (msg.topic === "foo") {
msg.payload.should.equal(1);
(Date.now() - t).should.be.approximately(0,50);
c = c + 1;
}
else if (msg.topic === "bar") {
msg.payload.should.equal(2);
(Date.now() - t).should.be.approximately(200,100);
c = c + 1;
}
else if (msg.topic === "fob") {
msg.payload.should.equal(5);
(Date.now() - t).should.be.approximately(400,100);
c = 5;
}
if (c === 5) { done(); }
} catch(e) {
done(e);
}
});
// send test messages
// delayNode1.receive({payload:1,topic:"foo"});
setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } );
setTimeout( function() { delayNode1.receive({payload:2,topic:"far"}); }, 10 );
setTimeout( function() { delayNode1.receive({payload:3,topic:"boo"}); }, 20 );
setTimeout( function() { delayNode1.receive({payload:4,topic:"bar"}); }, 30 );
setTimeout( function() { delayNode1.receive({flush:2,reset:true}); }, 200);
setTimeout( function() { delayNode1.receive({payload:5,topic:"fob"}); }, 300 );
setTimeout( function() { delayNode1.receive({flush:1,reset:true}); }, 400);
});
});
it('can full flush and reset rate limit queue', function(done) {
this.timeout(2000);
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":1,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(delayNode, flow, function() {
var delayNode1 = helper.getNode("delayNode1");
var helperNode1 = helper.getNode("helperNode1");
var t = Date.now();
var c = 0;
helperNode1.on("input", function(msg) {
// console.log("GOT",Date.now() - t,msg)
msg.should.have.a.property('payload');
msg.should.have.a.property('topic');
try {
if (msg.topic === "foo") {
msg.payload.should.equal(1);
(Date.now() - t).should.be.approximately(0,50);
c = c + 1;
}
else if (msg.topic === "bar") {
msg.payload.should.equal(4);
(Date.now() - t).should.be.approximately(200,100);
c = c + 1;
}
else if (msg.topic === "all") {
msg.payload.should.equal(5);
(Date.now() - t).should.be.approximately(200,100);
c = c + 1;
}
else if (msg.topic === "fob") {
msg.payload.should.equal(6);
(Date.now() - t).should.be.approximately(400,100);
c = 5;
}
if (c === 5) { done(); }
} catch(e) {
done(e);
}
});
// send test messages
// delayNode1.receive({payload:1,topic:"foo"});
setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } );
setTimeout( function() { delayNode1.receive({payload:2,topic:"far"}); }, 10 );
setTimeout( function() { delayNode1.receive({payload:3,topic:"boo"}); }, 20 );
setTimeout( function() { delayNode1.receive({payload:4,topic:"bar"}); }, 30 );
setTimeout( function() { delayNode1.receive({payload:5,topic:"last",flush:true,reset:true}); }, 200);
setTimeout( function() { delayNode1.receive({payload:6,topic:"fob"}); }, 300 );
setTimeout( function() { delayNode1.receive({flush:1,reset:true}); }, 400);
});
});
it('can part push to front of rate limit queue', function(done) {
this.timeout(2000);
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":1,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]},

View File

@@ -31,6 +31,7 @@ var multer = require("multer");
var RED = require("nr-test-utils").require("node-red/lib/red");
var fs = require('fs-extra');
var auth = require('basic-auth');
var crypto = require("crypto");
const { version } = require("os");
const net = require('net')
@@ -163,6 +164,100 @@ describe('HTTP Request Node', function() {
delete process.env.NO_PROXY;
}
function getDigestPassword() {
return 'digest-test-password';
}
function getDigest(algorithm, value) {
var hash;
if (algorithm === 'SHA-256') {
hash = crypto.createHash('sha256');
} else if (algorithm === 'SHA-512-256') {
hash = crypto.createHash('sha512');
} else {
hash = crypto.createHash('md5');
}
var hex = hash.update(value).digest('hex');
if (algorithm === 'SHA-512-256') {
hex = hex.slice(0, 64);
}
return hex;
}
function getDigestResponse(req, algorithm, sess, realm, username, nonce, nc, cnonce, qop) {
var ha1 = getDigest(algorithm, username + ':' + realm + ':' + getDigestPassword());
if (sess) {
ha1 = getDigest(algorithm, ha1 + ':' + nonce + ':' + cnonce)
}
let ha2 = getDigest(algorithm, req.method + ':' + req.path);
return qop
? getDigest(algorithm, ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
: getDigest(algorithm, ha1 + ':' + nonce + ':' + ha2);
}
function handleDigestResponse(req, res, algorithm, sess, qop) {
let realm = "node-red";
let nonce = "123456";
let nc = '00000001';
let algorithmValue = sess ? `${algorithm}-sess` : algorithm;
let authHeader = req.headers['authorization'];
if (!authHeader) {
let qopField = qop ? `qop="${qop}", ` : '';
res.setHeader(
'WWW-Authenticate',
`Digest ${qopField}realm="${realm}", nonce="${nonce}", algorithm="${algorithmValue}"`
);
res.status(401).end();
return;
}
var authFields = {};
let re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi;
for (;;) {
var match = re.exec(authHeader);
if (!match) {
break;
}
authFields[match[1]] = match[2] || match[3];
}
// console.log(JSON.stringify(authFields));
if (qop && authFields['qop'] != qop) {
console.log('test1');
res.status(401).end();
return;
}
if (
!authFields['username'] ||
!authFields['response'] ||
authFields['realm'] != realm ||
authFields['nonce'] != nonce ||
authFields['algorithm'] != algorithmValue
) {
console.log('test2');
res.status(401).end();
return;
}
let username = authFields['username'];
let response = authFields['response'];
let cnonce = authFields['cnonce'] || '';
let expectedResponse = getDigestResponse(
req, algorithm, sess, realm, username, nonce, nc, cnonce, qop
);
if (!response || expectedResponse.toLowerCase() !== response.toLowerCase()) {
console.log('test3', response, expectedResponse);
res.status(401).end();
return;
}
res.status(201).end();
}
before(function(done) {
testApp = express();
@@ -222,6 +317,21 @@ describe('HTTP Request Node', function() {
}
res.json(result);
});
testApp.get('/authenticate-digest-md5', function(req, res){
handleDigestResponse(req, res, "MD5", false, false);
});
testApp.get('/authenticate-digest-md5-sess', function(req, res){
handleDigestResponse(req, res, "MD5", true, 'auth');
});
testApp.get('/authenticate-digest-md5-qop', function(req, res){
handleDigestResponse(req, res, "MD5", false, 'auth');
});
testApp.get('/authenticate-digest-sha-256', function(req, res){
handleDigestResponse(req, res, "SHA-256", false, 'auth');
});
testApp.get('/authenticate-digest-sha-512-256', function(req, res){
handleDigestResponse(req, res, "SHA-512-256", false, 'auth');
});
testApp.get('/proxyAuthenticate', function(req, res){
// var user = auth.parse(req.headers['proxy-authorization']);
var result = {
@@ -2018,6 +2128,100 @@ describe('HTTP Request Node', function() {
});
*/
it('should authenticate on server - digest MD5', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',201);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
});
});
it('should authenticate on server - digest MD5 sess', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5-sess')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',201);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
});
});
it('should authenticate on server - digest MD5 qop', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5-qop')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',201);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
});
});
it('should authenticate on server - digest SHA-256', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-sha-256')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',201);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
});
});
it('should authenticate on server - digest SHA-512-256', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-sha-512-256')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',201);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
});
});
});
describe('file-upload', function() {

View File

@@ -53,7 +53,7 @@ describe('MQTT Nodes', function () {
mqttBroker.should.have.property('broker', BROKER_HOST);
mqttBroker.should.have.property('port', BROKER_PORT);
mqttBroker.should.have.property('brokerurl');
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
mqttBroker.should.have.property('autoUnsubscribe', true); //default: true
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
mqttBroker.should.have.property('options');
mqttBroker.options.should.have.property('clean', true);
@@ -96,8 +96,8 @@ describe('MQTT Nodes', function () {
mqttBroker.should.have.property('broker', BROKER_HOST);
mqttBroker.should.have.property('port', BROKER_PORT);
mqttBroker.should.have.property('brokerurl');
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
mqttBroker.should.have.property('autoUnsubscribe', true);
mqttBroker.should.have.property('autoConnect', false); //Set "autoConnect:false" in brokerOptions
mqttBroker.should.have.property('options');
mqttBroker.options.should.have.property('clean', false);
mqttBroker.options.should.have.property('clientId', 'clientid');