Override module loaded by require

I was using the module redact-secrets with Winston logger. This module makes sensitive data like passwords unreadable in logfiles. Very cool and handy module. It makes use of another module is-secret that contains a collection of patterns to determine what sensitive data is. One piece of sentive data was missing from is-secret: pass. I could fix it on my side, but I prefer the original GitHub project to be updated so others can also profit from it. So I submitted an issue on GitHub. While waiting for the fix I needed to continue with my development work. So I used another handy module override-require. This module overrides the resolution logic of require. So you can use it to override a dependency of a module. I used it in the following to overrule is-secret used by redact-secrets.

const overrideRequire = require('override-require');

// Check if a request needs to be overridden
const isOverride = (request) => {
return request === 'is-secret';
};

// If isOverride is true, load the module with the overridden module
const resolveRequest = (request) => {
return require('./overrule/is-secret');
};

// Initialize overide-require
const restoreOriginalModuleLoader = overrideRequire(isOverride, resolveRequest);

const { createLogger, format, transports } = require('winston');
// When redacts-secrets is loaded override-require will kickin and load our own module
const redact = require('redact-secrets')('******');
const fs = require('fs');
const path = require('path');

// Disable override require
restoreOriginalModuleLoader();

 

That’s it. Pretty cool isn’t it?

Advertisements

Testing Mongoose plugin with Jest and shared data

Grrrr just spent the better half of 2 hours trying to figure out why my Jest tests were not working for a Mongoose plugin I’m developing. It turns out that the schema configuration I was sharing between the tests was the culprit.

let Mongoose = require('mongoose');
let Schema = Mongoose.Schema;
const _ = require('lodash');
var mongooseI18n = require('../src/mongoose/mongoose-i18n-localize');

Mongoose.set('debug', true);

const simpleSchemaConfig = {
  firstName: { type: String, i18n: true, required: true },
  lastName: { type: String, i18n: false, required: true }
};

describe('Mongoose i18n localize', () => {
  beforeEach(() => {
    // Clear compiled models to avoid OverwriteModelError
    Mongoose.models = {};
  });
  
  it('should throw an exception if the locales array is not defined', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  it('should throw an exception if the locales array is an empty array', () => {
    const SimpleTestSchema = new Schema(simpleSchemaConfig);
    expect(() => {
      SimpleTestSchema.plugin(mongooseI18n, {
        locales: [],
        defaultLocale: 'nl',
        allLocalesRequired: false
      });
    }).toThrow('The required option locales array not provided or empty');
  });

  ...

});

The plugin makes changes to the schema configuration and these changes were made to the shared config. The fix I used was to make a copy of the shared config with lodash’s cloneDeep function. The only change I needed to make was the schema initialization.

const SimpleTestSchema = new Schema(_.cloneDeep(simpleSchemaConfig));

Now my tests run like a charm 🙂

Bonus

When I initially ran into the above problem, I was also initializing the schema globally. I thought that was the problem, so I moved the following statement to each test case.

const SimpleTestSchema = new Schema(simpleSchemaConfig);

That caused the exception OverwriteModelError, because I was trying to initialize a compiled model. The fix for this problem was to remove all models between test cases using beforeEach. The following code does the trick.

beforeEach(() => {
  // Clear compiled models to avoid OverwriteModelError
  Mongoose.models = {};
});

Hope this helps someone.