1
votes

TLDR: My api works fine with curl and postman, but fails in test with the error "Cannot set headers after they are sent to the client" when using chai-http in one put request(all other put requests work). I have a generic update method for all my collections(attached below), which works for the other update/put requests. Thanks for your help in advanced.

Mocha & Chai Test for the clients collection

process.env.NODE_ENV = 'test';

const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const c_paths = require('../constants/paths.js');
const app = require('../app.js');
chai.use(chaiHttp);

describe(c_paths.clients, () => {
it('It should create a new clients document', done => {
    let req = {
    test: true,
    data: [
        {
        name: 'client 1',
        contacts: [
            {
            name: 'client name 1',
            mobile: 'client mobile 1',
            email: '[email protected]',
            },
        ],
        address: ['client address 1'],
        },
    ],
    };
    chai
    .request(app)
    .post(c_paths.clients)
    .send(req)
    .end((err, res) => {
        expect(res).to.have.status(200);
        expect(res.body.insertedCount).is.equal(
        1,
        'Number of inserted documents.',
        );
        expect(res.body.ops[0].name).is.equal(req.data[0].name);
        expect(res.body.ops[0].contacts[0].name).is.equal(
        req.data[0].contacts[0].name,
        );
        expect(res.body.ops[0].contacts[0].mobile).is.equal(
        req.data[0].contacts[0].mobile,
        );
        expect(res.body.ops[0].contacts[0].email).is.equal(
        req.data[0].contacts[0].email,
        );
        expect(res.body.ops[0].address[0]).is.equal(req.data[0].address[0]);
        expect(res.body.ops[0].pause_updates).is.false;
        expect(res.body.ops[0].pause_updates_toggle).is.false;
        done();
    });
});

it.('It should add a reccuring job', done => {
    let req = {
    test: true,
    filter: {
        name: 'client 1',
    },
    set: {
        recurring_jobs: [
        {
            job_id: 'job id 1',
            current_employee_id: 'employee id 1',
            employee_scheduled_till: 'Apr 30 2019',
            notes: ['note 1'],
            duties: ['duty 1'],
            repeat_in_days: 7,
            time: '11:00:00', // Validation error switched to 10000
            start_date: 'Mar 01 2019',
            end_date: 'May 30 2019',
            is_active: true,
        },
        ],
    },
    };
    chai
    .request(app)
    .put(c_paths.clients)
    .send(req)
    .end((err, res) => {
    expect(res).to.have.status(200);
    expect(res.body.ok).is.equal(1);
    done();
    }); 
});

it('It should delete one document', done => {
    let req = {
    test: true,
    filter: {
        name: 'client 1',
    },
    };
    chai
    .request(app)
    .delete(c_paths.clients)
    .send(req)
    .end((err, res) => {
        expect(res).to.have.status(200);
        expect(res.body.n).is.equal(1, 'Number of documents removed');
        done();
    });
});
});

Update method in ./routes/crud.js. The error on line 131 is res.status(200)send(result);

function update(req, res, dbName, collectionName, joiSchema) {
var toValidate = '[';
var data = '{';
if (req.body.set) {
    toValidate = toValidate.concat(JSON.stringify(req.body.set), ',');
    data = data.concat('"$set":', JSON.stringify(req.body.set), ',');
}
if (req.body.inc) {
    toValidate = toValidate.concat(JSON.stringify(req.body.inc), ',');
    data = data.concat('"$inc":', JSON.stringify(req.body.inc), ',');
}
if (req.body.addToSet) {
    let temp = JSON.stringify(req.body.addToSet);
    temp = temp.replace('{"$each":[', '[');
    temp = temp.slice(0, -1); // Removes $each for validation
    toValidate = toValidate.concat(temp, ',');
    data = data.concat('"$addToSet":', JSON.stringify(req.body.addToSet), ',');
}
if (req.body.unset) {
    data = data.concat('"$unset":', JSON.stringify(req.body.unset), ',');
}
if (req.body.currentDate) {
    toValidate = toValidate.concat(JSON.stringify(req.body.currentDate), ',');
    data = data.concat(
    '"$currentDate":',
    JSON.stringify(req.body.currentDate),
    ',',
    );
}
if (req.body.rename) {
    data = data.concat('"$rename":', JSON.stringify(req.body.rename), ',');
}
if (req.body.pullAll) {
    toValidate = toValidate.concat(JSON.stringify(req.body.pullAll), ',');
    data = data.concat('"$pullAll":', JSON.stringify(req.body.pullAll), ',');
}
toValidate = toValidate.slice(0, -1); // Removes trailing comma
data = data.slice(0, -1);
toValidate = toValidate.concat(']');
data = data.concat('}');
Joi.validate(toValidate, joiSchema, function(err, value) {
    if (err) {
    res.send(err);
    return;
    }
});
MongoClient.connect(c_db.url, function(err, db) {
    if (err) {
    res.send(err);
    db.close();
    return;
    }
    const dbo = db.db(dbName);
    const filter = req.body.filter == null ? '' : req.body.filter;
    dbo
    .collection(collectionName)
    .updateMany(filter, JSON.parse(data), function(err, result) {
        if (err) {
        res.send(err);
        db.close();
        return;
        }
        res.status(200).send(result); //Error Line 131
        db.close();
    });
});
};

Error response

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (_http_outgoing.js:482:11) at ServerResponse.header (./App/node_modules/express/lib/response.js:767:10) at ServerResponse.send (./App/node_modules/express/lib/response.js:170:12) at ServerResponse.json (./App/node_modules/express/lib/response.js:267:15) at ServerResponse.send (./App/node_modules/express/lib/response.js:158:21) at ./App/routes/crud.js:131:25 at result (./App/node_modules/mongodb/lib/utils.js:414:17) at session.endSession (./App/node_modules/mongodb/lib/utils.js:401:11) at ClientSession.endSession (./App/node_modules/mongodb-core/lib/sessions.js:129:41) at executeCallback (./App/node_modules/mongodb/lib/utils.js:397:17)

1
you'll normally see an error such as Cannot set headers after they are sent to the client when you're firing the request twice in a row. I would suggest looking through the expectations and seeing if you're doing just thatDreamlines
Your callstack is doing send, json, send, head, setHeader, This comes back to line 131 of your ./App/routes/crud.js, So what does that show?Keith
@Keith, I have edited the question to show the error line, for convenience here is the error line in ./App/routes/crud.js in update function in the chai.request.end I call res.status(200).send(result); done(); also the same method is used in updating other collections and those test pass without an issue.Andre Fernando

1 Answers

0
votes

It was a Joi validation error, to figure it out I added JSON.stringify(req.body) to the expect error or console.log, i.e expect(res.body.n, JSON.stringify(req.body).is.equal(1);

This gave me the validation error in Mocha testing, once fixed it worked properly.