Node.js Configuration

Node.js Configuration > test > 2-config-test.js (source view)
Search:
 
Filters
// Change the configuration directory for testing
process.env.NODE_CONFIG_DIR = __dirname + '/config';

// Hardcode $NODE_ENV=test for testing
process.env.NODE_ENV='test';

// Test for multi-instance applications
process.env.NODE_APP_INSTANCE='3';

// Test for old style environment variable overrides
process.env.CONFIG_EnvOverride_parm__number__1 = 'overridden from test';
process.env.CONFIG_EnvOverride_parm2 = 13;

// Test $NODE_CONFIG environment and --NODE_CONFIG command line parameter
process.env.NODE_CONFIG='{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}'
process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');

// Dependencies
var vows = require('vows');
    assert = require('assert'),
    CONFIG = require('../lib/config'),
    FileSystem = require('fs'),
    originalWatchedValue = CONFIG.watchThisValue,
    newWatchedValue = Math.floor(Math.random() * 100000),
    originalDynamicArray = CONFIG.dynamicArray,
    newDynamicArray = [Math.floor(Math.random() * 100000), Math.floor(Math.random() * 100000)];

var runtimeJsonFilename = __dirname + '/config/runtime.json';
var runtimeJsonFilenameBak = runtimeJsonFilename+'.bak';

/**
 * <p>Unit tests for the node-config library.  To run type:</p>
 * <pre>npm test config</pre>
 *
 * @class ConfigTest
 */
exports.ConfigTest = vows.describe('Test suite for node-config').addBatch({
  'Library initialization': {
    'Config library is available': function() {
      assert.isObject(CONFIG);
    },
    'Config extensions are included with the library': function() {
      assert.isFunction(CONFIG._cloneDeep);
    }
  },

  'Configuration file Tests': {
    topic: function() {
      return CONFIG;
    },

    'Loading configurations from a JS module is correct': function() {
      assert.equal(CONFIG.Customers.dbHost, 'base');
      assert.equal(CONFIG.TestModule.parm1, 'value1');
    },

    'Loading configurations from a JSON file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm1, 'value1');
    },

    'Loading configurations from a .yaml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2, 'value2');
    },

    'Loading configurations from a .yml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2yml, 'value2yml');
    },

    'Loading configurations from a Coffee-Script file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm3, 'value3');
    },

    'Loading configurations from an environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPort, '5999');
    },

    'Loading configurations from the local file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword, 'real password');
    },

    'Loading configurations from the local environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword2, 'another password');
      assert.deepEqual(CONFIG.Customers.lang, ['en','de','es']);
    },

    'Loading prior runtime.json configurations is correct': function() {
      assert.equal(CONFIG.Customers.dbName, 'override_from_runtime_json');
    },

    'Multi-instance default.json override is correct': function() {
      assert.equal(CONFIG.Customers.altDbPort, 4400);
    },

    'Multi-instance local.yaml override is correct': function() {
      assert.equal(CONFIG.Customers.altDbPort1, 2209);
    }

  },

  'Old-Style Configurations from environment variables (deprecated)': {
    topic: function() {
      return CONFIG;
    },

    'Configuration can come from environment variables': function() {
      assert.equal(CONFIG.EnvOverride.parm2, 13);
    },

    'Double__underscores escape to single_underscores': function() {

      JSON.stringify(CONFIG, null, 2);
      assert.equal(CONFIG.EnvOverride.parm_number_1, 'overridden from test');
    }
  },

  'Configurations from the $NODE_CONFIG environment variable': {
    topic: function() {
      return CONFIG;
    },

    'Configuration can come from the $NODE_CONFIG environment': function() {
      assert.equal(CONFIG.EnvOverride.parm3, 'overridden from $NODE_CONFIG');
    },

    'Type correct configurations from $NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm4, 100);
    }

  },

  'Configurations from the --NODE_CONFIG command line': {
    topic: function() {
      return CONFIG;
    },

    'Configuration can come from the --NODE_CONFIG command line argument': function() {
      assert.equal(CONFIG.EnvOverride.parm5, 'overridden from --NODE_CONFIG');
    },

    'Type correct configurations from --NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm6, 101);
    }

  },

 'Assuring a configuration property can be hidden': {
    topic: function() {
      var object = {
        item1: 23,
        subObject: {
      	  item2: "hello"
        }
      };
      return object;
    },

    'The makeHidden() method is available': function() {
      assert.isFunction(CONFIG.makeHidden);
    },

    'The test object (before hiding) is correct': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"item1":23,"subObject":{"item2":"hello"}}');
    },

    'The test object (after hiding) is correct': function(object) {
      CONFIG.makeHidden(object, 'item1');
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
    },

    'The hidden property is readable, and has not changed': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
      assert.isTrue(object.item1 == 23);
    }

  },

  'Assuring a configuration property can be made immutable': {
    topic: function() {

      CONFIG.makeImmutable(CONFIG.TestModule, 'parm1');
      CONFIG.TestModule.parm1 = "setToThis";
      return CONFIG.TestModule.parm1;
    },

    'The makeImmutable() method is available': function() {
      assert.isFunction(CONFIG.makeImmutable);
    },

    'Correctly unable to change an immutable configuration': function(value) {
      assert.isTrue(value != "setToThis");
    },

    'Left the original value intact after attempting the change': function(value) {
      assert.equal(value, "value1");
    }
  },

  'Configuration for module developers': {
    topic: function() {

      // Set some parameters for the test module
      return CONFIG.setModuleDefaults("TestModule", {
        parm1: 1000, parm2: 2000, parm3: 3000
      });
    },

    'The setModuleDefaults() method is available': function() {
      assert.isFunction(CONFIG.setModuleDefaults);
    },

    'The module config is in the CONFIG object': function(moduleConfig) {
      assert.isTrue(typeof(CONFIG.TestModule) != "undefined");
      assert.deepEqual(CONFIG.TestModule, moduleConfig);
    },

    'Local configurations are mixed in': function(moduleConfig) {
      assert.equal(moduleConfig.parm1, "value1");
    },

    'Defaults remain intact unless overridden': function(moduleConfig) {
      assert.equal(moduleConfig.parm2, 2000);
    }

  },

  'Internal Change Notification Tests': {
    topic: function() {

      // Attach this topic as a watcher
      var cb = this.callback;
      CONFIG.watch(CONFIG, null, function(obj, prop, oldValue, newValue){
        // Don't process the submodule test - that's for later
        if (prop == 'parm3') return;
        if (prop == 'dynamicArray') return;
        if (prop == 'staticArray') {
          cb('Watch callback should not have been called for staticArray');
          return false;
        }

        cb(null, {obj:obj, prop:prop, oldValue:oldValue, newValue:newValue});
      });

      // Write the new watched value out to the runtime.json file
      CONFIG.watchThisValue = newWatchedValue;

      // Test that submodule configurations are persisted
      CONFIG.TestModule.parm3 = 1234;

      // Write the new array value out to the runtime.json file
      CONFIG.dynamicArray = newDynamicArray;

      // Test that changing an array d
      CONFIG.staticArray = [2,1,3];
    },

    'The watch() method is available': function() {
      assert.isFunction(CONFIG.watch);
    },

    'The change handler callback was fired': function(err, obj) {
      assert.isTrue(true);
    },

    'And it was called on the correct object': function(err, obj) {
      assert.isTrue(obj.obj === CONFIG);
    },

    'And it was called with the correct parameter': function(err, obj) {
      assert.equal(obj.prop, 'watchThisValue');
    },

    'And it has the correct prior value': function(err, obj) {
      assert.equal(obj.oldValue, originalWatchedValue);
    },

    'And it has the correct new value': function(err, obj) {
      assert.equal(obj.newValue, newWatchedValue);
    },

    'And the config value was correctly set': function(err, obj) {
      assert.equal(CONFIG.watchThisValue, newWatchedValue);
    },

    'Waiting for the O/S to notify us of changes...': function(err, obj) {
      // This is just a message for the next test
      assert.isTrue(true);
    }

  },

  'Runtime Configuration Changes are Persisted to runtime.json': {
    topic: function() {
      // Watch the file for changes
      var t = this;
      FileSystem.unwatchFile(runtimeJsonFilename);
      FileSystem.watchFile(runtimeJsonFilename, {persistent:true}, function(){
        // This was failing on node v0.6 due to the watch happening before full write,
        // so adding a small interval so it doesn't fail on older node.js versions
        setTimeout(function(){
	        t.callback(null, CONFIG._parseFile(runtimeJsonFilename));
	      },10);

        FileSystem.createReadStream(runtimeJsonFilename).pipe(FileSystem.createWriteStream(runtimeJsonFilenameBak));
      });
    },
    'The runtime.json file was changed': function(err, runtimeObj) {
      assert.isTrue(!err);
    },
    'Prior configuration values were kept intact': function(err, runtimeObj) {
      assert.equal(runtimeObj.Customers.dbName, "override_from_runtime_json");
    },
    'Changed configuration values were persisted': function(err, runtimeObj) {
      assert.equal(runtimeObj.watchThisValue, CONFIG.watchThisValue);
      assert.deepEqual(runtimeObj.dynamicArray, CONFIG.dynamicArray);
    },
    'Unchanged configuration values were not persisted': function(err, runtimeObj) {
      assert.isUndefined(runtimeObj.staticArray);
    },
    'Module default values are persisted': function(err, runtimeObj) {
      assert.equal(runtimeObj.TestModule.parm3, 1234);
    },
    'The resetRuntime() method is available': function() {
      assert.isFunction(CONFIG.resetRuntime);
    },
    'Runtime Configuration is empty': function() {
      CONFIG.resetRuntime(function(err, written, buffer) {
        FileSystem.readFile(runtimeJsonFilename, function(err, data) {
            assert.isEqual(data,'{}');
        });
      });
    },
    teardown: function() {
      FileSystem.rename(runtimeJsonFilenameBak, runtimeJsonFilename, function(err) {
        if(err) {
          console.log('Cant rename file');
        }
      });
    }
  },

  'Assuring you can get originalConfig': {
    topic: function() {
      return CONFIG.getOriginalConfig();
    },
    'The getOriginalConfig() method is available': function() {
      assert.isFunction(CONFIG.getOriginalConfig);
    }
  }

});

Released on github under the Apache License 2.0 version 0.4.32