Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slightly refactor & add tests for API config loading & redis setup #151

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ INSTALLATION INSTRUCTIONS FOR I/O DOCS
From the command line type in:
<pre> git clone http://github.com/mashery/iodocs.git
cd iodocs
npm install
npm install --production
</pre>


Expand Down
40 changes: 7 additions & 33 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ var express = require('express'),
http = require('http'),
https = require('https'),
crypto = require('crypto'),
redis = require('redis'),
RedisStore = require('connect-redis')(express);
RedisStore = require('connect-redis')(express),
apiConfigLoader = require('./src/apiConfigLoader.js'),
redisSetup = require('./src/redisSetup.js');

// Configuration
try {
Expand All @@ -49,42 +50,15 @@ try {
//
// Redis connection
//
var defaultDB = '0';
config.redis.database = config.redis.database || defaultDB;

if (process.env.REDISTOGO_URL) {
var rtg = require("url").parse(process.env.REDISTOGO_URL);
config.redis.host = rtg.hostname;
config.redis.port = rtg.port;
config.redis.password = rtg.auth.split(":")[1];
}

var db = redis.createClient(config.redis.port, config.redis.host);
db.auth(config.redis.password);

db.on("error", function(err) {
if (config.debug) {
console.log("Error " + err);
}
});
var db = redisSetup.configure(config.redis);

//
// Load API Configs
//

config.apiConfigDir = path.resolve(config.apiConfigDir || 'public/data');
if (!fs.existsSync(config.apiConfigDir)) {
console.error("Could not find API config directory: " + config.apiConfigDir);
process.exit(1);
}

try {
var apisConfig = require(path.join(config.apiConfigDir, 'apiconfig.json'));
if (config.debug) {
console.log(util.inspect(apisConfig));
}
} catch(e) {
console.error("File apiconfig.json not found or is invalid.");
var apisConfig = apiConfigLoader.load(config);
} catch (e) {
console.error(e.message);
process.exit(1);
}

Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@
"querystring": "0.1.0",
"supervisor": ">= 0.5.x"
},
"devDependencies": {},
"devDependencies": {
"mocha": "~1.15.1",
"should": "~2.1.1",
"sinon": "~1.7.3"
},
"main": "index",
"engines": {
"node": ">= 0.4.0",
"npm": ">= 1.1.49"
},
"scripts": {
"start": "node_modules/.bin/supervisor -e 'js|json' app",
"startwin": "supervisor -e 'js' app"
"startwin": "supervisor -e 'js' app",
"test": "node_modules/.bin/mocha --ui bdd --reporter spec"
}
}
34 changes: 34 additions & 0 deletions src/apiConfigLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var fs = require('fs'),
path = require('path'),
util = require('util'),
jsonLoader = require('./jsonLoader.js');

/**
* Loads the APIs' configuration object from the directory specified by config.apiConfigDir. The value of
* config.apiConfigdir is also updated to be a fully resolved path.
*
* @param config {Object} I/O Docs config object
* @returns {*}
*/
exports.load = function(config) {
var apiConfigFile = '[config file not yet set]',
apisConfig;

config.apiConfigDir = path.resolve(config.apiConfigDir || 'public/data');
if (!fs.existsSync(config.apiConfigDir)) {
throw new Error("Could not find API config directory: " + apiConfigDir);
}

try {
apiConfigFile = path.join(config.apiConfigDir, 'apiconfig.json');
apisConfig = jsonLoader.load(apiConfigFile);
} catch(e) {
throw new Error("File " + apiConfigFile + " not found or is invalid.");
}

if (config.debug) {
console.log(util.inspect(apisConfig));
}

return apisConfig;
};
11 changes: 11 additions & 0 deletions src/jsonLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Requires the given path and returns an object.
*
* This is a trivial wrapper for require(), used so that it can be stubbed in tests
*
* @param path {string}
* @returns {*}
*/
exports.load = function(path) {
return require(path);
};
24 changes: 24 additions & 0 deletions src/redisSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
var redis = require('redis');

exports.configure = function(redisConfig) {
var defaultDB = '0';
redisConfig.database = redisConfig.database || defaultDB;

if (process.env.REDISTOGO_URL) {
var rtg = require("url").parse(process.env.REDISTOGO_URL);
redisConfig.host = rtg.hostname;
redisConfig.port = rtg.port;
redisConfig.password = rtg.auth.split(":")[1];
}

var db = redis.createClient(redisConfig.port, redisConfig.host);
db.auth(redisConfig.password);

db.on("error", function(err) {
if (config.debug) {
console.log("Error " + err);
}
});

return db;
};
73 changes: 73 additions & 0 deletions test/apiConfigLoaderSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
var sinon = require('sinon'),
should = require('should');

var fs = require('fs'),
path = require('path'),
jsonLoader = require('../src/jsonLoader.js');

var apiConfigLoader = require('../src/apiConfigLoader.js');

describe("API config loader", function() {
var sandbox = sinon.sandbox.create();
var mockApiConfig = {_name: 'mock API config'};

function givenPathResolves(dirPath) {
sandbox.stub(path, 'resolve').withArgs(dirPath).returns('/some/resolved/path');
}

beforeEach(function() {
sandbox.stub(fs, 'existsSync').withArgs('/some/resolved/path').returns(true);
sandbox.stub(path, 'join').withArgs('/some/resolved/path', 'apiconfig.json').returns('/some/resolved/path/someConfig.json');
sandbox.stub(jsonLoader, 'load').withArgs('/some/resolved/path/someConfig.json').returns(mockApiConfig)
});

afterEach(function() {
sandbox.restore();
});

it('defaults to loading from public/data/apiconfig.json', function() {
// given
givenPathResolves('public/data');
var emptyConfig = {};

// when
var apiConfig = apiConfigLoader.load(emptyConfig);

// then
apiConfig.should.eql(mockApiConfig);
});

it('can have directory specified', function() {
// given
givenPathResolves('some/config/dir');
var config = {
apiConfigDir: 'some/config/dir'
};

// when
var apiConfig = apiConfigLoader.load(config);

// then
apiConfig.should.eql(mockApiConfig);
});

it('throws an error if the specified config dir cannot be found', function() {
givenPathResolves('public/data');
fs.existsSync.restore();
sandbox.stub(fs, 'existsSync').withArgs('/some/resolved/path').returns(false);

var loading = function() {apiConfigLoader.load(config)};

loading.should.throw();
});

it('throws an error if loading the JSON fails', function() {
givenPathResolves('public/data');
jsonLoader.load.restore();
sandbox.stub(jsonLoader, 'load').throws('Something went wrong!');

var loading = function() {apiConfigLoader.load(config)};

loading.should.throw();
});
});
111 changes: 111 additions & 0 deletions test/redisSetupSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
var sinon = require('sinon'),
should = require('should');

var redis = require('redis'),
url = require('url');

var redisSetup = require('../src/redisSetup.js');

describe('Redis setup', function() {
var sandbox = sinon.sandbox.create();

var fakeClient = {
auth: function() {},
on: function() {}
};

afterEach(function() {
sandbox.restore();
});

it('defaults the configured database to 0', function() {
// given
var config = {};

// when
redisSetup.configure(config);

// then
config.should.have.property('database', '0');
});

it('respects a previously configured database property', function() {
// given
var config = {database: '1'};

// when
redisSetup.configure(config);

// then
config.should.have.property('database', '1');
});

it('returns a Redis client created from config', function() {
// given
var config = {
port: 12345,
host: 'some-host',
password: 'its-a-secret'
};
sandbox.stub(redis, 'createClient').withArgs(config.port, config.host).returns(fakeClient);
var authSpy = sandbox.spy(fakeClient, 'auth');

// when
var client = redisSetup.configure(config);

// then
client.should.eql(fakeClient);
authSpy.calledWith(config.password).should.be.ok;
});

it('overrides config values with Redis To Go values if available', function() {
// given
var config = {
port: 12345,
host: 'some-host',
password: 'its-a-secret'
};
process.env.REDISTOGO_URL = 'some-url';
var redisToGoConfigPassword = 'another-password';
var redisToGoConfig = {
hostname: 'another-host',
port: 54321,
auth: 'user:' + redisToGoConfigPassword
};
sandbox.stub(url, 'parse').withArgs(process.env.REDISTOGO_URL).returns(redisToGoConfig);

// when
redisSetup.configure(config);

// then
config.should.have.property('port', redisToGoConfig.port);
config.should.have.property('host', redisToGoConfig.hostname);
config.should.have.property('password', redisToGoConfigPassword);
});

it('returns a Redis client created from Redit To Go values if available', function() {
// given
var config = {
port: 12345,
host: 'some-host',
password: 'its-a-secret'
};
process.env.REDISTOGO_URL = 'some-url';
var redisToGoConfigPassword = 'another-password';
var redisToGoConfig = {
hostname: 'another-host',
port: 54321,
auth: 'user:' + redisToGoConfigPassword
};
sandbox.stub(url, 'parse').withArgs(process.env.REDISTOGO_URL).returns(redisToGoConfig);
sandbox.stub(redis, 'createClient').withArgs(redisToGoConfig.port, redisToGoConfig.hostname).returns(fakeClient);
var authSpy = sandbox.spy(fakeClient, 'auth');

// when
var client = redisSetup.configure(config);

// then
client.should.eql(fakeClient);
authSpy.calledWith(redisToGoConfigPassword).should.be.ok;
});
});