node-red-contrib-boolean-logic-ultimate
Version:
A set of Node-RED enhanced boolean logic and utility nodes, flow interruption, blinker, debouncer, invert, filter, toggle etc.., with persistent values after reboot. Compatible also with Homeassistant values.
170 lines (149 loc) • 4.74 kB
JavaScript
;
const { expect } = require('chai');
const { helper, loadRateLimiter } = require('./helpers');
describe('RateLimiterUltimate node', function () {
this.timeout(5000);
before(function (done) {
helper.startServer(done);
});
after(function (done) {
helper.stopServer(done);
});
afterEach(function () {
return helper.unload();
});
it('debounce trailing emits after wait', function (done) {
const flowId = 'flow1';
const flow = [
{ id: flowId, type: 'tab', label: 'flow1' },
{
id: 'rate',
type: 'RateLimiterUltimate',
z: flowId,
name: 'debounce-test',
mode: 'debounce',
wait: 40,
emitOn: 'trailing',
payloadPropName: 'payload',
controlTopic: 'rate',
statInterval: 0,
wires: [['out'], ['diag']],
},
{ id: 'in', type: 'helper', z: flowId, wires: [['rate']] },
{ id: 'out', type: 'helper', z: flowId },
{ id: 'diag', type: 'helper', z: flowId },
];
loadRateLimiter(flow).then(() => {
const rate = helper.getNode('rate');
const out = helper.getNode('out');
const diag = helper.getNode('diag');
diag.on('input', () => done(new Error('Should not emit diagnostics for trailing debounce')));
out.on('input', (msg) => {
try {
expect(msg.payload).to.equal('first');
done();
} catch (err) {
done(err);
}
});
rate.receive({ topic: 'sensor', payload: 'first' });
}).catch(done);
});
it('throttle drops rapid message when trailing disabled', function (done) {
const flowId = 'flow2';
const flow = [
{ id: flowId, type: 'tab', label: 'flow2' },
{
id: 'rate',
type: 'RateLimiterUltimate',
z: flowId,
name: 'throttle-test',
mode: 'throttle',
interval: 120,
trailing: false,
payloadPropName: 'payload',
controlTopic: 'rate',
statInterval: 0,
wires: [['out'], ['diag']],
},
{ id: 'in', type: 'helper', z: flowId, wires: [['rate']] },
{ id: 'out', type: 'helper', z: flowId },
{ id: 'diag', type: 'helper', z: flowId },
];
loadRateLimiter(flow).then(() => {
const rate = helper.getNode('rate');
const out = helper.getNode('out');
const diag = helper.getNode('diag');
let seenForward = 0;
out.on('input', (msg) => {
seenForward += 1;
if (seenForward > 1) {
done(new Error('Unexpected second forward message'));
} else {
expect(msg.payload).to.equal('first');
}
});
diag.on('input', (msg) => {
try {
expect(msg.payload).to.have.property('reason', 'dropped-throttle');
expect(msg.payload.msg.payload).to.equal('second');
done();
} catch (err) {
done(err);
}
});
rate.receive({ topic: 'sensor', payload: 'first' });
setTimeout(() => {
rate.receive({ topic: 'sensor', payload: 'second' });
}, 20);
}).catch(done);
});
it('window queue replays message after window expires', function (done) {
const flowId = 'flow3';
const flow = [
{ id: flowId, type: 'tab', label: 'flow3' },
{
id: 'rate',
type: 'RateLimiterUltimate',
z: flowId,
name: 'window-test',
mode: 'window',
windowSize: 120,
maxInWindow: 1,
dropStrategy: 'queue',
payloadPropName: 'payload',
controlTopic: 'rate',
statInterval: 0,
wires: [['out'], ['diag']],
},
{ id: 'in', type: 'helper', z: flowId, wires: [['rate']] },
{ id: 'out', type: 'helper', z: flowId },
{ id: 'diag', type: 'helper', z: flowId },
];
loadRateLimiter(flow).then(() => {
const rate = helper.getNode('rate');
const out = helper.getNode('out');
const diag = helper.getNode('diag');
const start = Date.now();
const outputs = [];
diag.on('input', () => done(new Error('Queue strategy should not drop messages')));
out.on('input', (msg) => {
outputs.push({ msg, ts: Date.now() - start });
if (outputs.length === 2) {
try {
expect(outputs[0].msg.payload).to.equal('one');
expect(outputs[1].msg.payload).to.equal('two');
expect(outputs[1].ts).to.be.at.least(100);
done();
} catch (err) {
done(err);
}
}
});
rate.receive({ topic: 'sensor', payload: 'one' });
setTimeout(() => {
rate.receive({ topic: 'sensor', payload: 'two' });
}, 20);
}).catch(done);
});
});