/** * 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 net = require("net"); var should = require("should"); var stoppable = require('stoppable'); var helper = require("node-red-node-test-helper"); var tcpinNode = require("nr-test-utils").require("@node-red/nodes/core/network/31-tcpin.js"); var RED = require("nr-test-utils").require("node-red/lib/red.js"); describe('TCP Request Node', function() { var server = undefined; var port = 9000; function startServer(done) { port += 1; server = stoppable(net.createServer(function(c) { c.on('data', function(data) { var rdata = "ACK:"+data.toString(); c.write(rdata); }); c.on('error', function(err) { startServer(done); }); })).listen(port, "127.0.0.1", function(err) { done(); }); } before(function(done) { startServer(done); }); after(function(done) { server.stop(done); }); afterEach(function() { helper.unload(); }); function testTCP(flow, val0, val1, done) { helper.load(tcpinNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); n2.on("input", function(msg) { try { if (typeof val1 === 'object') { msg.should.have.properties(Object.assign({}, val1, {payload: Buffer.from(val1.payload)})); } else { msg.should.have.property('payload', Buffer.from(val1)); } done(); } catch(err) { done(err); } }); if((typeof val0) === 'object') { n1.receive(val0); } else { n1.receive({payload:val0}); } }); } function testTCPMany(flow, values, result, done) { helper.load(tcpinNode, flow, () => { const n1 = helper.getNode("n1"); const n2 = helper.getNode("n2"); n2.on("input", msg => { try { if (typeof result === 'object') { if (flow[0].ret === "string") { msg.should.have.properties(Object.assign({}, result, {payload: result.payload})); } else { msg.should.have.properties(Object.assign({}, result, {payload: Buffer.from(result.payload)})); } } else { if (flow[0].ret === "string") { msg.should.have.property('payload', result); } else { msg.should.have.property('payload', Buffer.from(result)); } } done(); } catch(err) { done(err); } }); values.forEach(value => { n1.receive(typeof value === 'object' ? value : {payload: value}); }); }); } describe('single message', function () { it('should send & recv data', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: 'foo', topic: 'bar' }, { payload: 'ACK:foo', topic: 'bar' }, done); }); it('should retain complete message', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: 'foo', topic: 'bar' }, { payload: 'ACK:foo', topic: 'bar' }, done); }); it('should send & recv data when specified character received', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: 'foo0bar0', topic: 'bar' }, { payload: 'ACK:foo0', topic: 'bar' }, done); }); it('should send & recv data after fixed number of chars received', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: 'foo bar', topic: 'bar' }, { payload: 'ACK:foo', topic: 'bar' }, done); }); it('should send & receive, then keep connection', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: 'foo', topic: 'bar' }, { payload: 'ACK:foo', topic: 'bar' }, done); }); it('should send & recv data to/from server:port from msg', function(done) { var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCP(flow, { payload: "foo", host: "localhost", port: port }, { payload: "ACK:foo", host: 'localhost', port: port }, done); }); }); describe('many messages', function () { it('should send & recv data', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: 'f', topic: 'bar' }, { payload: 'o', topic: 'bar' }, { payload: 'o', topic: 'bar' }], { payload: 'ACK:foo', topic: 'bar' }, done); }); it('should send & recv data when specified character received', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "foo0", topic: 'bar' }, { payload: "bar0", topic: 'bar' }], { payload: "ACK:foo0", topic: 'bar' }, done); }); it('should send & recv data after fixed number of chars received', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "fo", topic: 'bar' }, { payload: "ob", topic: 'bar' }, { payload: "ar", topic: 'bar' }], { payload: "ACK:foo", topic: 'bar' }, done); }); it('should send & receive, then keep connection', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "foo", topic: 'bar' }, { payload: "bar", topic: 'bar' }, { payload: "baz", topic: 'bar' }], { payload: "ACK:foobarbaz", topic: 'bar' }, done); }); it('should send & receive, then keep connection, and not split return strings', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", ret:"string", newline:"", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "foo", topic: 'boo' }, { payload: "bar\nfoo", topic: 'boo' }], { payload: "ACK:foobar\nfoo", topic: 'boo' }, done); }); it('should send & receive, then keep connection, and split return strings', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", ret:"string", newline:"\\n", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "foo", topic: 'boo' }, { payload: "bar\nfoo", topic: 'boo' }], { payload: "ACK:foobar", topic: 'boo' }, done); }); it('should send & receive, then keep connection, and split return strings and reattach delimiter', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", ret:"string", newline:"\\n", trim:true, wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: "foo", topic: 'boo' }, { payload: "bar\nfoo", topic: 'boo' }], { payload: "ACK:foobar\n", topic: 'boo' }, done); }); it('should send & recv data to/from server:port from msg', function(done) { var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [ { payload: "f", host: "localhost", port: port }, { payload: "o", host: "localhost", port: port }, { payload: "o", host: "localhost", port: port } ], { payload: "ACK:foo", host: 'localhost', port: port }, done); }); it('should limit the queue size', function (done) { RED.settings.tcpMsgQueueSize = 10; var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, {id:"n2", type:"helper"}]; // create one more msg than is allowed const msgs = new Array(RED.settings.tcpMsgQueueSize + 1).fill('x'); const expected = msgs.slice(0, -1); testTCPMany(flow, msgs, "ACK:" + expected.join(''), done); }); it('should only retain the latest message', function(done) { var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, {id:"n2", type:"helper"}]; testTCPMany(flow, [{ payload: 'f', topic: 'bar' }, { payload: 'o', topic: 'baz' }, { payload: 'o', topic: 'quux' }], { payload: 'ACK:foo', topic: 'quux' }, done); }); }); });