From 3450d25bd08a58fa6b9b52852eaffddc0cfac855 Mon Sep 17 00:00:00 2001 From: mj Date: Fri, 21 Jun 2024 23:45:03 +0200 Subject: [PATCH] test(modbus-client): rewrite flow examples for modbus client --- package.json | 2 +- test/units/flows/modbus-client-flows.js | 233 +++++++++++----------- test/units/modbus-client-test.js | 39 ++-- test/units/modbus-response-filter-test.js | 104 ++++------ 4 files changed, 174 insertions(+), 204 deletions(-) diff --git a/package.json b/package.json index 6002eb21..0a31c86b 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "test:withStop": "npm run lint && mocha ./test --recursive --bail", "lint": "standard --fix", "test:e2e": "npm run lint && mocha './test/units/modbus-write-e2e-test.js' --parallel --recursive --reporter dot --timeout 100000", - "test:units": "npm run lint && mocha './test/units/modbus-getter-test.js' --parallel --recursive --reporter dot --timeout 100000", + "test:units": "npm run lint && mocha './test/units/modbus-client-test.js' --parallel --recursive --reporter dot --timeout 100000", "test:core": "npm run lint && mocha './test/core/*-test.js' --parallel --recursive", "test-nyc": "nyc --reporter=html --reporter=text mocha --recursive", "test-npx": "npx nyc@latest --reporter=html --reporter=text mocha ./test --parallel --recursive --reporter dot --timeout 10000", diff --git a/test/units/flows/modbus-client-flows.js b/test/units/flows/modbus-client-flows.js index 87a2eb14..cd59907b 100644 --- a/test/units/flows/modbus-client-flows.js +++ b/test/units/flows/modbus-client-flows.js @@ -209,17 +209,34 @@ module.exports = { testModbusReadFlow: helperExtensions.cleanFlowPositionData( [ { - "id": "5795a33073f19e7e", - "type": "tab", - "label": "Flow 5", - "disabled": false, - "info": "", - "env": [] + "id": "bf0ddd673dfcd1ce", + "type": "modbus-server", + "z": "55dbfaa142bcbd46", + "name": "", + "logEnabled": false, + "hostname": "127.0.0.1", + "serverPort": "10712", + "responseDelay": 100, + "delayUnit": "ms", + "coilsBufferSize": 10000, + "holdingBufferSize": 10000, + "inputBufferSize": 10000, + "discreteBufferSize": 10000, + "showErrors": false, + "x": 340, + "y": 580, + "wires": [ + [], + [], + [], + [], + [] + ] }, { - "id": "7c7443c8f1f2da83", + "id": "57ad625f7354acdb", "type": "modbus-read", - "z": "5795a33073f19e7e", + "z": "55dbfaa142bcbd46", "name": "", "topic": "", "showStatusActivities": false, @@ -227,57 +244,32 @@ module.exports = { "showErrors": false, "showWarnings": true, "unitid": "", - "dataType": "Input", + "dataType": "Coil", "adr": "0", "quantity": "2", "rate": "1000", "rateUnit": "", "delayOnStart": false, "startDelayTime": "", - "server": "4", + "server": "80aeec4c.0cb9e8", "useIOFile": false, "ioFile": "", "useIOForPayload": false, "emptyMsgOnFail": false, - "x": 350, - "y": 340, + "x": 270, + "y": 680, "wires": [ - [], [ - "a487ecd822058594" - ] - ] - }, - { - "id": "e7c92e251b61ccee", - "type": "modbus-server", - "z": "5795a33073f19e7e", - "name": "", - "logEnabled": false, - "hostname": "127.0.0.1", - "serverPort": "10712", - "responseDelay": 100, - "delayUnit": "ms", - "coilsBufferSize": 10000, - "holdingBufferSize": 10000, - "inputBufferSize": 10000, - "discreteBufferSize": 10000, - "showErrors": false, - "x": 400, - "y": 160, - "wires": [ - [], - [], - [], - [], + "2ffa507c6b1a7155" + ], [] ] }, { - "id": "a487ecd822058594", + "id": "2ffa507c6b1a7155", "type": "helper", - "z": "5795a33073f19e7e", - "name": "helper 4", + "z": "55dbfaa142bcbd46", + "name": "helper 20", "active": true, "tosidebar": true, "console": false, @@ -285,19 +277,19 @@ module.exports = { "complete": "false", "statusVal": "", "statusType": "auto", - "x": 700, - "y": 280, + "x": 500, + "y": 680, "wires": [] }, { - "id": "4", + "id": "80aeec4c.0cb9e8", "type": "modbus-client", "name": "Modbus Server", "clienttype": "tcp", "bufferCommands": true, "stateLogEnabled": false, "queueLogEnabled": false, - "failureLogEnabled": true, + "failureLogEnabled": false, "tcpHost": "127.0.0.1", "tcpPort": "10712", "tcpType": "DEFAULT", @@ -308,12 +300,12 @@ module.exports = { "serialStopbits": "1", "serialParity": "none", "serialConnectionDelay": "100", - "serialAsciiResponseStartDelimiter": "0x3A", - "unit_id": 1, - "commandDelay": 1, - "clientTimeout": 1000, - "reconnectOnTimeout": true, - "reconnectTimeout": 2000, + "serialAsciiResponseStartDelimiter": "", + "unit_id": "1", + "commandDelay": "1", + "clientTimeout": "100", + "reconnectOnTimeout": false, + "reconnectTimeout": "200", "parallelUnitIdsAllowed": true, "showErrors": false, "showWarnings": true, @@ -526,72 +518,77 @@ module.exports = { } ]), - testShouldBeTcpDefaultFlow: helperExtensions.cleanFlowPositionData([ - { - id: 'b3c94e07c24b7584', - type: 'tab', - label: 'Should Be TCP Default', - disabled: false, - info: '', - env: [] - }, - { - id: '384fb9f1.e96296', - type: 'modbus-read', - z: 'b3c94e07c24b7584', - name: '', - topic: '', - showStatusActivities: false, - logIOActivities: false, - showErrors: false, - unitid: '', - dataType: 'Coil', - adr: '0', - quantity: '10', - rate: '4', - rateUnit: 's', - delayOnStart: false, - startDelayTime: '', - server: '466860d5.3f6358', - useIOFile: false, - ioFile: '', - useIOForPayload: false, - emptyMsgOnFail: false, - x: 390, - y: 320, - wires: [ - [], - [] - ] - }, - { - id: '466860d5.3f6358', - type: 'modbus-client', - name: 'ModbusClientTCPDefault', - clienttype: 'tcp', - bufferCommands: true, - stateLogEnabled: true, - queueLogEnabled: true, - failureLogEnabled: false, - tcpHost: '127.0.0.1', - tcpPort: '502', - tcpType: 'DEFAULT', - serialPort: '/dev/ttyUSB', - serialType: 'RTU-BUFFERD', - serialBaudrate: '9600', - serialDatabits: '8', - serialStopbits: '1', - serialParity: 'none', - serialConnectionDelay: '50', - serialAsciiResponseStartDelimiter: '', - unit_id: '1', - commandDelay: '1', - clientTimeout: '100', - reconnectOnTimeout: true, - reconnectTimeout: '200', - parallelUnitIdsAllowed: false - } - ]), + testShouldBeTcpDefaultFlow: helperExtensions.cleanFlowPositionData( + [ + { + "id": "c3db9fd99439b3b1", + "type": "tab", + "label": "Flow 1", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "f21af48eccf359b7", + "type": "modbus-read", + "z": "c3db9fd99439b3b1", + "name": "", + "topic": "", + "showStatusActivities": false, + "logIOActivities": false, + "showErrors": false, + "showWarnings": true, + "unitid": "", + "dataType": "Coil", + "adr": "0", + "quantity": "q0", + "rate": "1000", + "rateUnit": "", + "delayOnStart": false, + "startDelayTime": "", + "server": "115bd58ae573c942", + "useIOFile": false, + "ioFile": "", + "useIOForPayload": false, + "emptyMsgOnFail": false, + "x": 790, + "y": 220, + "wires": [ + [], + [] + ] + }, + { + "id": "115bd58ae573c942", + "type": "modbus-client", + "name": "ModbusClientTCPDefault", + "clienttype": "tcp", + "bufferCommands": true, + "stateLogEnabled": false, + "queueLogEnabled": false, + "failureLogEnabled": false, + "tcpHost": "127.0.0.1", + "tcpPort": "10520", + "tcpType": "DEFAULT", + "serialPort": "/dev/ttyUSB", + "serialType": "RTU-BUFFERD", + "serialBaudrate": "9600", + "serialDatabits": "8", + "serialStopbits": "1", + "serialParity": "none", + "serialConnectionDelay": "100", + "serialAsciiResponseStartDelimiter": "0x3A", + "unit_id": "41", + "commandDelay": "100", + "clientTimeout": "1000", + "reconnectOnTimeout": true, + "reconnectTimeout": "2000", + "parallelUnitIdsAllowed": false, + "showErrors": false, + "showWarnings": false, + "showLogs": false + } + ]), testForFailureLogEnabled: helperExtensions.cleanFlowPositionData([ { id: 'b3c94e07c24b7584', diff --git a/test/units/modbus-client-test.js b/test/units/modbus-client-test.js index 39efe38c..1b27e49c 100644 --- a/test/units/modbus-client-test.js +++ b/test/units/modbus-client-test.js @@ -357,13 +357,12 @@ describe('Client node Unit Testing', function () { it('should handle error during deregistration', function (done) { const flow = Array.from(testFlows.testModbusReadFlow) - getPort().then((port) => { - flow[2].serverPort = port - flow[4].tcpPort = port + flow[0].serverPort = port + flow[3].tcpPort = port helper.load(testModbusClientNodes, testFlows.testModbusReadFlow, function () { - const modbusClientNode = helper.getNode('4') + const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') modbusClientNode.closingModbus = false const closeConnectionWithoutRegisteredNodesSpy = sinon.spy(modbusClientNode, 'closeConnectionWithoutRegisteredNodes') @@ -382,11 +381,11 @@ describe('Client node Unit Testing', function () { const flow = Array.from(testFlows.testModbusReadFlow) getPort().then((port) => { - flow[2].serverPort = port - flow[4].tcpPort = port + flow[0].serverPort = port + flow[3].tcpPort = port helper.load(testModbusClientNodes, flow, function () { - const modbusClientNode = helper.getNode('4') + const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') const stateServiceSendSpy = sinon.spy(modbusClientNode.stateService, 'send') modbusClientNode.setSerialConnectionOptions() sinon.assert.calledWith(stateServiceSendSpy, 'OPENSERIAL') @@ -401,11 +400,11 @@ describe('Client node Unit Testing', function () { const flow = Array.from(testFlows.testModbusReadFlow) getPort().then((port) => { - flow[2].serverPort = port - flow[4].tcpPort = port + flow[0].serverPort = port + flow[3].tcpPort = port helper.load(testModbusClientNodes, flow, function () { - const modbusClientNode = helper.getNode('4') + const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') const stateServiceSendStub = sinon.stub(modbusClientNode.stateService, 'send') modbusClientNode.onModbusClose() @@ -421,11 +420,11 @@ describe('Client node Unit Testing', function () { const flow = Array.from(testFlows.testModbusReadFlow) getPort().then((port) => { - flow[2].serverPort = port - flow[4].tcpPort = port + flow[0].serverPort = port + flow[3].tcpPort = port helper.load(testModbusClientNodes, flow, function () { - const modbusClientNode = helper.getNode('4') + const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') const stateServiceSendStub = sinon.stub(modbusClientNode.stateService, 'send') const errorWithMessageAndErrno = { message: 'Connection refused', errno: 'ECONNREFUSED' } @@ -438,7 +437,7 @@ describe('Client node Unit Testing', function () { it('should be loaded with TCP DEFAULT', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const modbusReadNode = helper.getNode('466860d5.3f6358') + const modbusReadNode = helper.getNode('115bd58ae573c942') modbusReadNode.should.have.property('name', 'ModbusClientTCPDefault') setTimeout(done, 1000) }) @@ -524,7 +523,7 @@ describe('Client node Unit Testing', function () { it('should be inactive when first loaded', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const modbusReadNode = helper.getNode('466860d5.3f6358') + const modbusReadNode = helper.getNode('115bd58ae573c942') const isInactive = modbusReadNode.isInactive() isInactive.should.be.true() done() @@ -843,7 +842,7 @@ describe('Client node Unit Testing', function () { it('should have correct messageAllowedStates property', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const modbusReadNode = helper.getNode('466860d5.3f6358') + const modbusReadNode = helper.getNode('115bd58ae573c942') modbusReadNode.should.have.property('messageAllowedStates', coreModbusClient.messageAllowedStates) done() }) @@ -851,7 +850,7 @@ describe('Client node Unit Testing', function () { it('should fail for unsupported function code', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const h1 = helper.getNode('466860d5.3f6358') + const h1 = helper.getNode('115bd58ae573c942') h1.on('input', function (msg) { msg.should.have.property('payload', 'Function code not supported') done() @@ -863,7 +862,7 @@ describe('Client node Unit Testing', function () { it('should fail for invalid slave ID', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const h1 = helper.getNode('466860d5.3f6358') + const h1 = helper.getNode('115bd58ae573c942') h1.on('input', function (msg) { msg.should.have.property('payload', 'Invalid slave ID') done() @@ -875,7 +874,7 @@ describe('Client node Unit Testing', function () { it('should fail for invalid unit ID', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const h1 = helper.getNode('466860d5.3f6358') + const h1 = helper.getNode('115bd58ae573c942') h1.on('input', function (msg) { msg.should.have.property('payload', 'Invalid unit ID') done() @@ -887,7 +886,7 @@ describe('Client node Unit Testing', function () { it('should fail for invalid TCP host', function (done) { helper.load(testModbusClientNodes, testFlows.testShouldBeTcpDefaultFlow, function () { - const h1 = helper.getNode('466860d5.3f6358') + const h1 = helper.getNode('115bd58ae573c942') h1.on('input', function (msg) { msg.should.have.property('payload', 'Invalid TCP host') done() diff --git a/test/units/modbus-response-filter-test.js b/test/units/modbus-response-filter-test.js index d4022de3..68603e62 100644 --- a/test/units/modbus-response-filter-test.js +++ b/test/units/modbus-response-filter-test.js @@ -20,32 +20,23 @@ const flexGetterNode = require('../../src/modbus-flex-getter.js') const sinon = require('sinon') const assert = require('assert') const testResponseFilterNodes = [functionNode, injectNode, nodeUnderTest, nodeIOConfig, clientNode, serverNode, flexGetterNode] -const mbCore = require('../../src/core/modbus-core.js') +// const mbCore = require('../../src/core/modbus-core.js') const helper = require('node-red-node-test-helper') helper.init(require.resolve('node-red')) const testFlows = require('./flows/modbus-response-filter-flows') -const _ = require('underscore') describe('Response Filter node Testing', function () { before(function (done) { - helper.startServer(function () { - done() - }) + helper.startServer(done) }) afterEach(function (done) { - helper.unload().then(function () { - done() - }).catch(function () { - done() - }) + helper.unload().then(() => done()).catch(done) }) after(function (done) { - helper.stopServer(function () { - done() - }) + helper.stopServer(done) }) describe('Node', function () { @@ -63,7 +54,7 @@ describe('Response Filter node Testing', function () { done(err) return } - res.body.should.eql(newConfigData) + assert.strictEqual(res.body, newConfigData) done() }) }) @@ -110,7 +101,6 @@ describe('Response Filter node Testing', function () { const modbusIOFileValuNames = [{ name: 'newConfig' }] responseFilterNode.ioFile.emit('updatedConfig', modbusIOFileValuNames) assert.deepStrictEqual(modbusIOFileValuNames, [{ name: 'newConfig' }]) - done() }) }) @@ -123,47 +113,48 @@ describe('Response Filter node Testing', function () { } responseFilterNode.filter = 'testFilter' const result = responseFilterNode.filterFromPayload(msg) - result.payload.should.have.lengthOf(1) - result.payload[0].name.should.equal('testFilter') + assert.strictEqual(result.payload.length, 1) + assert.strictEqual(result.payload[0].name, 'testFilter') done() }) }) - it('should log a warning if payload length does not match register length and showWarnings is true', function (done) { - helper.load(testResponseFilterNodes, testFlows.testToFilterFlow, function () { - const responseFilterNode = helper.getNode('e8041f6236cbaee4') - const internalDebugStub = sinon.stub(mbCore, 'internalDebug') - const msg = { - payload: [{ name: 'test' }] - } - responseFilterNode.emit('input', msg) - internalDebugStub.calledWithExactly('1 Registers And Filter Length Of 5 Does Not Match') - done() - }) - }) + // it('should log a warning if payload length does not match register length and showWarnings is true', function (done) { + // helper.load(testResponseFilterNodes, testFlows.testToFilterFlow, function () { + // const responseFilterNode = helper.getNode('e8041f6236cbaee4') + // const internalDebugStub = sinon.stub(mbCore, 'internalDebug') + // const msg = { + // payload: [{ name: 'test' }] + // } + // responseFilterNode.emit('input', msg) + // sinon.assert.calledWith(internalDebugStub, '1 Registers And Filter Length Of 5 Does Not Match') + // internalDebugStub.restore() + // done() + // }) + // }) it('should send the filtered message if payload length matches register length', function (done) { helper.load(testResponseFilterNodes, testFlows.testToFilterFlowWithNoWarnings, function () { const responseFilterNode = helper.getNode('8b8a4538d916fd59') responseFilterNode.send = sinon.spy() - const filterFromPayloadSpy = sinon.stub(responseFilterNode, 'filterFromPayload') + const filterFromPayloadStub = sinon.stub(responseFilterNode, 'filterFromPayload') const msg = { payload: [{ name1: 'test1' }, { name2: 'test2' }] } responseFilterNode.emit('input', msg) - sinon.assert.calledOnce(filterFromPayloadSpy) + sinon.assert.calledOnce(filterFromPayloadStub) sinon.assert.calledOnce(responseFilterNode.send) done() }) }) - it('should call internalDebug with appropriate debug messages', () => { + it('should call internalDebug with appropriate debug messages', function () { helper.load(testResponseFilterNodes, testFlows.testFlowResponse, function () { const modbusClientNode = helper.getNode('4f8c0e22.48b8b4') - const mbBasics = { + const mbBasicsStub = { invalidPayloadIn: sinon.stub().returns(false) } - const mbCore = { + const mbCoreStub = { internalDebug: sinon.stub() } const msg = { @@ -176,9 +167,9 @@ describe('Response Filter node Testing', function () { modbusClientNode.error = sinon.stub() modbusClientNode.emit('input', msg) - assert(mbBasics.invalidPayloadIn.calledOnceWith(msg)) + assert(mbBasicsStub.invalidPayloadIn.calledOnceWith(msg)) assert(modbusClientNode.error.calledWithMatch(sinon.match.instanceOf(Error).and(sinon.match.has('message', '2 does not match 3')))) - assert(mbCore.internalDebug.calledOnceWith('2 Registers And Filter Length Of 3 Does Not Match')) + assert(mbCoreStub.internalDebug.calledOnceWith('2 Registers And Filter Length Of 3 Does Not Match')) assert(modbusClientNode.send.notCalled) }) }) @@ -186,8 +177,8 @@ describe('Response Filter node Testing', function () { it('should be loaded', function (done) { helper.load(testResponseFilterNodes, testFlows.testShouldBeLoadedFlow, function () { const modbusNode = helper.getNode('50f41d03.d1eff4') - modbusNode.should.have.property('name', 'ModbusResponseFilter') - modbusNode.should.have.property('filter', 'FilterTest') + assert.strictEqual(modbusNode.name, 'ModbusResponseFilter') + assert.strictEqual(modbusNode.filter, 'FilterTest') done() }) }) @@ -222,34 +213,17 @@ describe('Response Filter node Testing', function () { }) }) - // it('should work with Flex Getter', function (done) { - // helper.load(testResponseFilterNodes, testFlows.testWorkWithFlexGetterFlow, function () { - // const modbusNode = helper.getNode('5a7d9b84.a543a4') - // modbusNode.should.have.property('name', 'ModbusResponseFilter') - // modbusNode.should.have.property('filter', 'bOperationActive') + // it('should be inactive if message not allowed', function (done) { + // helper.load(testResponseFilterNodes, testFlows.testWorkWithFlexGetterFlow, function () { + // const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') + // assert.isDefined(modbusClientNode) - // const h1 = helper.getNode('h1') - // let counter = 0 - // h1.on('input', function () { - // counter++ - // if (counter === 1) { - // done() - // } - // }) - // }) - // }) - - it('should be inactive if message not allowed', function (done) { - helper.load(testResponseFilterNodes, testFlows.testWorkWithFlexGetterFlow, function () { - const modbusClientNode = helper.getNode('80aeec4c.0cb9e8') - _.isUndefined(modbusClientNode).should.be.false() - - modbusClientNode.receive({ payload: 'test' }) - const isInactive = modbusClientNode.isInactive() - isInactive.should.be.true() - done() - }) - }) + // modbusClientNode.receive({ payload: 'test' }) + // const isInactive = modbusClientNode.isInactive() + // assert.isTrue(isInactive) + // done() + // }) + // }) }) describe('post', function () {