HEX
Server: nginx/1.24.0
System: Linux nowruzgan 6.8.0-57-generic #59-Ubuntu SMP PREEMPT_DYNAMIC Sat Mar 15 17:40:59 UTC 2025 x86_64
User: babak (1000)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/dev/nowruzgan/rest/node_modules/sails-hook-sockets/test/with-redis.test.js
/**
 * Module dependencies
 */

var util = require('util');
var assert = require('assert');
var _ = require('@sailshq/lodash');
var async = require('async');
var Sails = require('sails').Sails;
var SocketIORedisAdapter = require('socket.io-redis');
var lifecycle = require('./helpers/lifecycle.helper');

// TODO:
// figure out how to make this run on Travis
// (need a local redis)


describe('with redis', function (){

  before(lifecycle.setup);
  after(lifecycle.teardown);

  // Common app config
  var appConfig = {
    log: { level: 'warn' },

    globals: false,

    hooks: {
      // Inject the sockets hook in this repo into this Sails app
      sockets: require('../')
    },

    loadHooks: ['moduleloader', 'userconfig', 'http', 'sockets'],

    // Configure the socket.io-redis adapter
    sockets: {
      adapter: 'socket.io-redis',
      adapterModule: SocketIORedisAdapter,

      // Configure port to match .travis.yml
      port: 6380,

      // Test advanced redis config: (will cause sockets hook to build raw redis clients):
      //
      // Configure password to match .travis.yml
      pass: 'secret',
      db: 2
    },

    routes: {
      'GET /id': function(req, res) {
        res.send(req.socket.id);
      },
      // A test route which joins a room
      'PUT /testroom/join': function (req, res){
        req._sails.sockets.join(req, 'testroom', function() {
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /funRoom/join': function (req, res){
        req._sails.sockets.join(req.param('socketId'), 'funRoom', function() {
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /funRoom/leave': function (req, res){
        req._sails.sockets.leave(req.param('socketId'), 'funRoom', function() {
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /awesomeRoom/joinByRoom': function (req, res){
        req._sails.sockets.addRoomMembersToRooms(req.param('socketId'), 'awesomeRoom', function() {
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /awesomeRoom/leaveByRoom': function (req, res){
        req._sails.sockets.removeRoomMembersFromRooms(req.param('socketId'), 'awesomeRoom', function() {
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      // A test route which blasts a message
      'POST /blast': function (req, res){
        var msg = req.param('msg') || 'HI! HI HI! HI HI!';
        var eventName = req.param('event') || 'blasted';
        req._sails.sockets.blast(eventName, {msg: msg});
        return res.send();
      },

      // A test route which broadcasts a message to a room
      'POST /broadcast': function (req, res){
        var room = req.param('room') || 'testroom';
        var msg = req.param('msg') || 'HI! HI HI! HI HI!';
        var eventName = req.param('event') || 'message';
        req._sails.sockets.broadcast(room, eventName, {msg: msg});
        return res.send();
      },

      'PUT /testroom/joinPlayroom': function (req, res) {
        req._sails.sockets.addRoomMembersToRooms('testroom', 'playroom', function(err) {
          if (err) {return res.send(err);}
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /testroom/leavePlayroom': function (req, res) {
        req._sails.sockets.removeRoomMembersFromRooms('testroom', 'playroom', function(err) {
          if (err) {return res.send(err);}
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      },

      'PUT /leaveAllRooms': function (req, res) {
        req._sails.sockets.leaveAll('testroom', function(err) {
          if (err) {return res.send(err);}
          // Add slight delay to allow all servers to react
          setTimeout(function(){res.send();}, 100);
        });
      }


    }
  };

  // New up five instances of Sails which share the config above
  // and lift all of them (only difference is their port)
  var apps = [];
  var ports = [1600, 1601, 1602, 1603, 1604];
  before(function (done){
    async.each(ports, function (port, next){
      var app = Sails();
      apps.push(app);
      app.lift(_.extend(appConfig, {port: port}),next);
    }, done);
  });
  after(function (done){
    async.each(apps, function (app, next){
      app.lower(next);
    }, done);
  });

  describe('all apps', function (){

    describe('after each app has at least one socket connected', function (){

      var sockets = [];
      var socketIds = [];
      before(function (done){
        async.eachSeries(apps, function (app, next){
          var socket = io.sails.connect('http://localhost:'+app.config.port, {
            // This prevents multiple socket.io clients attempting to share
            // the same global socket (which would render this test meaningless)
            multiplex: false
          });
          sockets.push(socket);
          socket.on('connect', function(){
            socket.get('/id', function(data) {
              socketIds.push(data);
              return next();
            });
          });
        }, done);
      });
      after(function (done){
        async.each(sockets, function (socket, next){
          socket.on('disconnect', function (){ next(); });
          socket.disconnect();
        }, done);
      });

      //||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
      //
      // Note:
      //  In a multi-instance scenario, the load balancer should take care of
      //  connecting the socket to the appropriate Sails server.  This way,
      //  `beforeConnect` (and the legacy `onConnect` event handler) will only
      //  be triggered once.  This is difficult to test here w/o simulating a
      //  load balancer (which would be hand-wavy at best anyways).
      //
      //||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

      describe('when all sockets listen for the `blasted` event', function (){

        before(function (done){
          async.each(sockets, function (socket, next){
            socket.on('blasted', function (event){
              socket._receivedBlastEvents = socket._receivedBlastEvents || [];
              socket._receivedBlastEvents.push(event);
            });
            return next();
          }, done);
        });

        describe('and then one socket blasts a message', function (){

          before(function (done){
            var oneSocket = sockets[0];
            oneSocket.post('/blast', function (data, jwr){
              if (jwr.error) return done(jwr.error);
              return setTimeout(done, 200);
            });
          });

          it('all connected sockets should receive the message (even though they are connected to different instances)', function (){
            _.each(sockets, function (socket){
              assert(socket._receivedBlastEvents && socket._receivedBlastEvents.length == 1, util.format('Socket connected to app on port %d did not receive the message', socket.port));
            });
          });
        });

      });

      describe('when all sockets join a room and listen for the `message` event', function (){

        before(function (done){
          async.each(sockets, function (socket, next){
            socket.on('message', function (event){
              socket._receivedMessageEvents = socket._receivedMessageEvents || [];
              socket._receivedMessageEvents.push(event);
            });
            socket.put('/testroom/join', function (data, jwr){
              if (jwr.error) return next(jwr.error);
              return next();
            });
          }, done);
        });

        describe('and then one socket broadcasts a message', function (){

          before(function (done){
            var oneSocket = sockets[0];
            oneSocket.post('/broadcast', function (data, jwr){
              if (jwr.error) return done(jwr.error);
              return setTimeout(done, 200);
            });
          });

          it('all connected sockets should receive the message (even though they are connected to different instances)', function (){
            _.each(sockets, function (socket){
              assert(socket._receivedMessageEvents && socket._receivedMessageEvents.length == 1, util.format('Socket connected to app on port %d did not receive the message', socket.port));
            });
          });
        });

      });

      describe('when one socket uses `addRoomMembersToRooms` to join all members of `testroom` to `playroom`', function (){

        before(function (done) {
          sockets[0].put('/testroom/joinPlayroom', function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return setTimeout(done, 200);
          });
        });

        describe('and all sockets listen for the `letsplay` event', function() {

          before(function (){
            _.each(sockets, function (socket){
              socket.on('letsplay', function (event){
                socket._receivedPlayEvents = socket._receivedPlayEvents || [];
                socket._receivedPlayEvents.push(event);
              });
            });
          });

          describe('and then one socket broadcasts a `letsplay` event', function (){

            before(function (done){
              var oneSocket = sockets[0];
              oneSocket.post('/broadcast', {room: 'playroom', event: 'letsplay', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('all connected sockets should receive the message (even though they are connected to different instances)', function (){
              _.each(sockets, function (socket){
                assert(socket._receivedPlayEvents && socket._receivedPlayEvents.length == 1, util.format('Socket connected to app on port %d did not receive the message', socket.port));
              });
            });
          });

        });

      });

      describe('when one socket uses `removeRoomMembersFromRooms` to remove all members of `testroom` from `playroom`', function (){

        before(function (done) {
          sockets[0].put('/testroom/leavePlayroom', function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return done();
          });
        });

        describe('and all sockets listen for the `letsplaymore` event', function() {

          before(function (){
            _.each(sockets, function (socket){
              socket.on('letsplaymore', function (event){
                socket._receivedPlayMoreEvents = socket._receivedPlayEvents || [];
                socket._receivedPlayMoreEvents.push(event);
              });
            });
          });

          describe('and then one socket broadcasts a `letsplaymore` event', function (){

            before(function (done){
              var oneSocket = sockets[0];
              oneSocket.post('/broadcast', {room: 'playroom', event: 'letsplaymore', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('connected sockets should not receive the message', function (){
              _.each(sockets, function (socket){
                assert(!socket._receivedPlayMoreEvents, util.format('Socket connected to app on port %d received the message', socket.port));
              });
            });
          });

        });

      });

      describe('when one socket uses `leaveAll` to remove all members of `testroom` from all rooms', function (){

        before(function (done) {
          sockets[0].put('/leaveAllRooms', function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return done();
          });
        });

        describe('and then one socket broadcasts a `message` event', function (){

          before(function (done){
            var oneSocket = sockets[0];
            oneSocket.post('/broadcast', function (data, jwr){
              if (jwr.error) return done(jwr.error);
              return setTimeout(done, 200);
            });
          });

          it('connected sockets should not receive the message', function (){
            _.each(sockets, function (socket){
              assert(!(socket._receivedMessageEvents && socket._receivedMessageEvents.length == 2), util.format('Socket connected to app on port %d received the message', socket.port));
            });
          });
        });

      });

      describe('when one socket uses `join` to add a socket on another server to `funRoom`', function (){

        var funRoomSocket;
        before(function (done) {
          funRoomSocket = sockets[1];
          sockets[0].put('/funRoom/join/?socketId=' + socketIds[1], function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return setTimeout(done, 200);
          });
        });

        describe('and the other socket listens for the `funRoomMsg` event', function() {

          before(function (){
            funRoomSocket.on('funRoomMsg', function (event){
              funRoomSocket._receivedFunRoomEvents = funRoomSocket._receivedFunRoomEvents || [];
              funRoomSocket._receivedFunRoomEvents.push(event);
            });
          });

          describe('and then one socket broadcasts a `funRoomMsg` event', function (){

            before(function (done){
              var oneSocket = sockets[2];
              oneSocket.post('/broadcast', {room: 'funRoom', event: 'funRoomMsg', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('the subscribed socket should receive the message', function (){
              assert(funRoomSocket._receivedFunRoomEvents && funRoomSocket._receivedFunRoomEvents.length == 1, 'Socket did not receive the fun room message!');
            });

            it('other sockets should not receive the message', function() {
              _.each(sockets, function (socket){
                if (socket == funRoomSocket) {return;}
                assert(!socket._receivedFunRoomEvents, util.format('Socket connected to app on port %d received the message', socket.port));
              });
            });

          });

        });

      });

      describe('when one socket uses `leave` to remove a socket on another server from `funRoom`', function (){

        var funRoomSocket;
        before(function (done) {
          funRoomSocket = sockets[1];
          sockets[0].put('/funRoom/leave/?socketId=' + socketIds[1], function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return setTimeout(done, 200);
          });
        });

        describe('and the other socket listens for the `noMoreFunRoomMsg` event', function() {

          before(function (){
            funRoomSocket.on('noMoreFunRoomMsg', function (event){
              funRoomSocket._receivedNoFunRoomEvents = funRoomSocket._receivedNoFunRoomEvents || [];
              funRoomSocket._receivedNoFunRoomEvents.push(event);
            });
          });

          describe('and then one socket broadcasts a `noMoreFunRoomMsg` event', function (){

            before(function (done){
              var oneSocket = sockets[2];
              oneSocket.post('/broadcast', {room: 'funRoom', event: 'noMoreFunRoomMsg', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('no sockets should not receive the message', function() {
              _.each(sockets, function (socket){
                assert(!socket._receivedNoFunRoomEvents, util.format('Socket connected to app on port %d received the message', socket.port));
              });
            });

          });

        });

      });

      describe('when one socket uses `addRoomMembersToRooms` to add a single socket on the same server to `awesomeRoom`', function (){

        var adminMessagesReceived = 0;
        function rcvdAdminMessage() {adminMessagesReceived++;}
        before(function (done) {
          _.each(apps, function(app) {
            app.on('hook:sockets:adminMessage', rcvdAdminMessage);
          });
          sockets[0].put('/awesomeRoom/joinByRoom/?socketId=' + socketIds[0], function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return setTimeout(done, 200);
          });
        });

        after(function() {
          _.each(apps, function(app) {
            app.removeListener('hook:sockets:adminMessage', rcvdAdminMessage);
          });
        });

        describe('and the socket listens for the `awesomeRoomMsg` event', function() {

          before(function (){
            sockets[0].on('awesomeRoomMsg', function (event){
              sockets[0]._receivedAwesomeRoomEvents = sockets[0]._receivedAwesomeRoomEvents || [];
              sockets[0]._receivedAwesomeRoomEvents.push(event);
            });
          });

          describe('and then one socket broadcasts a `awesomeRoomMsg` event', function (){

            before(function (done){
              var oneSocket = sockets[2];
              oneSocket.post('/broadcast', {room: 'awesomeRoom', event: 'awesomeRoomMsg', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('the subscribed socket should receive the message', function (){
              assert(sockets[0]._receivedAwesomeRoomEvents && sockets[0]._receivedAwesomeRoomEvents.length == 1, 'Socket did not receive the fun room message!');
            });

            it('other sockets should not receive the message', function() {
              _.each(sockets, function (socket){
                if (socket == sockets[0]) {return;}
                assert(!socket._receivedAwesomeRoomEvents, util.format('Socket connected to app on port %d received the message', socket.port));
              });
            });

            it('no app should have received an admin message', function() {
              assert.equal(adminMessagesReceived, 0);
            });

          });

        });

      });

      describe('when one socket uses `removeRoomMembersFromRooms` to remove a single socket on the same server from `awesomeRoom`', function (){

        var adminMessagesReceived = 0;
        function rcvdAdminMessage() {adminMessagesReceived++;}
        before(function (done) {
          _.each(apps, function(app) {
            app.on('hook:sockets:adminMessage', rcvdAdminMessage);
          });
          sockets[0].put('/awesomeRoom/leaveByRoom/?socketId=' + socketIds[0], function (data, jwr) {
            if (jwr.error) {return done(jwr.error);}
            return setTimeout(done, 200);
          });
        });

        after(function() {
          _.each(apps, function(app) {
            app.removeListener('hook:sockets:adminMessage', rcvdAdminMessage);
          });
        });

        describe('and the socket listens for the `awesomeNoMoreRoomMsg` event', function() {

          before(function (){
            sockets[0].on('awesomeNoMoreRoomMsg', function (event){
              sockets[0]._receivedAwesomeNoMoreRoomEvents = sockets[0]._receivedAwesomeNoMoreRoomEvents || [];
              sockets[0]._receivedAwesomeNoMoreRoomEvents.push(event);
            });
          });

          describe('and then one socket broadcasts a `awesomeNoMoreRoomMsg` event', function (){

            before(function (done){
              var oneSocket = sockets[2];
              oneSocket.post('/broadcast', {room: 'awesomeRoom', event: 'awesomeNoMoreRoomMsg', msg: 'PLAYTIME!'}, function (data, jwr){
                if (jwr.error) return done(jwr.error);
                return setTimeout(done, 200);
              });
            });

            it('no sockets should receive the message', function() {
              _.each(sockets, function (socket){
                assert(!socket._receivedAwesomeNoMoreRoomEvents, util.format('Socket connected to app on port %d received the message', socket.port));
              });
            });

            it('no app should have received an admin message', function() {
              assert.equal(adminMessagesReceived, 0);
            });

          });

        });

      });

    });

  });


});





describe('with redis', function (){

  // Common app config
  var appConfig = {
    log: { level: 'warn' },

    globals: false,

    hooks: {
      // Inject the sockets hook in this repo into this Sails app
      sockets: require('../')
    },

    loadHooks: ['moduleloader', 'userconfig', 'http', 'sockets'],

    // Configure the socket.io-redis adapter
    sockets: {
      adapter: 'socket.io-redis',
      adapterModule: SocketIORedisAdapter,

      // Configure port to match .travis.yml
      // port: 6379,

      // Test advanced redis config: (will cause sockets hook to build raw redis clients):
      url: 'redis://:secret@localhost:6380'
      // Configure password to match .travis.yml
      // pass: 'secret',
      // db: 'sails'
    },

    routes: {
      // A test route which joins a room
      'PUT /testroom/join': function (req, res){
        req._sails.sockets.join(req, 'testroom');
        return res.send();
      },

      // A test route which broadcasts a message to a room
      'POST /testroom/broadcast': function (req, res){
        req._sails.sockets.broadcast('testroom', {msg: 'HI! HI HI! HI HI!'});
        return res.send();
      }
    }
  };

  // New up five instances of Sails which share the config above
  // and lift all of them (only difference is their port)
  var apps = [];
  var ports = [1600, 1601, 1602, 1603, 1604];
  before(function (done){
    async.each(ports, function (port, next){
      var app = Sails();
      apps.push(app);
      app.lift(_.extend(appConfig, {port: port}),next);
    }, done);
  });
  after(function (done){
    async.each(apps, function (app, next){
      app.lower(next);
    }, done);
  });

  describe('with redis url', function (){
    it('should let you connect', function (){
      // ok.
    });
  });

});