Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mocha --watch and mongoose models

If I leave mocha watching for changes, every time I save a file mongoose throws the following error:

OverwriteModelError: Cannot overwrite Client model once compiled

I know that mongoose won't allow to define a model twice, but I don't know how to make it work with mocha --watch.

// client.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var clientSchema = new Schema({
    secret: { type: String, required: true, unique: true },
    name: String,
    description: String,
    grant_types: [String],
    created_at: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Client', clientSchema);

And here is the test

// client-test.js
var chai = require('chai');
var chaiHttp = require('chai-http');
var mongoose = require('mongoose');

var server = require('../../app');
var Client = require('../../auth/models').Client;

var should = chai.should();

chai.use(chaiHttp);

describe('client endpoints', function() {
    after(function(done) {
        mongoose.connection.close();
        done();
    });

    it('should get a single client on /auth/client/{clientId} GET', function(done) {
        var clt = new Client({
            name: 'my app name',
            description: 'super usefull and nice app',
            grant_types: ['password', 'refresh_token']
        });

        clt.save(function(err) {
            chai.request(server)
                .get('/auth/client/' + clt._id.toString())
                .end(function(err, res) {
                    res.should.have.status(200);
                    res.should.be.json;
                    res.body.should.have.property('client_id');
                    res.body.should.not.have.property('secret');
                    res.body.should.have.property('name');
                    res.body.should.have.property('description');
                    done();
                });
        });
    });
});
like image 530
nicowernli Avatar asked Oct 30 '22 21:10

nicowernli


1 Answers

I had the same issue. My solution was to check whether the model was created/compiled yet, and if not then do so, otherwise just retrieve the model.

using mongoose.modelNames() you can get an array of the names of your models. Then use .indexOf to check if the model you want to get is in the array or not. If it is not, then compile the model, for example: mongoose.model("User", UserSchema), but if it is already defined (as is the case with mocha --watch), simply retrieve the model (don't compile it again), which you can do with for example: mongoose.connection.model("User").

This is a function which returns a function to do this checking logic, which itself returns the model (either by compiling it or just retrieving it).

const mongoose = require("mongoose");
//returns a function which returns either a compiled model, or a precompiled model
//s is a String for the model name e.g. "User", and model is the mongoose Schema
function getModel(s, model) {
  return function() {
    return mongoose.modelNames().indexOf(s) === -1
      ? mongoose.model(s, model)
      : mongoose.connection.model(s);
  };
}
module.exports = getModel;

This means you have to require your model a bit differently, since you are likely replacing something like this:

module.exports = mongoose.model("User", UserSchema);

which returns the model itself, with this:

module.exports = getModel("User", UserSchema);

which returns a function to return the model, either by compiling it or just retrieving it. This means when you require the 'User' model, you would want to call the function returned by getModel:

const UserModel = require("./models/UserModel")();

I hope this helps.

like image 157
George Avatar answered Nov 11 '22 06:11

George