0

I have the following TypeScript and nodeJS file which registers a series of socket.io namespaces. I then have some mocha tests which test those namespaces, however, my first test always fails, unless I am live reloading, in which case the second time around the tests will pass. NB: the db.Line.findAll() and similar functions are sequelize functions returning promises.

export class RealTimeStations {
    public complete: Promise<boolean>;
    server: any;

    constructor(io: any) {
        this.server = io;
        this.complete = Promise.resolve(db.Line.findAll()).then(lines => {
                // do some mapping to generate routes and cells
                this.registerEndPoints(routes, [], cells);
            }).catch(err => console.log(err))
    }

    registerEndPoints(routes: Array<string>, nsps: Array<any>, cells: Array<string>) {
        for (let i = 0; i < routes.length; i++) {
            nsps.push(this.server.of('/api/testNamespace/' + routes[i]));
            let that = this;
            nsp.on('connection', function (socket) {
                that.emitInitialPackage(nsps[i], routes[i], cells[i]);
            });
        }
    }

    emitInitialPackage(nsp: any, name: string, cell: any) {
        return db.Line.find({/* Some sequelize params*/}).then(results => {
            nsp.emit('value', results);
        }).catch(err => console.log(err));
    }
}

I have seen this problem before, where the socketio setup wasn't being completed before the test executed, hence I moved to the promise based approach, however, this test now fails first time around but then on 2nd and subsequent livereloads, it passes...

describe('Stations Socket /api/getLine/stations', function () {
    beforeEach(function (done) {
        models.sequelize.sync({ force: true }).then(function () { //setup SQLite db
            sequelize_fixtures.loadFile('C:/Users/George/Source/Repos/Coty%20Monitoring%20System/server/seeders/default.json', db) //seed the db
                .then(function () {
                    app.set('port', '3000');
                    server = http.createServer(app);
                    server.listen('3000', function () { });
                    var io = require('socket.io').listen(server);
                    new RealTimeStations(io).complete.then(() => { //config socketio namespaces
                        socket = require('socket.io-client')('http://localhost:3000/api/testNamespace/2');
                        done();
                    });
                });
        })
    })
    describe('Setup', () => {
        it('Should connect and receive correct state', function (done) {
            socket.on('connect', function () {
                socket.on('value', function (test) {
                    test.stations.should.equal('"expected value"');
                    done();
                });
            });
        });
    });

    afterEach(function () {
        socket.disconnect();
        server.close();
        socket = null;
    })
})

Update:

The issue is that the nsp.emit('value', results); line doesn't get executed before the complete promise resolves. This means that when the test begins and connects to the server, the value event isn't triggered, and as the connect event is only sent once, the test just timeouts.

So I need a way of linking the complete promise to the emitInitialPackage promise.

George Edwards
  • 8,979
  • 20
  • 78
  • 161
  • @KevinB Thanks - I have modified my code, but as you noted, I still have the problem, see my updated code. – George Edwards Feb 22 '17 at 17:49
  • At the very least, fix the loop counter issue in `registerEndPoints` ... `for (let i=0; ...)` not `for (var i=0; ...)`. – Roamer-1888 Feb 22 '17 at 18:35
  • @Roamer-1888 Done! - although am still facing the same issue – George Edwards Feb 22 '17 at 19:02
  • 1
    so... where is it failing? how is it failing? why is it failing? – Kevin B Feb 22 '17 at 19:10
  • @KevinB I believe the namespace registering logic (first code block) isn't all being completed within the promise completing, so when the test comes around, the socket hasn't been configured correctly, so when the test is waiting for the 'value' event, it times out – George Edwards Feb 22 '17 at 19:12
  • is what is waiting for it to complete properly waiting? Are you sure it is actually completing? (your catch will convert a rejected promise to a resolved one) – Kevin B Feb 22 '17 at 19:15
  • In the test, take a look at `socket`. The inner `describe` relies on something asynchrounous to have successfully completed before `socket` becomes available. I'm not sure it's valid but you could try localizing `socket` (and `server`), and moving the inner `describe` between `let socket = ...;` and `done();`. – Roamer-1888 Feb 22 '17 at 19:17
  • Why do you even have a `complete` promise but never wait for it anywhere? – Bergi Feb 22 '17 at 19:47
  • (Hint: you [shouldn't use (need) that `complete` promise](http://stackoverflow.com/q/24398699/1048572) at all) – Bergi Feb 22 '17 at 19:48
  • @Bergi I am waiting for it in the `new RealTimeStations(io).complete.then(() => {` line of the test – George Edwards Feb 23 '17 at 10:01
  • @GeorgeEdwards Ah, I see now. What exactly is the error you get when it fails, and what could be the reason? Would that error happen when `registerEndPoint` was not yet called? Or maybe you should wait for the `nsp.on('connection', …)` events? – Bergi Feb 23 '17 at 22:45
  • @Bergi my tests timeout, so what I think is happening is - the `beforeEach` event completes (complete promise) before the `emitInitialPackage()` function has completed (as this is a promise in itself), so the test beigns and the connection event is fired, but as the `emitInitialPackage()` function still hasn't been completed, the `nsp.emit('value', results);` line doesn't get hit, so by the time the initial package func is completed, the connection has already been sent, hence the timeout – George Edwards Feb 27 '17 at 15:03
  • @KevinB Yes, I think the issue is that my `emitInitialPackage()` takes a while and isn't completed when my `complete` promise completes correctly – George Edwards Feb 27 '17 at 15:05
  • @Roamer-1888 Thanks, If you see some of the comments above, I think we have isolated the problem, but I am not sure how to fix it... I need `emitInitialPackage()` to complete before the `complete` promise is resolved. – George Edwards Feb 27 '17 at 15:06
  • Seems like there's a gap in the promise logic that leads to resolution of `complete`. At the top level, `this.complete = Promise.resolve(db.Line.findAll())` is fine but there's no attempt to allow the low level promises returned by `emitInitialPackage()` to be assimilated at the top level. In the middle, `registerEndPoints()` is a promise-free black hole. At the heart of `registerEndPoints()`, you can promisify `nsp.on('connection' ...);` (which is presumably a one-off event per nsp) and chain calls to `emitInitialPackage()`. You will also need a `Promise.all(...)` , and appropriate returns. – Roamer-1888 Feb 28 '17 at 03:09

0 Answers0