daniegarcia254
daniegarcia254

Reputation: 1207

Tests don't finish because of pending MongoDB connections (@hapi/lab, mongodb-memory-server, node)

I'm having this issue, all test pass successfully, but in the end, the process never ends. And what I can see is that there are always two mongod process instances running.

I'm using mongodb-memory-server library to mock the database connections, last versions of mongodb and hapijs environment.

const { expect } = require ('@hapi/code');
const lab = exports.lab = require ('@hapi/lab').script ();
const { it, describe, after, before } = lab;
const Sinon = require ('sinon');
const DAO = require ('./index');
const CaseBaseDao = require ('case-base-dao'); //from private npm repo

let baseDaoFindStub;
let updateOneStub;

describe ('Profiles DAO', () =>
{
    before (async ({ context }) =>
    {
        const { MongoClient } = require ('mongodb');
        const { MongoMemoryServer } = require ('mongodb-memory-server');
        const mongoServerInstance = new MongoMemoryServer ({ instance: { port: 27018, dbName: 'administration' } });

        const url = await mongoServerInstance.getConnectionString ();
        const dbClient = await MongoClient.connect (url);
        const collection = await dbClient.db ().createCollection ('profiles');
        await collection.insertMany (require ('./examples/profiles.json'));
        const dao = DAO () (dbClient.db ());

        context.dbClient = dbClient;
        context.mongoServerInstance = mongoServerInstance;
        context.sandbox = Sinon.createSandbox ();
        context.dao = dao;

        baseDaoFindStub = context.sandbox.stub (CaseBaseDao.BaseDao.prototype, 'find').resolves ();
        updateOneStub = context.sandbox.stub (dao.collection, 'updateOne').resolves ();
    });

    after (async ({ context: { sandbox, dbClient, mongoServerInstance } }) =>
    {
        await dbClient.close (); // only added for debug purposes
        await mongoServerInstance.stop (); // only added for debug purposes
        sandbox.restore ();
    });

    it ('Should expose DAO methods', ({ context: { dao } }) =>
    {
        expect (dao).to.be.an.object ();
        expect (dao.findByEmail).to.exist ();
        expect (dao.find).to.exist ();
        expect (dao.findByQuery).to.exist ();
        expect (dao.updateOneById).to.exist ();
        expect (dao.addSmartPerformanceDashboards).to.exist ();
        expect (dao.deleteSmartPerformanceDashboards).to.exist ();
        expect (dao.updateSmartPerformanceDashboards).to.exist ();
    });
});

There are a lot more of tests, but if I only execute this one, it gets stucked at the end.

And if I try to close connection manually in the after block, these are the errors that I get:

For the await dbClient.close()

message:"Illegal invocation"

stack:"TypeError: Illegal invocation\n at handleWriteReq (internal/stream_base_commons.js:46:26)\n at writeGeneric (internal/stream_base_commons.js:139:15)\n at Socket._writeGeneric (net.js:771:11)\n at Socket._write (net.js:783:8)\n at doWrite (_stream_writable.js:431:12)\n at writeOrBuffer (_stream_writable.js:415:5)\n at Socket.Writable.write (_stream_writable.js:305:11)\n at Connection.write ({path}/node_modules/mongodb/lib/core/connection/connection.js:265:21)\n at {path}/node_modules/mongodb/lib/core/connection/pool.js:1208:40\n at checkStatus ({path}/node_modules/mongodb/lib/core/connection/pool.js:732:21)\n at Pool.destroy ({path}/node_modules/mongodb/lib/core/connection/pool.js:739:3)\n at Server.destroy ({path}/node_modules/mongodb/lib/core/topologies/server.js:897:15)\n ...

For the await mongoServerInstance.stop ();

message:"Method Promise.prototype.then called on incompatible receiver #"

stack:"TypeError: Method Promise.prototype.then called on incompatible receiver #\n at Promise.then ()"

I've tried almost everything: change ports, use promises instead of async/await, avoid the use of Sinon and stubs (just in case), etc.. and I'm running out of ideas.

I'm using this exact same code to mock databases in other projects, and it works just fine, without any need of close the database connections manually, it just does automatically.

This is happening in my local environment as well as it does in the Bitbucket pipelines, so, discarded to be something related to my computer.

Upvotes: 0

Views: 1092

Answers (1)

daniegarcia254
daniegarcia254

Reputation: 1207

Finally solved it.

Not sure why this happens, but the solution was not to close MongoDB connections using the mongo instance related vars through @hapi/lab context, but defining them outside the before block. So:

let baseDaoFindStub;
let updateOneStub;
let mongoServerInstance;  // Now defined here
let dbClient;             // Now defined here

describe ('Profiles DAO', () =>
{
    before (async ({ context }) =>
    {
        const { MongoClient } = require ('mongodb');
        const { MongoMemoryServer } = require ('mongodb-memory-server');
        mongoServerInstance = new MongoMemoryServer ({ instance: { port: 27018, dbName: 'administration' } });

        const url = await mongoServerInstance.getConnectionString ();
        dbClient = await MongoClient.connect (url);
        const collection = await dbClient.db ().createCollection ('profiles');
        await collection.insertMany (req
...
...
...

after (async ({ context: { sandbox } }) =>  // Don't get vars from context
    {
        sandbox.restore ();
        await dbClient.close ();
        await mongoServerInstance.stop ();
    });

Not the first time I have this kind of problems using @hapi/lab context, so, be careful with it!

Upvotes: 1

Related Questions