Node.js Configuration

Node.js Configuration > test > 1-protected-test.js (source view)
Search:
 
Filters
/**
 * <p>Unit tests</p>
 *
 * @module test
 */

// 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 CONFIG = require('../lib/config');
var vows = require('vows');
var assert = require('assert');

// Make a copy of the command line args
var argvOrg = process.argv;

/**
 * <p>Tests for underlying node-config utilities.  To run type:</p>
 * <pre>npm test config</pre>
 *
 * @class ProtectedTest
 */
exports.PrivateTest = vows.describe('Protected (hackable) utilities test').addBatch({
  'Library initialization': {
    'Library is available': function() {
      assert.isObject(CONFIG);
    }
  },

  '_isObject() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG._isObject);
    },
    'Correctly identifies objects': function() {
   	  assert.isTrue(CONFIG._isObject({A:"b"}));
    },
    'Correctly excludes non-objects': function() {
   	  assert.isFalse(CONFIG._isObject("some string"));
   	  assert.isFalse(CONFIG._isObject(45));
   	  assert.isFalse(CONFIG._isObject([2, 3]));
   	  assert.isFalse(CONFIG._isObject(["a", "b"]));
   	  assert.isFalse(CONFIG._isObject(null));
   	  assert.isFalse(CONFIG._isObject(undefined));
    }
  },

  '_cloneDeep() tests': {
    topic: function() {
      // Return an object for copy tests
      return {
        elem0:true,
        elem1:"Element 1",
        elem2:2,
        elem3:[1,2,3],
        elem4:function(){return "hello";},
        elem5:{sub1:"sub 1",sub2:2,sub3:[1,2,3]}
      };
    },
    'The function exists': function() {
      assert.isFunction(CONFIG._cloneDeep);
    },
    'Original and copy should test equivalent (deep)': function(orig) {
      var copy = CONFIG._cloneDeep(orig);
      assert.deepEqual(copy, orig);
    },
    'The objects should be different': function(orig) {
      var copy = CONFIG._cloneDeep(orig);
      copy.elem1 = false;
      assert.notDeepEqual(copy, orig);
    },
    'Object clones should be objects': function(orig) {
      assert.isObject(CONFIG._cloneDeep({a:1, b:2}));
    },
    'Array clones should be arrays': function(orig) {
      assert.isArray(CONFIG._cloneDeep(["a", "b", 3]));
    },
    'Arrays should be copied by value, not by reference': function(orig) {
      var copy = CONFIG._cloneDeep(orig);
      assert.deepEqual(copy, orig);
      copy.elem3[0] = 2;
      // If the copy wasn't deep, elem3 would be the same object
      assert.notDeepEqual(copy, orig);
    },
    'Objects should be copied by value, not by reference': function(orig) {
      var copy = CONFIG._cloneDeep(orig);
      copy.elem5.sub2 = 3;
      assert.notDeepEqual(copy, orig);
      copy = CONFIG._cloneDeep(orig);
      copy.elem5.sub3[1] = 3;
      assert.notDeepEqual(copy, orig);
    }
  },

  '_extendDeep() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG._extendDeep);
    },
    'Performs normal extend': function() {
      var orig = {elem1:"val1", elem2:"val2"};
      var extWith = {elem3:"val3"};
      var shouldBe = {elem1:"val1", elem2:"val2", elem3:"val3"};
      assert.deepEqual(CONFIG._extendDeep(orig, extWith), shouldBe);
    },
    'Replaces non-objects': function() {
      var orig = {elem1:"val1", elem2:["val2","val3"],elem3:{sub1:"val4"}};
      var extWith = {elem1:1,elem2:["val4"],elem3:"val3"};
      var shouldBe = {elem1:1, elem2:["val4"],elem3:"val3"};
      assert.deepEqual(CONFIG._extendDeep(orig, extWith), shouldBe);
    },
    'Merges objects': function() {
      var orig = {e1:"val1", elem2:{sub1:"val4",sub2:"val5"}};
      var extWith = {elem2:{sub2:"val6",sub3:"val7"}};
      var shouldBe = {e1:"val1", elem2:{sub1:"val4",sub2:"val6",sub3:"val7"}};
      assert.deepEqual(CONFIG._extendDeep(orig, extWith), shouldBe);
    },
    'Correctly types new objects and arrays': function() {
      var orig = {e1:"val1", e3:["val5"]};
      var extWith = {e2:{elem1:"val1"}, e3:["val6","val7"]};
      var shouldBe = {e1:"val1", e2:{elem1:"val1"}, e3:["val6","val7"]};
      var ext = CONFIG._extendDeep({}, orig, extWith);
      assert.isObject(ext.e2);
      assert.isArray(ext.e3);
    },
    'Keeps non-merged objects intact': function() {
      var orig     = {e1:"val1", elem2:{sub1:"val4",sub2:"val5"}};
      var shouldBe = {e1:"val1", elem2:{sub1:"val4",sub2:"val5"}};
      var extWith = {elem3:{sub2:"val6",sub3:"val7"}};
      CONFIG._extendDeep({}, orig, extWith);
      assert.deepEqual(orig, shouldBe);
    }
  },

  '_equalsDeep() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG._equalsDeep);
    },
    'Succeeds on two empty objects': function() {
      assert.isTrue(CONFIG._equalsDeep({}, {}));
    },
    'Succeeds on array comparisons': function() {
      assert.isTrue(CONFIG._equalsDeep([1,'hello',2], [1,'hello',2]));
    },
    'Succeeds on the same object': function() {
      var a = {hello:'world'};
      assert.isTrue(CONFIG._equalsDeep(a, a));
    },
    'Succeeds on a regular object': function() {
      var a = {value_3: 14, hello:'world', value_1: 29};
      var b = {value_1: 29, hello:'world', value_3: 14};
      assert.isTrue(CONFIG._equalsDeep(a, b));
    },
    'Succeeds on a deep object': function() {
      var a = {value_3: 14, hello:'world', value_1: 29, value_4:['now','is','the','time']};
      var b = {value_1: 29, hello:'world', value_3: 14, value_4:['now','is','the','time']};
      var c = {creditLimit: 10000, deepValue: a};
      var d = {deepValue: b, creditLimit:10000};
      assert.isTrue(CONFIG._equalsDeep(c, d));
    },
    'Fails if either object is null': function() {
      assert.isFalse(CONFIG._equalsDeep({}, null));
      assert.isFalse(CONFIG._equalsDeep(null, {}));
      assert.isFalse(CONFIG._equalsDeep(null, null));
    },
    'Fails if either object is undefined': function() {
      var a = {};
      assert.isFalse(CONFIG._equalsDeep({}));
      assert.isFalse(CONFIG._equalsDeep(a['noElement'], {}));
    },
    'Fails if either object is undefined': function() {
      var a = {};
      assert.isFalse(CONFIG._equalsDeep({}));
      assert.isFalse(CONFIG._equalsDeep(a['noElement'], {}));
    },
    'Fails if object1 has more elements': function() {
      var a = {value_3: 14, hello:'world', value_1: 29, otherElem: 40};
      var b = {value_1: 29, hello:'world', value_3: 14};
      assert.isFalse(CONFIG._equalsDeep(a, b));
    },
    'Fails if object2 has more elements': function() {
      var a = {value_1: 29, hello:'world', value_3: 14};
      var b = {value_3: 14, hello:'world', value_1: 29, otherElem: 40};
      assert.isFalse(CONFIG._equalsDeep(a, b));
    },
    'Fails if any value is different': function() {
      var a = {value_1: 30, hello:'world', value_3: 14, value_4:['now','is','the','time']};
      var b = {value_1: 29, hello:'world', value_3: 14, value_4:['now','is','the','time']};
      assert.isFalse(CONFIG._equalsDeep(a, b));
      var a = {value_1: 29, hello:'world', value_3: 14, value_4:['now','is','the','time']};
      var b = {value_1: 29, hello:'world', value_3: 14, value_4:['now','isnt','the','time']};
      assert.isFalse(CONFIG._equalsDeep(a, b));
    }
  },

  '_diffDeep() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG._diffDeep);
    },
    'Returns an empty object if no differences': function() {
      var a = {value_3: 14, hello:'world', value_1: 29};
      var b = {value_1: 29, hello:'world', value_3: 14};
      assert.equal(typeof(CONFIG._diffDeep(a,b)), 'object');
      assert.isTrue(Object.keys(CONFIG._diffDeep(a, b)).length == 0);
    },
    'Returns an empty object if no differences (deep)': function() {
      var a = {value_3: 14, hello:'world', value_1: 29, value_4:[1,'hello',2], deepObj:{a:22,b:{c:45,a:44}}};
      var b = {value_1: 29, hello:'world', value_3: 14, value_4:[1,'hello',2], deepObj:{a:22,b:{a:44,c:45}}};
      assert.equal(typeof(CONFIG._diffDeep(a,b)), 'object');
      assert.isTrue(Object.keys(CONFIG._diffDeep(a, b)).length == 0);
    },
    'Returns just the diff values': function() {
      var a = {value_3: 14, hello:'wurld', value_1: 29, deepObj:{a:22,b:{c:45,a:44}}};
      var b = {value_1: 29, hello:'world', value_3: 14, deepObj:{a:22,b:{a:44,c:45}}};
      var diff = CONFIG._diffDeep(a,b);
      assert.equal(Object.keys(diff).length, 1);
      assert.equal(diff.hello, 'world');
    },
    'Returns just the diff values (deep)': function() {
      var a = {value_3: 14, hello:'wurld', value_1: 29, value_4:[1,'hello',2], deepObj:{a:22,b:{c:45,a:44}}};
      var b = {value_1: 29, hello:'wurld', value_3: 14, value_4:[1,'goodbye',2], deepObj:{a:22,b:{a:45,c:44}}};
      var diff = CONFIG._diffDeep(a,b);
      assert.equal(Object.keys(diff).length, 2);
      assert.equal(Object.keys(diff.deepObj).length, 1);
      assert.equal(Object.keys(diff.deepObj.b).length, 2);
      assert.equal(diff.deepObj.b.a, 45);
      assert.equal(diff.deepObj.b.c, 44);
      assert.deepEqual(diff.value_4, [1, 'goodbye', 2]);
    }
  },

  '_stripComments() tests': {
    // Only testing baseline stripComments functionality.
	// This implementation handles lots of edge cases that aren't in these tests
    'The function exists': function() {
      assert.isFunction(CONFIG._stripComments);
    },
    'Leaves a simple string without comments alone': function() {
   	  var str = "Hello\nWorld";
   	  assert.equal(CONFIG._stripComments(str), str);
    },
    'Strips out line-type comments': function() {
   	  var str1 = "var a='Hello'; // Comment about the a variable";
   	  var str2 = "var a='Hello'; ";
   	  assert.equal(CONFIG._stripComments(str1), str2);
    },
    'Strips out block-type comments': function() {
   	  var str1 = "var a='Hello';/* Block Comment */ var b=24";
   	  var str2 = "var a='Hello'; var b=24";
   	  assert.equal(CONFIG._stripComments(str1), str2);
    },
    'Strips out multi-line block comments': function() {
   	  var str1 = "var a='Hello';\n/* Block Comment\n  Line 2 comment\n*/\nvar b=24";
   	  var str2 = "var a='Hello';\n\nvar b=24";
   	  assert.equal(CONFIG._stripComments(str1), str2);
    }
  },

  '_parseFile() tests': {
    topic: function() {
      return CONFIG._parseFile(__dirname + '/config/default.yaml');
    },
    'The function exists': function() {
      assert.isFunction(CONFIG._parseFile);
    },
    'An object is returned': function(config) {
      assert.isObject(config);
    },
    'The correct object is returned': function(config) {
      assert.isObject(config.Customers);
      assert.isTrue(config.Customers.dbName == 'from_default_yaml');
      assert.isTrue(config.Customers.dbPort == 5984);
      assert.isObject(config.AnotherModule);
      assert.isTrue(config.AnotherModule.parm2 == "value2");
    }
  },

  '_loadFileConfigs() tests': {
    topic: function() {
      return CONFIG._loadFileConfigs();
    },
    'The function exists': function() {
      assert.isFunction(CONFIG._loadFileConfigs);
    },
    'An object is returned': function(configs) {
      assert.isObject(configs);
    },
    'The correct object is returned': function(config) {
      assert.isObject(config.Customers);
      assert.isTrue(config.Customers.dbHost == 'base');
      assert.isTrue(config.Customers.dbName == 'override_from_runtime_json');
    }
  },

  '_attachProtoDeep() tests': {
    topic: function() {
      // Create an object that contains other objects to see
      // that the prototype is added to all objects.
      var watchThis = {
        subObject: {
          item1: 23,
          subSubObject: {
        	item2: "hello"
          }
        }
      };
      return CONFIG._attachProtoDeep(watchThis);
    },
    'The function exists': function() {
      assert.isFunction(CONFIG._attachProtoDeep);
    },
    'The original object is returned': function(config) {
      assert.isObject(config);
      assert.isTrue(config.subObject.item1 === 23);
      assert.isTrue(config.subObject.subSubObject.item2 === "hello");
    },
    'The _cloneDeep method is attached to the object': function(config) {
      assert.isTrue({a:27}.a == config._cloneDeep({a:27}).a);
    },
    'The _cloneDeep method is also attached to sub-objects': function(config) {
      assert.isTrue({a:27}.a == config.subObject._cloneDeep({a:27}).a);
      assert.isTrue({a:27}.a == config.subObject.subSubObject._cloneDeep({a:27}).a);
    },
    'Prototype methods are not exposed in the object': function(config) {
      // This test is here because altering object.__proto__ places the method
      // directly onto the object. That caused problems when iterating over the
      // object.  This implementation does the same thing, but hides them.
      assert.isTrue(JSON.stringify(config) == '{"subObject":{"item1":23,"subSubObject":{"item2":"hello"}}}');
    }
  },

  '_getCmdLineArg() tests': {
    topic: function() {
        // Set process.argv example object
        var testArgv = [
            process.argv[0],
            process.argv[1],
            '--NODE_ENV=staging'
        ];
        process.argv = testArgv;
        return CONFIG._getCmdLineArg('NODE_ENV');
    },
    'The function exists': function() {
        assert.isFunction(CONFIG._getCmdLineArg);
    },
    'NODE_ENV should be staging': function(nodeEnv) {
        assert.equal(nodeEnv, 'staging');
    },
    'Returns false if the argument did not match': function() {
        assert.isFalse(CONFIG._getCmdLineArg('NODE_CONFIG_DIR'));
    },
    'Returns the argument (alternative syntax)': function() {
        process.argv.push('--NODE_CONFIG_DIR=/etc/nodeConfig');
        assert.equal(CONFIG._getCmdLineArg('NODE_CONFIG_DIR'), '/etc/nodeConfig');
    },
    'Returns always the first matching': function() {
        process.argv.push('--NODE_ENV=test');
        assert.equal(CONFIG._getCmdLineArg('NODE_ENV'), 'staging');
    },
    'Revert original process aruments': function() {
        assert.notEqual(process.argv, argvOrg);
        process.argv = argvOrg;
        assert.equal(process.argv, argvOrg);
    }
  }

});

Released on github under the Apache License 2.0 version 0.4.32