How to test socket.io server when client emits - node.js

I want a client to emit a signal, and test the behaviour of my socket.io server when that signal is received. I have looked at this question and these blog posts:
jest
mocha, chai
but they seem to be directed at testing the client, rather than the server.
Here is an example of something that I am trying to implement:
test('should communicate with waiting for socket.io handshakes', (done) => {
socket_client.emit('example', 'some messages');
setTimeout(() => {
socket_server.on('example', message => {
expect(message).toBe('INCORRECT MESSAGE');
});
done();
}, 50);
When I run my test suit, this should fail, but doesn't.
Does anyone have a simple example of testing this sort of behaviour?
Currently I'm using jest but any framework is fine.
My set up and teardown of the socket.io server test is as below:
import * as http from 'http';
import ioBack from 'socket.io';
let socket_client;
let httpServer;
let httpServerAddr;
let socket_server;
beforeAll((done) => {
httpServer = http.createServer().listen();
httpServerAddr = httpServer.address();
socket_server = ioBack(httpServer);
done();
});
afterAll((done) => {
socket_server.close();
httpServer.close();
done();
});

I am using mocha for testing. But I am not sure what you are doing. In your backend socket server there is no listener that you defined.
Here is a small example for a test. Maybe that helps?!
Test case
var sockethost = websocketUrl;
socketclient = io.connect(sockethost);
socketclient.on('customerId', function(data) {
var data = {
customerId: 10,
}
socketclient.emit('customerId', data);
});
Server:
var io = require('socket.io')(http);
io.sockets.on('connection', function (socket) {
socket.emit('customerId', null);
});
So that is a very simple test. The backend server sends out the connected client 'customerId' the client has a listener for customerId and sends back its customerId. You can also do the other way around, that you have a listener in server, and an emit in client. But I am not completely sure what you are trying to do.

Related

Whats the problem with the socketio connection?

Im having this alot of http petitions (6k INSIDE LAGGING) in 1-3 minutes in the console when i receive or send data to a socketio connection.
Im using node+express in the backend and vue on the front
Backend:
app.js
mongoose.connect('mongodb://localhost/app',{useNewUrlParser:true,useFindAndModify:false})
.then(result =>{
const server = app.listen(3000)
const io = require('./sockets/socket').init(server)
io.on('connection', socket =>{
// console.log('client connected')
})
if(result){console.log('express & mongo running');
}
})
.catch(error => console.log(error))
I created a io instance to use it on the routes
let io
module.exports = {
init: httpServer => {
io = require('socket.io')(httpServer)
return io;
},
getIo:()=>{
if(!io){
throw new Error('socket io not initialized')
}
return io;
}
}
Then, on the route, depending of the logic, the if,else choose what type socket response do
router.post('/post/voteup',checkAuthentication, async (req,res)=>{
//some logic
if(a.length <= 0){
io.getIo().emit('xxx', {action:'cleanAll'})
}
else if(b.length <= 0){
io.getIo().emit('xxx', {action:'cleanT',datoOne})
}
else{
io.getIo().emit('xxx', {action:'cleanX',dataTwo,dataOne,selected})
}
res.json({ serverResponse:'success'})
})
In the front (component) (activated with beforeUpdate life cycle hook)
getData(){
let socket = openSocket('http://localhost:3000')
socket.on('xxx', data => {
if(data.action === 'cleanX'){
if(this.selected === data.selected){
this.ddd = data.dataTwo
}
else if(!this.userTeamNickname){
this.qqq= data.dataOne
}
}
else if(data.action === 'cleanAll'){
this.ddd= []
this.qqq= []
}
else if(data.action === 'cleanT'){
this.ddd= data.dataOne
}
})
},
1. What kind of behavior can produce this such error?
2. Is any other most efficient way to do this?
It looks like socket.io is failing to establish a webSocket connection and has never advanced out of polling. By default, a socket.io connection starts with http polling and after a bit of negotiation with the server, it attempts to establish a webSocket connection. If that succeeds, it stops doing the polling and uses only the webSocket connection. If the the webSocket connection fails, it just keeps doing the polling.
Here are some reasons that can happen:
You have a mismatched version of socket.io in client and server.
You have some piece of infrastructure (proxy, firewall, load balancer, etc...) in between client and server that is not letting webSocket connections through.
You've attached more than one socket.io server handler to the same web server. You can't do that as the communication will get really messed up as multiple server handlers try to respond to the same client.
As a test, you could force the client to connect only with webSocket (no polling at all to start) and see if the connection fails:
let socket = io(yourURL, {transports: ["websocket"]})'
socket.on('connect', () => {console.log("connected"});
socket.on('connect_error', (e) => {console.log("connect error: ", e});
socket.on('connect_timeout', (e) => {console.log("connect timeout: ", e});

Jest test WebSocket client API

Context :
I have a WebSocket server program (.exe build with C#) and i want to test my request with a NodeJs client (in production mode, it will be NodeJs client app which going to use the "API") I use jest to test it
My test code :
const WebSocket = require('ws');
test('Extension Connection test', async ()=>{
var ws = new WebSocket("ws://127.0.0.1:2031/Extension");
await new Promise((resolve, reject)=>{
ws.onmessage = function (message) {
var responseAttended = {"type":"Information","data":{"information":"Success connection as Extension"},"target":"Extension"};
assert(message.data, responseAttended);
resolve();
};
ws.onopen = function () {
var request = {target: "ExtensionService", type: "Auth", data: {name: "extension"}};
ws.send(JSON.stringify(request));
};
ws.onerror = function () {
assert(false);
reject();
}
});
});
I started my server before launch test, my server catch the value send (so websocket is connected). My server did some stuff and send back the response, but it's looks like this one never arrived in my NodeJs test client
As no response arrived, the promise is not resolved and after 5s the jest timeout stop the test.
Any idea what i made bad ?
I think you can use jest.setTimeout(millisecond) before use test(), it work for me
jest.setTimeout(10000);
test('Extension Connection test', async () => {
// TODO your code
})

socket io and mqtt nodejs duplicate entry

I am using mqttjs and socketio on my nodejs backend.
I am using angular as my frontend framework.
On my frontend there are 3 routes.
All requires socket connection for real time data.
So on ngOnInit i run client side socket io connection code and on ngOnDestroy I will run socket disconnect as well.
And in my server side code (index.js) there are mainly 3 actions that is happening.
const io = require('socket.io')(server)
mqtt.createConnection();
mqtt.mqttSubscriptions(io);
mqtt.mqttMessages(io);
These are the mqtt methods:
const createConnection = () => {
let options = {
protocol: 'mqtt',
clientId: process.env.MQTT_CLIENT_ID,
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
};
client = mqtt.connect(process.env.MQTT_HOST, options);
client.on('connect', function() {
winston.info('MQTT connected');
});
client.on('error', function(err) {
winston.error(err);
});
};
const mqttSubscriptions = io => {
winston.info(`Socket connected.`);
client.subscribe([TOPICS.DATA], function(error, granted) {
if (error) {
winston.error(error);
}
winston.info('Topics: ', granted);
});
};
const mqttMessages = io => {
io.sockets.on('connection', socket => {
winston.info(`Socket connected.`);
client.on('message', function(topic, message) {
let payload = JSON.parse(message.toString());
winston.info(topic);
winston.info(payload.id);
switch (topic) {
case TOPICS.DATA:
dataController.storeData(payload, io);
break;
default:
winston.error('Wrong topic');
break;
}
});
});
};
And on the datacontroller I am running
socket.emit()
My problem is everytime I navigate to a route and come back the dataController.storeData is called multiple times.
That is when I am at route A, and then navigate to route B and then back to A and then to C, the data is multiplied that many times of my route navigation. (In this case 4 times.)
I found that it is socket io and mqtt connection problem, but I don't know how to solve, since I am new to both of these.
Any help?

Node.js - Socket.io-client does not emit data

I'm trying to build a simple socket.io-client using nodejs, but I'm facing a trouble...
I'm connecting with the socket.io (server), but I can't emit any data. Follow bellow my simple code:
Client Side:
var socketIO = require('socket.io-client')('http://serverdns:3000');
socketIO.on("dashboard", (data) => {
console.log(data);
});
socketIO.on('connect', function(){
console.log("Connected with the translator service.");
socketIO.emit('dashboard', 'teste');
});
socketIO.on('disconnect', function(){
console.log("Disconnected from the translator service");
});
socketIO.on('error', function(err){
console.log(err);
});
Socket.io version: 2.1.1 (I've tried to use old versions but the same problem happens).
The connect event works, the log "Connected with the translator service." is generated, but emit does not work.
Server side:
var server = require('http').createServer();
var ioServer = require('socket.io')(server, { pingInterval: 2000, pingTimeout: 60000, cookie: false });
class SocketServer {
constructor() {
var self = this;
ioServer.on('connection', function (client) {
console.log('[SOCKETIO] AVAILABLE');
client.on('main', self.main);
client.on('disconnect', self.disconnect);
});
server.listen(3000);
}
getSocket(){
return ioServer;
}
main(data) {
console.log(data);
}
disconnect() {
console.log("[SOCKETIO] DISCONNECTED");
}
}
module.exports = new SocketServer();
Anyone can help me?
Are there anything I'm not seeing?
Thanks a lot.
Right now you are emitting to the event dashboard from client. But on the server side you have no code that is handling that event. You are currently logging the event main which does not match with what you're emitting. Try client.on('dashboard', self.dashboard). Make your own dashboard function.

Use socket.io-client with Express and nodejs to send query to java server

I have a web app built upon Express. The nodejs backend is using a java server to perform some heavy operations. The dialogue between Express and the java server is done using socketio. The nodejs server is the client and uses socket.io-client to send queries to the java server. The javaserver is based upon netty-socketio.
Here is what I am doing in my nodejs app:
var io = require('socket.io-client')
var socket = io.connect('http://localhost:8080');
socket.on('connect', function () {
console.log('0 Connected!');
socket.emit('myEvent', ['0' ,'here is the query'], function (data) {
console.log('\tSending query ... waiting for ACK0');
console.log(data);
});
socket.on('serverResponse', function (data) {
console.log('\tserverResponse event triggered, data:');
console.log(data);
});
});
When calling this script outside my web app everything is working like a charm, but when I call this code from express my client fails to connect (I don't reach the '0 Connected!' line). There are no error messages.
The weird part is that if I am first running my web app, throwing a query, and then start my java server, the client connects to the java server and everything is working (for that query only). Any clues on how to fix that ?
EDIT 1
Here is a schema of what I am trying to achieve:
client javascript backend java server
via browser <---> node/Express/socketio-client <---> netty-socketio
#client's machine | #my server | #my server (the same)
| |
myDNS:80 localhost:8080
More precisions on the java server. Here is the squeleton:
public class App {
public static void main(String[] args) throws InterruptedException, UnsupportedEncodingException {
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(8080);
final SocketIOServer server = new SocketIOServer(config);
server.addEventListener("myEvent", String[].class, new DataListener<String[]>() {
#Override
public void onData(final SocketIOClient client, String[] data, final AckRequest ackRequest) {
//Id of the client
String id = data[0];
//Acknowledge the request:
ackRequest.sendAckData("ACK_"+id);
//doing some calculations ...
// ... ... ...
// ... ... ...
client.sendEvent("serverResponse", new VoidAckCallback(){
#Override
protected void onSuccess() {}
}, "I am the answer from the server");
}
});
server.start();
System.out.println("[JAVA SERVER INFO] Java server started.");
Thread.sleep(60000*3);//Integer.MAX_VALUE);
server.stop();
System.out.println("[JAVA SERVER INFO] Java server stopped.");
}
}
My web app nodejs backend and my java server are running on the same machine, the communication with socket.io is done via localhost:8080. Once again, the weird thing is that the client's script is working when used outside the express framework, this let me think it might be a compatibility problem between socket.io-client and Express.
EDIT 2
I modified my socket.io-client code to see with more details what is happening, I added:
socket.on('connect_error', function(err){
console.log(err);
});
socket.on('connect_timeout', function(){
console.log("connect_timeout");
});
socket.on('reconnect_attempt', function(){
console.log("reconnect_attempt");
});
When I run the client with the java server switched off, I get a 'connect_error' event. When the java server is on I get no message at all. It seems the connection is neither failing nor successful, nothing happen ... Any idea on how to debug this better ?
EDIT 3
Here is the code I am using to handle a request from the browser:
index.js:
var express = require('express');
var router = express.Router();
var controller = require('../controllers/myController.js');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
router.post('/api/getProcessedData', function(req, res, next){
var text = req.body.text;
controller.get_processed_data(text, res);
});
myController.js:
var socket = require('socket.io-client')('http://localhost:8080');
module.exports.get_processed_data = function(text, res) {
var timestamp = new Date().getTime();
console.log('starting client');
socket.on('connect', function () {
console.log("client connected.");
socket.emit('myEvent', [timestamp ,text], function (data) {
console.log('\tSending query ... waiting for ACK');
console.log(data);
});
socket.on('serverResponse', function (data) {
console.log('\tserverResponse' event trigged, data:');
res.send(data);
});
});
socket.on('connect_error', function(err){
console.log(err);
});
socket.on('connect_timeout', function(){
console.log("connect_timeout");
});
socket.on('reconnect_attempt', function(){
console.log("reconnect_attempt");
});
socket.on('reconnecting', function(){
console.log("reconnecting");
});
}
The structure of your controller is a bit messed up. Here are some things that are wrong:
You connect to the Java server when the module is loaded, but you don't assign a connect event handler until the route gets hit. This means you will normally miss the connect event except when the server isn't yet running. So, this entirely explains what you observe. If the java server is already up when you start your Express server, you miss the connect event so you never execute any of the logic in your get_processed_data() function.
You install a new connect handler every time the route is hit which means you will get multiple event handlers assigned, though because of the first issue, none of them will likely get hit.
If you want the socket.io connection to be continually connected, this would be one way to rewrite the controller:
var socket = require('socket.io-client')('http://localhost:8080');
socket.on('connect', function () {
console.log("client connected.");
});
socket.on('connect_error', function(err){
console.log(err);
});
socket.on('connect_timeout', function(){
console.log("connect_timeout");
});
socket.on('reconnect_attempt', function(){
console.log("reconnect_attempt");
});
socket.on('reconnecting', function(){
console.log("reconnecting");
});
var transactionCntr = 0;
module.exports.get_processed_data = function(text, res) {
var timestamp = new Date().getTime();
var transactionId = transactionCntr++;
console.log('sending data to client');
function onResponse(data) {
// for concurrency reasons, make sure this is the right
// response. The server must return the same
// transactionId that it was sent
if (data.transactionId === transactionId) {
console.log('\tserverResponse' event trigged, data:');
res.send(data);
socket.off('serverResponse', onResponse);
}
}
socket.on('serverResponse', onResponse);
// send data and transactionId
socket.emit('myEvent', [timestamp ,text, transactionId], function (data) {
console.log('\tSending query ... waiting for ACK');
console.log(data);
});
}
Your current structure has an issue in that it does not appear to have a way to determine which response goes with which request and can have concurrency issues. It would be simpler to just use a separate http request each time because then the response would be uniquely paired with the appropriate request.
With your socket.io connection, you could use some sort of ID in your request/response so you can tell which response belongs to which request. I've shown how that would work in the express server. From your Java server, you would have to echo the transactionId back in the response to the Express server so it can track which response goes with which request.
As your code was, if multiple requests for the '/api/getProcessedData' route are in play at the same time, the responses from the different requests could easily get mixed up. This is an architectural problem of the way you're doing things.
I'm no Java expert, but it looks to me like this line:
Thread.sleep(60000*3);
will sleep your thread for 180,000 milliseconds (3 minutes) and then right after that your code calls server.stop(). So, your Java server shuts itself down after 3 minutes.
So, thus you could only connect to your Java server within the first 3 minutes after you started it.
The logical question here is why are you stopping your server at all?

Resources