lundi 20 juillet 2020

Cannot set headers after they are sent to the client - Inside Chai tests

I have an in-memory Mongoose database spinning up when we start our automated tests. I know the database spin-up code is working because if I have it run with npm start when I start my API, I can connect to it and all is well.

The problem is when it tries to do it during the tests. My server.ts has some code like this:

async function connectToDatabase() {
if (process.env.IN_MEMORY_DATABASE === 'true') {
    await createInMemoryDatabase()
} else {
    mongoose.connect(config.database, {
        useFindAndModify: false,
        useUnifiedTopology: true,
    });

    app.set("db", config.database);
}}

The createInMemoryDatabase looks like this:

import createDatabaseCollections from "./createDatabaseCollections";
import mongoose from "mongoose";
import { Guid } from "core/dist/utils/Guid";

import { MongoMemoryServer } from 'mongodb-memory-server';
import { resolve } from "path";

export async function createInMemoryDatabase() {
    let mongod;
    let databseUri;

    // The next three lines are for future-proofing
    // planned deprecation of old functions.
    mongoose.set("useNewUrlParser", true);
    mongoose.set("useUnifiedTopology", true);
    mongoose.set("useCreateIndex", true);
    mongoose.set("useFindAndModify", false);
    mongoose.Promise = global.Promise;

    // This takes a number of difference parameters to make sure the database matches
    // the settings that we want so every connects nicely.
    mongod = new MongoMemoryServer({
        instance: {
            dbName: "test-db-" + Guid.newGuid(),
        },
        binary: {
            version: "3.6.2"
        }
    });

    // Once the in-memory database connection is established
    // it will run the function to generate the database collections.
    await mongod.getUri().then(async (dbUri) => {
        databseUri = dbUri;

        console.log("In-Memory Database started at " + dbUri)

        mongoose.connect(dbUri, {
            useFindAndModify: false,
            useUnifiedTopology: true,
        })

        mongoose.connection.on('error', (e) => {
            if (e.message.code === 'ETIMEDOUT') {
                console.log("Failed to start In-Memory Database: " + e);
            }
        })

        mongoose.connection.once('open', () => {
            createDatabaseCollections(mongoose.connection)
        })
    });

    resolve(databseUri);
}

This all seems to work fine as the database is created and my code to scaffold the database does what it is suppose to.

In our test file we have a before() startement that looks like this:

let requester: ChaiHttp.Agent;

    before(() => {
        return new Promise(async (complete) => {
            requester = chai.request(app).keepOpen();

            program1 = await generateValidProgramObject(true);
            user1 = await generateValidUserObject(true, Roles.User, false, program1._id);
            user2 = await generateValidUserObject(true, Roles.User, false, program1._id);


            setTimeout(() => {
                complete()
            }, 10000);
        })
    })

I know the timeout is gross but we have it there temporarily while we try to sort out this problem running the tests. The first two tests try to post a valid and invalid user. The first one is this:

it("Post a valid user", (done) => {
        user1.Roles[0].Program = programID;
        requester
            .post(`/api/user/`)
            .send(user1)
            .end((err, res) => {
                // Retrieve and save the newly posted user's ID
                const results = res.text;
                const start = results.indexOf("_id")
                const idSegment = results.substring(start + 6, results.length);
                const returnedId = idSegment.substring(0, idSegment.indexOf("\""));
                id = returnedId;
                expect(res).to.have.status(200);
                done();
            })
    })

This is where we get the problem with the error about UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I've tried to find the cause of that error and I've found a lot of solutions for things when the problem exists in the actual API call, but it doesn't happen when the API is running normally and works great. So I am trying to see how we have our tests configured improperly.

Any help would be appreciated.

Aucun commentaire:

Enregistrer un commentaire