save point durring huge unorganized refactor
This commit is contained in:
40
test/unit/.jshintrc
Normal file
40
test/unit/.jshintrc
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"node": true,
|
||||
"white": true,
|
||||
"bitwise": false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
"eqeqeq": true,
|
||||
"forin": true,
|
||||
"immed": true,
|
||||
"expr": true,
|
||||
"indent": 2,
|
||||
"latedef": "nofunc",
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"undef": true,
|
||||
"quotmark": "single",
|
||||
"plusplus": false,
|
||||
"boss": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
"validthis": true,
|
||||
"sub": true,
|
||||
"maxlen": 140,
|
||||
"-W084": true,
|
||||
"maxerr": 10,
|
||||
|
||||
"-W030": true,
|
||||
"-W068": true,
|
||||
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"before": true,
|
||||
"after": true,
|
||||
"it": true,
|
||||
"beforeEach": true,
|
||||
"afterEach": true
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
var es = require('../../src/elasticsearch'),
|
||||
api = require('../../src/lib/api'),
|
||||
expect = require('expect.js');
|
||||
|
||||
describe('Client instances creation', function () {
|
||||
var client;
|
||||
|
||||
beforeEach(function () {
|
||||
client = new es.Client();
|
||||
});
|
||||
|
||||
it('inherits the api', function () {
|
||||
client.bulk.should.eql(api.bulk);
|
||||
client.cluster.nodeStats.should.eql(api.cluster.prototype.nodeStats);
|
||||
});
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
var es = require('../../src/elasticsearch');
|
||||
|
||||
describe('Connection Pool', function () {
|
||||
var client, pool;
|
||||
beforeEach(function () {
|
||||
client = new es.Client();
|
||||
pool = client.config.connectionPool;
|
||||
});
|
||||
|
||||
describe('default settings', function () {
|
||||
it('by default has one living connection and no dead connections', function () {
|
||||
pool.connections.alive.should.have.lengthOf(1);
|
||||
pool.connections.dead.should.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,80 +0,0 @@
|
||||
|
||||
var EsServer = require('../mocks/es_server');
|
||||
var _ = require('../../src/lib/utils');
|
||||
var http = require('http');
|
||||
|
||||
describe('EsServer Mock', function () {
|
||||
|
||||
it('should emit an online event when ready, passing it\'s port number', function (done) {
|
||||
var server = new EsServer();
|
||||
server.on('online', function (port) {
|
||||
port.should.have.type('number');
|
||||
server.shutdown(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when it\'s online', function () {
|
||||
var server;
|
||||
var port;
|
||||
|
||||
function makeRequest(opts, respCb) {
|
||||
opts = _.defaults(opts || {}, {
|
||||
host: 'localhost',
|
||||
port: port
|
||||
});
|
||||
|
||||
var response = null;
|
||||
var req = http.request(opts, function (incomming) {
|
||||
response = '';
|
||||
|
||||
incomming.on('data', function (chunk) {
|
||||
response += chunk;
|
||||
});
|
||||
|
||||
incomming.on('end', function () {
|
||||
if (incomming.headers['content-type'] === 'application/json') {
|
||||
try {
|
||||
respCb(null, JSON.parse(response), incomming.statusCode);
|
||||
} catch (e) {
|
||||
respCb(e, response, incomming.statusCode);
|
||||
}
|
||||
} else {
|
||||
respCb(null, response, incomming.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', respCb);
|
||||
|
||||
req.end();
|
||||
}
|
||||
|
||||
before(function (done) {
|
||||
server = new EsServer();
|
||||
server.on('online', function (_port) {
|
||||
port = _port;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
server.shutdown(done);
|
||||
});
|
||||
|
||||
it('should respond with json to a ping', function (done) {
|
||||
makeRequest({
|
||||
path: '/'
|
||||
}, function (err, resp, status) {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
status.should.be.exactly(200);
|
||||
resp.version.number.should.match(/^\d+\.\d+\.\d+/);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
describe('Http Connector', function () {
|
||||
|
||||
var Host = require('../../src/lib/host');
|
||||
var HttpConnection = require('../../src/lib/connectors/http');
|
||||
var host = new Host('http://someesserver.com:9205/prefix');
|
||||
var con;
|
||||
|
||||
describe('#makeReqParams', function () {
|
||||
|
||||
before(function () {
|
||||
con = new HttpConnection(host, {});
|
||||
});
|
||||
|
||||
it('creates the request params property', function () {
|
||||
var reqParams = con.makeReqParams({
|
||||
method: 'GET',
|
||||
path: '/_cluster/nodes/stats',
|
||||
query: {
|
||||
jvm: true
|
||||
}
|
||||
});
|
||||
|
||||
reqParams.should.include({
|
||||
method: 'GET',
|
||||
protocol: 'http:',
|
||||
auth: '',
|
||||
hostname: 'someesserver.com',
|
||||
port: '9205',
|
||||
path: '/prefix/_cluster/nodes/stats?jvm=true'
|
||||
});
|
||||
|
||||
Object.keys(reqParams).should.not.include([
|
||||
'host', 'pathname', 'query'
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,70 +0,0 @@
|
||||
var Log = require('../../src/lib/log');
|
||||
|
||||
exports['Log::parseLevels'] = {
|
||||
|
||||
'parses a string': function (test) {
|
||||
test.deepEqual(Log.parseLevels('warning'), ['error', 'warning']);
|
||||
test.done();
|
||||
},
|
||||
|
||||
'filters an array': function (test) {
|
||||
test.deepEqual(Log.parseLevels(['trace', 'not a level']), ['trace']);
|
||||
test.done();
|
||||
},
|
||||
|
||||
'returns nothing as a defauls': function (test) {
|
||||
test.ok(!Log.parseLevels());
|
||||
test.done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports['Log::join'] = {
|
||||
|
||||
'joins strings': function (test) {
|
||||
test.equal(Log.join(['one', 'two']), 'one two');
|
||||
test.done();
|
||||
},
|
||||
|
||||
'flattens nested arrays': function (test) {
|
||||
test.equal(Log.join(['one', ['three', 'four']]), 'one three,four');
|
||||
test.done();
|
||||
},
|
||||
|
||||
'flattens arguments': function (test) {
|
||||
(function() {
|
||||
test.equal(Log.join(arguments), 'one two');
|
||||
}('one', 'two'));
|
||||
test.done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Empty log bridge (no outputs)
|
||||
* @type {Log}
|
||||
*/
|
||||
var log;
|
||||
|
||||
exports['Log instance with no outputs'] = {
|
||||
|
||||
setUp: function (done) {
|
||||
log = new Log([]);
|
||||
done();
|
||||
},
|
||||
|
||||
tearDown: function (done) {
|
||||
done();
|
||||
},
|
||||
|
||||
'should not emit any events': function (test) {
|
||||
log.emit = function () {
|
||||
test.ok(false, 'Emit should not have been called');
|
||||
};
|
||||
|
||||
log.error('Error Message');
|
||||
|
||||
test.done();
|
||||
}
|
||||
|
||||
};
|
||||
@ -1,67 +0,0 @@
|
||||
|
||||
var es = require('../../src/elasticsearch'),
|
||||
Stdio = require('../../src/lib/loggers/stdio'),
|
||||
_ = require('../../src/lib/utils'),
|
||||
expect = require('expect.js');
|
||||
|
||||
describe('Stdio Logger listening to levels warning and error', function () {
|
||||
var client, log, logger;
|
||||
|
||||
before(function () {
|
||||
client = new es.Client({
|
||||
log: []
|
||||
});
|
||||
log = client.config.log;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
if (logger) {
|
||||
logger.cleanUpListeners();
|
||||
}
|
||||
|
||||
// new logger in warning mode
|
||||
logger = new Stdio({
|
||||
levels: ['error', 'warning']
|
||||
}, log);
|
||||
});
|
||||
|
||||
it('logs error messages', function (done) {
|
||||
logger.write = function (to, label, colorize, what) {
|
||||
label.should.eql('ERROR');
|
||||
what.should.have.type('string');
|
||||
what.should.match(/^Error: Test Error Message/);
|
||||
done();
|
||||
};
|
||||
|
||||
log.error('Test Error Message');
|
||||
});
|
||||
|
||||
it('logs warning messages', function (done) {
|
||||
logger.write = function (to, label, colorize, what) {
|
||||
expect(label).to.be('WARNING');
|
||||
expect(what).to.be('Test Warning Message');
|
||||
done();
|
||||
};
|
||||
|
||||
log.warning('Test Warning', 'Message');
|
||||
});
|
||||
|
||||
it('does not log info messages', function () {
|
||||
if (log.info('Info')) {
|
||||
throw new Error('There shouldn\'t be listeners for info logs');
|
||||
}
|
||||
});
|
||||
|
||||
it('does not log debug messages', function () {
|
||||
if (log.debug('Debug')) {
|
||||
throw new Error('There shouldn\'t be listeners for debug logs');
|
||||
}
|
||||
});
|
||||
|
||||
it('does not log trace messages', function () {
|
||||
if (log.trace('curl "http://localhost:9200" -d "{ \"query\": ... }"')) {
|
||||
throw new Error('There shouldn\'t be listeners for trace logs');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
35
test/unit/test_client.js
Normal file
35
test/unit/test_client.js
Normal file
@ -0,0 +1,35 @@
|
||||
var es = require('../../src/elasticsearch');
|
||||
var api = require('../../src/lib/api');
|
||||
|
||||
describe('Client instances creation', function () {
|
||||
var client;
|
||||
|
||||
beforeEach(function () {
|
||||
if (client) {
|
||||
client.close();
|
||||
}
|
||||
client = new es.Client();
|
||||
});
|
||||
|
||||
it('inherits the api', function () {
|
||||
client.bulk.should.eql(api.bulk);
|
||||
client.cluster.nodeStats.should.eql(api.cluster.prototype.nodeStats);
|
||||
});
|
||||
|
||||
it('closing the client causes it\'s transport to be closed', function () {
|
||||
var called = false;
|
||||
client.transport.close = function () {
|
||||
called = true;
|
||||
};
|
||||
client.close();
|
||||
called.should.be.exactly(true);
|
||||
});
|
||||
|
||||
it('creates a warning level logger by default', function () {
|
||||
client.transport.log.listenerCount('error').should.eql(1);
|
||||
client.transport.log.listenerCount('warning').should.eql(1);
|
||||
client.transport.log.listenerCount('info').should.eql(0);
|
||||
client.transport.log.listenerCount('debug').should.eql(0);
|
||||
client.transport.log.listenerCount('trace').should.eql(0);
|
||||
});
|
||||
});
|
||||
816
test/unit/test_client_action.js
Normal file
816
test/unit/test_client_action.js
Normal file
@ -0,0 +1,816 @@
|
||||
var ca = require('../../src/lib/client_action');
|
||||
var should = require('should');
|
||||
var _ = require('lodash');
|
||||
var when = require('when');
|
||||
|
||||
/**
|
||||
* Creates a simple mock of the client, whose "transport" has a request
|
||||
* function that just calls back with the parameters it received
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
function mockClient() {
|
||||
return {
|
||||
transport: {
|
||||
request: function (params, cb) {
|
||||
if (typeof cb === 'function') {
|
||||
process.nextTick(function () {
|
||||
cb(void 0, params);
|
||||
});
|
||||
} else {
|
||||
return when.resolve(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client action, ensuring that is has some default url specs, and binds it to
|
||||
* a mock client.
|
||||
*
|
||||
* @param {Object} spec - the spec for the client action
|
||||
* @return {Function} - the client action
|
||||
*/
|
||||
function makeClientAction(spec) {
|
||||
spec = spec || {};
|
||||
|
||||
if (!spec.urls && !spec.url) {
|
||||
spec.url = {
|
||||
fmt: '/'
|
||||
};
|
||||
}
|
||||
|
||||
return _.bind(ca(spec), mockClient());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls ca.proxy and binds it to a mock client
|
||||
* @param {Function} fn - the function to proxy
|
||||
* @param {Object} spec - The spec for the proxy
|
||||
* @return {Function} - the clientActionProxy
|
||||
*/
|
||||
function makeClientActionProxy(fn, spec) {
|
||||
return _.bind(ca.proxy(fn, spec || {}), mockClient());
|
||||
}
|
||||
|
||||
|
||||
describe('Client Action runner', function () {
|
||||
var action;
|
||||
|
||||
describe('argument juggling', function () {
|
||||
it('creates an empty param set when no params are sent', function (done) {
|
||||
action = makeClientAction();
|
||||
|
||||
// note: the first arg is the callback
|
||||
action(function (err, params) {
|
||||
params.query.should.eql({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clientAction::proxy', function () {
|
||||
it('proxies to the passed function', function () {
|
||||
var action = makeClientActionProxy(function (params, cb) {
|
||||
throw new Error('proxy function called');
|
||||
});
|
||||
|
||||
(function () {
|
||||
action({}, function () {});
|
||||
}).should.throw('proxy function called');
|
||||
|
||||
});
|
||||
|
||||
it('provides the proper context', function (done) {
|
||||
var client;
|
||||
var action = makeClientActionProxy(function (params, cb) {
|
||||
client = this;
|
||||
process.nextTick(function () {
|
||||
cb(void 0, params);
|
||||
});
|
||||
});
|
||||
|
||||
action({}, function (err, params) {
|
||||
client.transport.request.should.be.type('function');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('handles passing just the callback', function () {
|
||||
var action = makeClientActionProxy(function (params, cb) {
|
||||
should(_.isObject(params)).be.ok;
|
||||
cb.should.be.type('function');
|
||||
});
|
||||
|
||||
action(function () {});
|
||||
});
|
||||
|
||||
it('supports a param transformation function', function () {
|
||||
var action = makeClientActionProxy(function (params, cb) {
|
||||
params.should.have.property('transformed');
|
||||
}, {
|
||||
transform: function (params) {
|
||||
params.transformed = true;
|
||||
}
|
||||
});
|
||||
|
||||
action(function () {});
|
||||
});
|
||||
|
||||
it('returns the proxied function\'s return value', function () {
|
||||
var football = {};
|
||||
var action = makeClientActionProxy(function (params, cb) {
|
||||
return football;
|
||||
});
|
||||
|
||||
action().should.be.exactly(football);
|
||||
});
|
||||
});
|
||||
|
||||
describe('param casting', function () {
|
||||
describe('duration', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: {
|
||||
type: 'duration'
|
||||
},
|
||||
two: {
|
||||
type: 'duration'
|
||||
},
|
||||
three: {
|
||||
type: 'duration'
|
||||
},
|
||||
four: {
|
||||
type: 'duration'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts a number, string, or interval', function (done) {
|
||||
action({
|
||||
one: 1500,
|
||||
two: '500',
|
||||
three: '15m'
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.eql(1500);
|
||||
params.query.two.should.eql('500');
|
||||
params.query.three.should.eql('15m');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects date values', function (done) {
|
||||
action({
|
||||
one: new Date()
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects array', function (done) {
|
||||
action({
|
||||
one: ['one'],
|
||||
two: [ 1304 ]
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects object', function (done) {
|
||||
action({
|
||||
one: { but: 'duration' }
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('list', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'list' },
|
||||
two: { type: 'list' },
|
||||
three: { type: 'list' }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts a string, number, or array', function (done) {
|
||||
action({
|
||||
one: 'some,strings',
|
||||
two: 1430,
|
||||
three: ['some', 'strings'],
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.should.eql({
|
||||
one: 'some,strings',
|
||||
two: 1430,
|
||||
three: 'some,strings'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('it rejects regexp', function (done) {
|
||||
action({
|
||||
one: /regexp!/g
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('it rejects objects', function (done) {
|
||||
action({
|
||||
one: {
|
||||
pasta: 'sauce'
|
||||
}
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('enum', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'enum', options: ['opt', 'other opt', '150'] }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts any value in the list', function (done) {
|
||||
action({
|
||||
one: 'opt'
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.eql('opt');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts any value kind of in the list', function (done) {
|
||||
action({
|
||||
one: 150
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.be.exactly('150');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('it rejects things not in the list', function (done) {
|
||||
action({
|
||||
one: 'not an opt'
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('boolean', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'boolean' },
|
||||
two: { type: 'boolean' },
|
||||
three: { type: 'boolean' },
|
||||
four: { type: 'boolean' },
|
||||
five: { type: 'boolean' },
|
||||
six: { type: 'boolean' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('casts "off", "no", and other falsey things to false', function (done) {
|
||||
action({
|
||||
one: 'off',
|
||||
two: 'no',
|
||||
three: false,
|
||||
four: ''
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
should(params.query.one).be.exactly(false);
|
||||
should(params.query.two).be.exactly(false);
|
||||
should(params.query.three).be.exactly(false);
|
||||
should(params.query.four).be.exactly(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('cast most everything else to true', function (done) {
|
||||
action({
|
||||
one: 'yes',
|
||||
two: 'ok',
|
||||
three: true,
|
||||
four: 1,
|
||||
five: new Date(),
|
||||
six: {}
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
should(params.query.one).be.exactly(true);
|
||||
should(params.query.two).be.exactly(true);
|
||||
should(params.query.three).be.exactly(true);
|
||||
should(params.query.four).be.exactly(true);
|
||||
should(params.query.five).be.exactly(true);
|
||||
should(params.query.six).be.exactly(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('number', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'number' },
|
||||
two: { type: 'number' },
|
||||
three: { type: 'number' },
|
||||
four: { type: 'number' },
|
||||
five: { type: 'number' },
|
||||
six: { type: 'number' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('casts integers properly', function (done) {
|
||||
action({
|
||||
one: '42',
|
||||
two: '-69',
|
||||
three: 15,
|
||||
four: -100,
|
||||
five: '0xFF',
|
||||
six: 0xFFF
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.equal(42);
|
||||
params.query.two.should.equal(-69);
|
||||
params.query.three.should.equal(15);
|
||||
params.query.four.should.equal(-100);
|
||||
params.query.five.should.equal(255);
|
||||
params.query.six.should.equal(4095);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('casts floats properly', function (done) {
|
||||
action({
|
||||
one: '-1.6',
|
||||
two: '4.536',
|
||||
three: -2.6,
|
||||
four: 3.1415,
|
||||
five: 8e5,
|
||||
six: '123e-2',
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.equal(-1.6);
|
||||
params.query.two.should.equal(4.536);
|
||||
params.query.three.should.equal(-2.6);
|
||||
params.query.four.should.equal(3.1415);
|
||||
params.query.five.should.equal(800000);
|
||||
params.query.six.should.equal(1.23);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects dates', function (done) {
|
||||
action({
|
||||
one: new Date()
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects objects', function (done) {
|
||||
action({
|
||||
one: {}
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects arrays', function (done) {
|
||||
action({
|
||||
one: []
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects regexp', function (done) {
|
||||
action({
|
||||
one: /pasta/g
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'string' },
|
||||
two: { type: 'string' },
|
||||
three: { type: 'string' },
|
||||
four: { type: 'string' },
|
||||
five: { type: 'string' },
|
||||
six: { type: 'string' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numbers and strings', function (done) {
|
||||
action({
|
||||
one: '42',
|
||||
two: '-69',
|
||||
three: 15,
|
||||
four: -100,
|
||||
five: '0xFF',
|
||||
six: 0xFFF
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.equal('42');
|
||||
params.query.two.should.equal('-69');
|
||||
params.query.three.should.equal('15');
|
||||
params.query.four.should.equal('-100');
|
||||
params.query.five.should.equal('0xFF');
|
||||
params.query.six.should.equal('4095');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects dates', function (done) {
|
||||
action({
|
||||
one: new Date()
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects objects', function (done) {
|
||||
action({
|
||||
one: {}
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects arrays', function (done) {
|
||||
action({
|
||||
one: []
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects regexp', function (done) {
|
||||
action({
|
||||
one: /pasta/g
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('time', function () {
|
||||
beforeEach(function () {
|
||||
action = makeClientAction({
|
||||
params: {
|
||||
one: { type: 'time' },
|
||||
two: { type: 'time' },
|
||||
three: { type: 'time' },
|
||||
four: { type: 'time' },
|
||||
five: { type: 'time' },
|
||||
six: { type: 'time' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numbers, strings, and dates', function (done) {
|
||||
var now = new Date();
|
||||
|
||||
action({
|
||||
one: '42',
|
||||
two: '-69',
|
||||
three: 15,
|
||||
four: now,
|
||||
five: new Date(999, 2399, 152433)
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.one.should.equal('42');
|
||||
params.query.two.should.equal('-69');
|
||||
params.query.three.should.equal('15');
|
||||
params.query.four.should.equal('' + now.getTime());
|
||||
params.query.five.should.equal('-11162941200000');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects objects', function (done) {
|
||||
action({
|
||||
one: {}
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects arrays', function (done) {
|
||||
action({
|
||||
one: []
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects regexp', function (done) {
|
||||
action({
|
||||
one: /pasta/g
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing of control params from spec', function () {
|
||||
it('passes bulkBody', function (done) {
|
||||
var action = makeClientAction({
|
||||
bulkBody: true
|
||||
});
|
||||
|
||||
action({}, function (err, params) {
|
||||
params.bulkBody.should.be.exactly(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('passes castExists', function (done) {
|
||||
var action = makeClientAction({
|
||||
castExists: true
|
||||
});
|
||||
|
||||
action({}, function (err, params) {
|
||||
params.castExists.should.be.exactly(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('body handling', function () {
|
||||
var action = makeClientAction({
|
||||
needsBody: true
|
||||
});
|
||||
|
||||
it('passed the body when it is set', function (done) {
|
||||
var body = '{"JSON":"PLEASE"}';
|
||||
|
||||
action({ body: body }, function (err, params) {
|
||||
params.body.should.be.exactly(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when the body is not set but required', function (done) {
|
||||
action().then(function () {
|
||||
done(new Error('Error should have been raised'));
|
||||
}, function (err) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing of http method', function () {
|
||||
it('uppercases and passed the default method', function (done) {
|
||||
var action = makeClientAction({
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
action({method: 'get'}, function (err, params) {
|
||||
params.method.should.be.exactly('GET');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uppercases and passed the default method', function (done) {
|
||||
var action = makeClientAction({
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
action({}, function (err, params) {
|
||||
params.method.should.be.exactly('POST');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing of ignore param', function () {
|
||||
it('passes ignore as an array', function (done) {
|
||||
var action = makeClientAction({});
|
||||
action({ ignore: 404 }, function (err, params) {
|
||||
params.ignore.should.eql([404]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing of timeout', function () {
|
||||
it('passes the timeout', function (done) {
|
||||
var action = makeClientAction({
|
||||
timeout: 100
|
||||
});
|
||||
|
||||
action({}, function (err, params) {
|
||||
params.timeout.should.be.exactly(100);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('passes the provided value for timeout', function (done) {
|
||||
var action = makeClientAction({
|
||||
timeout: 100
|
||||
});
|
||||
|
||||
action({ timeout: 3000 }, function (err, params) {
|
||||
params.timeout.should.be.exactly(3000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uses 10000 as the default timeout', function (done) {
|
||||
var action = makeClientAction({});
|
||||
|
||||
action({}, function (err, params) {
|
||||
params.timeout.should.be.exactly(10000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('url resolver', function () {
|
||||
|
||||
var action = makeClientAction({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/<%=index%>/<%=type%>/<%=id%>/<%=thing%>',
|
||||
req: {
|
||||
index: {
|
||||
type: 'list'
|
||||
},
|
||||
id: {
|
||||
type: 'any'
|
||||
}
|
||||
},
|
||||
opt: {
|
||||
type: {
|
||||
type: 'list',
|
||||
default: '_all'
|
||||
},
|
||||
thing: {
|
||||
type: 'any',
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// match a url to the parameters passed in.
|
||||
it('rejects a url if it required params that are not present', function (done) {
|
||||
action({
|
||||
type: ['type1', 'type2']
|
||||
}, function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the default value for optional params', function (done) {
|
||||
action({
|
||||
index: 'index1',
|
||||
id: '1'
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.path.should.be.exactly('/index1/_all/1/');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('casts both optional and required args', function (done) {
|
||||
action({
|
||||
index: ['index1', 'index2'],
|
||||
id: '123',
|
||||
type: ['_all', '-pizza'],
|
||||
thing: 'poo'
|
||||
}, function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.path.should.be.exactly('/index1%2Cindex2/_all%2C-pizza/123/poo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('param collection', function () {
|
||||
var action = makeClientAction({
|
||||
params: {
|
||||
a: { type: 'list', required: true },
|
||||
b: { type: 'duration', default: '15m' },
|
||||
q: { type: 'any' }
|
||||
}
|
||||
});
|
||||
|
||||
it('collects all of the params into params.query', function (done) {
|
||||
action({
|
||||
a: 'pizza',
|
||||
b: '1M'
|
||||
},
|
||||
function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.should.eql({
|
||||
a: 'pizza',
|
||||
b: '1M'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('includes extra params', function (done) {
|
||||
action({
|
||||
a: 'pizza',
|
||||
b: '3w',
|
||||
c: 'popular',
|
||||
},
|
||||
function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.should.eql({
|
||||
a: 'pizza',
|
||||
b: '3w',
|
||||
c: 'popular'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('excludes default values', function (done) {
|
||||
action({
|
||||
a: 'pizza',
|
||||
b: '15m',
|
||||
},
|
||||
function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.should.eql({
|
||||
a: 'pizza'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not include non-query param keys', function (done) {
|
||||
action({
|
||||
a: 'pizza',
|
||||
b: '3w',
|
||||
q: 'beep',
|
||||
body: '{ "mmm": "json" }',
|
||||
timeout: 1000,
|
||||
method: 'head',
|
||||
ignore: 201
|
||||
},
|
||||
function (err, params) {
|
||||
if (err) { throw err; }
|
||||
params.query.should.eql({
|
||||
a: 'pizza',
|
||||
b: '3w',
|
||||
q: 'beep'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enforces required params', function (done) {
|
||||
action({
|
||||
b: '3w'
|
||||
},
|
||||
function (err, params) {
|
||||
err.should.be.an.instanceOf(TypeError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
154
test/unit/test_connection_abstract.js
Normal file
154
test/unit/test_connection_abstract.js
Normal file
@ -0,0 +1,154 @@
|
||||
var ConnectionAbstract = require('../../src/lib/connection');
|
||||
var Host = require('../../src/lib/host');
|
||||
var sinon = require('sinon');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('Connection Abstract', function () {
|
||||
var host = new Host('localhost:9200');
|
||||
|
||||
it('constructs with defaults for deadTimeout, requestCount, host, and bound', function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
conn.deadTimeout.should.eql(30000);
|
||||
conn.requestCount.should.eql(0);
|
||||
conn.host.should.be.exactly(host);
|
||||
conn.bound.should.have.properties('resuscitate');
|
||||
});
|
||||
|
||||
it('requires a valid host', function () {
|
||||
(function () {
|
||||
new ConnectionAbstract();
|
||||
}).should.throw(TypeError);
|
||||
|
||||
(function () {
|
||||
new ConnectionAbstract({});
|
||||
}).should.throw(TypeError);
|
||||
});
|
||||
|
||||
it('required that the request method is overridden', function () {
|
||||
(function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
conn.request();
|
||||
}).should.throw(/overwrit/);
|
||||
});
|
||||
|
||||
describe('#ping', function () {
|
||||
it('requires a callback', function () {
|
||||
(function () {
|
||||
(new ConnectionAbstract(host)).ping();
|
||||
}).should.throw(TypeError);
|
||||
});
|
||||
|
||||
it('calls it\'s own request method', function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
var football = {};
|
||||
conn.request = function () {
|
||||
return football;
|
||||
};
|
||||
|
||||
conn.ping(function () {}).should.be.exactly(football);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setStatus', function () {
|
||||
it('emits the "status set" event with `new`, `old` & `conn` args', function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
var emitted = false;
|
||||
|
||||
conn.emit = function (eventName) {
|
||||
emitted = {
|
||||
name: eventName,
|
||||
args: Array.prototype.slice.call(arguments, 1)
|
||||
};
|
||||
};
|
||||
|
||||
conn.setStatus('closed');
|
||||
emitted.name.should.eql('status set');
|
||||
emitted.args.should.eql(['closed', null, conn]);
|
||||
});
|
||||
|
||||
it('stores the status in this.status', function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
|
||||
conn.setStatus('closed');
|
||||
conn.status.should.eql('closed');
|
||||
});
|
||||
|
||||
it('sets a timeout when set to dead, and removed when alive', function () {
|
||||
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
|
||||
var conn = new ConnectionAbstract(host);
|
||||
|
||||
var start = _.size(clock.timeouts);
|
||||
conn.setStatus('dead');
|
||||
_.size(clock.timeouts).should.be.eql(start + 1);
|
||||
|
||||
conn.setStatus('alive');
|
||||
_.size(clock.timeouts).should.eql(start);
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#resuscitate', function () {
|
||||
it('should not ping the connection unless it is still dead', function () {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
|
||||
conn.setStatus('alive');
|
||||
conn.ping = function () {
|
||||
throw new Error('ping should not have been called');
|
||||
};
|
||||
|
||||
conn.resuscitate();
|
||||
});
|
||||
|
||||
it('should ping the connection after the deadTimeout, and set the status to "alive" on pong', function (done) {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
|
||||
|
||||
// schedules the resuscitate
|
||||
conn.setStatus('dead');
|
||||
|
||||
// override the ping method to just callback without an error
|
||||
conn.ping = function (cb) {
|
||||
process.nextTick(function () {
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
// will be called after the ping calls back
|
||||
conn.setStatus = function (status) {
|
||||
status.should.eql('alive');
|
||||
clock.restore();
|
||||
done();
|
||||
};
|
||||
|
||||
// fast forward the clock
|
||||
clock.tick(conn.deadTimeout);
|
||||
});
|
||||
|
||||
it('should ping the connection after the deadTimeout, and set the status to "dead" on error', function (done) {
|
||||
var conn = new ConnectionAbstract(host);
|
||||
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
|
||||
|
||||
// schedules the resuscitate
|
||||
conn.setStatus('dead');
|
||||
|
||||
// override the ping method to just callback without an error
|
||||
conn.ping = function (cb) {
|
||||
process.nextTick(function () {
|
||||
cb(new Error('server still down'));
|
||||
});
|
||||
};
|
||||
|
||||
// will be called after the ping calls back
|
||||
conn.setStatus = function (status) {
|
||||
status.should.eql('dead');
|
||||
clock.restore();
|
||||
done();
|
||||
};
|
||||
|
||||
// fast forward the clock
|
||||
clock.tick(conn.deadTimeout);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
237
test/unit/test_connection_pool.js
Normal file
237
test/unit/test_connection_pool.js
Normal file
@ -0,0 +1,237 @@
|
||||
var ConnectionPool = require('../../src/lib/connection_pool');
|
||||
var Host = require('../../src/lib/host');
|
||||
var ConnectionAbstract = require('../../src/lib/connection');
|
||||
var _ = require('lodash');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var should = require('should');
|
||||
|
||||
function listenerCount(emitter, event) {
|
||||
if (EventEmitter.listenerCount) {
|
||||
// newer node
|
||||
return EventEmitter.listenerCount(emitter, event);
|
||||
} else {
|
||||
// older node
|
||||
return emitter.listeners(event).length;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Connection Pool', function () {
|
||||
describe('Adding/Removing/Syncing Connections', function () {
|
||||
var pool, host, connection, host2, connection2;
|
||||
|
||||
beforeEach(function () {
|
||||
pool = new ConnectionPool({});
|
||||
|
||||
host = new Host({
|
||||
port: 999
|
||||
});
|
||||
connection = new ConnectionAbstract(host);
|
||||
|
||||
host2 = new Host({
|
||||
port: 2222
|
||||
});
|
||||
connection2 = new ConnectionAbstract(host2);
|
||||
});
|
||||
|
||||
it('#addConnection only adds the connection if it doesn\'t already exist', function () {
|
||||
_.keys(pool.index).length.should.eql(0);
|
||||
pool.addConnection(connection);
|
||||
|
||||
_.keys(pool.index).should.eql([host.toString()]);
|
||||
|
||||
pool.connections.alive.should.eql([connection]);
|
||||
pool.connections.dead.should.eql([]);
|
||||
});
|
||||
|
||||
describe('#removeConnection', function () {
|
||||
it('removes the connection if it exist', function () {
|
||||
pool.addConnection(connection);
|
||||
pool.removeConnection(connection2);
|
||||
|
||||
pool.connections.alive.should.eql([connection]);
|
||||
pool.connections.dead.should.eql([]);
|
||||
_.keys(pool.index).length.should.eql(1);
|
||||
});
|
||||
|
||||
it('closes the connection when it removes it', function () {
|
||||
pool.addConnection(connection);
|
||||
connection.status.should.eql('alive');
|
||||
listenerCount(connection, 'status set').should.eql(1);
|
||||
|
||||
pool.removeConnection(connection);
|
||||
|
||||
connection.status.should.eql('closed');
|
||||
listenerCount(connection, 'status set').should.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('#setHosts syncs the list of Hosts with the connections in the index', function () {
|
||||
// there should now be two connections
|
||||
pool.setHosts([host, host2]);
|
||||
pool.connections.alive.length.should.eql(2);
|
||||
pool.connections.dead.length.should.eql(0);
|
||||
|
||||
// get the new connections
|
||||
connection = pool.index[host.toString()];
|
||||
connection2 = pool.index[host2.toString()];
|
||||
|
||||
// should remove the second connection
|
||||
pool.setHosts([host]);
|
||||
pool.connections.alive.should.eql([connection]);
|
||||
pool.connections.dead.length.should.eql(0);
|
||||
|
||||
// should skip the first, but create a new for the second
|
||||
pool.setHosts([host, host2]);
|
||||
pool.connections.alive.length.should.eql(2);
|
||||
pool.connections.dead.length.should.eql(0);
|
||||
|
||||
// a new connection should have been created
|
||||
pool.index[host2.toString()].should.not.be.exactly(connection2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Connection selection', function () {
|
||||
var pool, host, host2;
|
||||
|
||||
beforeEach(function () {
|
||||
pool = new ConnectionPool({});
|
||||
|
||||
host = new Host('localhost:9200');
|
||||
host2 = new Host('localhost:9201');
|
||||
|
||||
pool.setHosts([
|
||||
host,
|
||||
host2
|
||||
]);
|
||||
});
|
||||
|
||||
it('detects if the selector is async', function (done) {
|
||||
pool.selector = function (list, cb) {
|
||||
cb.should.have.type('function');
|
||||
cb();
|
||||
};
|
||||
|
||||
pool.select(function (err) {
|
||||
if (err) { throw err; }
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('detects if the selector is not async', function (done) {
|
||||
pool.selector = function (list) {
|
||||
arguments.should.have.length(1);
|
||||
};
|
||||
|
||||
pool.select(function (err) {
|
||||
if (err) { throw err; }
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sync selectors should still return async', function (done) {
|
||||
pool.selector = function (list) {
|
||||
return list[0];
|
||||
};
|
||||
|
||||
var selected = null;
|
||||
|
||||
pool.select(function (err, selection) {
|
||||
if (err) { throw err; }
|
||||
selection.host.should.be.exactly(host);
|
||||
selected = selection;
|
||||
done();
|
||||
});
|
||||
|
||||
should(selected).be.exactly(null);
|
||||
});
|
||||
|
||||
it('should catch errors in sync selectors', function (done) {
|
||||
pool.selector = function (list) {
|
||||
return JSON.notAMethod();
|
||||
};
|
||||
|
||||
pool.select(function (err, selection) {
|
||||
should(err).Error;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should automatically select the first dead connection when there no living connections', function (done) {
|
||||
pool.connections.alive = [];
|
||||
pool.connections.dead = [1, 2, 3];
|
||||
|
||||
pool.select(function (err, selection) {
|
||||
selection.should.be.exactly(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Connection state management', function () {
|
||||
var pool, host, host2, connection, connection2;
|
||||
|
||||
beforeEach(function () {
|
||||
pool = new ConnectionPool({});
|
||||
|
||||
host = new Host('localhost:9200');
|
||||
host2 = new Host('localhost:9201');
|
||||
|
||||
pool.setHosts([
|
||||
host,
|
||||
host2
|
||||
]);
|
||||
|
||||
connection = pool.index[host2.toString()];
|
||||
connection2 = pool.index[host2.toString()];
|
||||
|
||||
pool.connections.alive.should.have.length(2);
|
||||
pool.connections.dead.should.have.length(0);
|
||||
});
|
||||
|
||||
it('moves an alive connection to dead', function () {
|
||||
connection.setStatus('dead');
|
||||
|
||||
pool.connections.alive.should.have.length(1);
|
||||
pool.connections.dead.should.have.length(1);
|
||||
});
|
||||
|
||||
it('moves a dead connection to the end of the dead list when it re-dies', function () {
|
||||
connection.setStatus('dead');
|
||||
connection2.setStatus('dead');
|
||||
|
||||
// connection is at the front of the line
|
||||
pool.connections.dead[0].should.be.exactly(connection);
|
||||
// it re-dies
|
||||
connection.setStatus('dead');
|
||||
// connection2 is now at the front of the list
|
||||
pool.connections.dead[0].should.be.exactly(connection2);
|
||||
});
|
||||
|
||||
it('moves a does nothing when a connection is re-alive', function () {
|
||||
var last = pool.connections.alive[pool.connections.alive.length - 1];
|
||||
var first = pool.connections.alive[0];
|
||||
|
||||
last.should.not.be.exactly(first);
|
||||
|
||||
// first re-alives
|
||||
first.setStatus('alive');
|
||||
pool.connections.alive[0].should.be.exactly(first);
|
||||
pool.connections.alive[pool.connections.alive.length - 1].should.be.exactly(last);
|
||||
|
||||
// last re-alives
|
||||
last.setStatus('alive');
|
||||
pool.connections.alive[0].should.be.exactly(first);
|
||||
pool.connections.alive[pool.connections.alive.length - 1].should.be.exactly(last);
|
||||
});
|
||||
|
||||
it('removes all its connection when it closes, causing them to be closed', function () {
|
||||
pool.close();
|
||||
pool.connections.alive.should.have.length(0);
|
||||
pool.connections.dead.should.have.length(0);
|
||||
|
||||
connection.status.should.eql('closed');
|
||||
connection2.status.should.eql('closed');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
25
test/unit/test_errors.js
Normal file
25
test/unit/test_errors.js
Normal file
@ -0,0 +1,25 @@
|
||||
var errors = require('../../src/lib/errors');
|
||||
var _ = require('lodash');
|
||||
|
||||
_.each(errors, function (CustomError, name) {
|
||||
if (name.charAt(0) !== '_') {
|
||||
describe(name, function () {
|
||||
it('extend the ErrorAbstract and Error classes', function () {
|
||||
var err = new CustomError();
|
||||
err.message.length.should.be.above(7);
|
||||
err.should.be.an.instanceOf(Error).and.an.instanceOf(errors._Abstract);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('Error Abstract', function () {
|
||||
it('provides a stack property in the browser', function () {
|
||||
var isBrowser = process.browser;
|
||||
process.browser = true;
|
||||
var err = new errors._Abstract();
|
||||
process.browser = isBrowser;
|
||||
|
||||
err.stack.should.be.exactly('');
|
||||
});
|
||||
});
|
||||
134
test/unit/test_host.js
Normal file
134
test/unit/test_host.js
Normal file
@ -0,0 +1,134 @@
|
||||
var Host = require('../../src/lib/host');
|
||||
var _ = require('lodash');
|
||||
var url = require('url');
|
||||
|
||||
describe('Host class', function () {
|
||||
describe('construction', function () {
|
||||
it('properly sets the defaults', function () {
|
||||
var host = new Host();
|
||||
host.should.eql({
|
||||
protocol: 'http',
|
||||
host: 'localhost',
|
||||
port: 9200,
|
||||
path: '/',
|
||||
auth: null,
|
||||
query: {}
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts a string for query', function () {
|
||||
var host = new Host({ query: 'beep=boop'});
|
||||
|
||||
host.query.should.eql({
|
||||
beep: 'boop'
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts other generic params', function () {
|
||||
var headers = { 'X-Special-Routing-Header': 'pie' };
|
||||
var host = new Host({ headers: headers });
|
||||
|
||||
host.headers.should.be.exactly(headers);
|
||||
});
|
||||
|
||||
it('accepts a string for the entire url', function () {
|
||||
var host = new Host('john:dude@pizza.com:420/pizza/cheese?shrooms=true');
|
||||
|
||||
host.should.eql({
|
||||
protocol: 'http',
|
||||
host: 'pizza.com',
|
||||
port: 420,
|
||||
path: '/pizza/cheese',
|
||||
auth: 'john:dude',
|
||||
query: {
|
||||
shrooms: 'true'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('based on the output from url.parse', function () {
|
||||
it('might cause weird things to happen', function () {
|
||||
var parsedUrl = url.parse('pizza.com:888');
|
||||
|
||||
// I imagine most people don't expect
|
||||
parsedUrl.should.include({
|
||||
protocol: 'pizza.com:',
|
||||
host: '888',
|
||||
});
|
||||
|
||||
var host = new Host(parsedUrl);
|
||||
host.protocol.should.eql('pizza.com');
|
||||
host.host.should.eql('888');
|
||||
});
|
||||
|
||||
it('will cause extra properties', function () {
|
||||
var host = new Host(url.parse('https://joe:diner@pizza.com:888/path?query=yes'));
|
||||
host.should.include({
|
||||
protocol: 'https',
|
||||
host: 'pizza.com',
|
||||
port: 888,
|
||||
path: '/path',
|
||||
auth: 'joe:diner',
|
||||
query: {
|
||||
query: 'yes'
|
||||
}
|
||||
});
|
||||
|
||||
_.keys(host).should.include('slashes', 'hash', 'href', 'search');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores anything that\'s not a string or object-y', function () {
|
||||
var host = new Host(1234);
|
||||
|
||||
host.should.eql({
|
||||
protocol: 'http',
|
||||
host: 'localhost',
|
||||
port: 9200,
|
||||
path: '/',
|
||||
auth: null,
|
||||
query: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#makeUrl', function () {
|
||||
it('merges parameters', function () {
|
||||
var host = new Host({
|
||||
path: '/prefix',
|
||||
query: {
|
||||
user_id: 123
|
||||
}
|
||||
});
|
||||
|
||||
host.makeUrl({
|
||||
path: '/this and that',
|
||||
query: {
|
||||
param: 1
|
||||
}
|
||||
}).should.eql('http://localhost:9200/prefix/this and that?param=1&user_id=123');
|
||||
});
|
||||
|
||||
it('ensures that path starts with a forward-slash', function () {
|
||||
var host = new Host();
|
||||
host.path = 'prefix';
|
||||
|
||||
host.makeUrl({ path: '/this and that'})
|
||||
.should.eql('http://localhost:9200/prefix/this and that');
|
||||
});
|
||||
|
||||
it('does not try to prevent double forward-slashes', function () {
|
||||
var host = new Host({ path: 'prefix/' });
|
||||
|
||||
host.makeUrl({ path: '/this and that'})
|
||||
.should.eql('http://localhost:9200/prefix//this and that');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toString', function () {
|
||||
it('just calls makeUrl with no parameters', function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
58
test/unit/test_http_connection.js
Normal file
58
test/unit/test_http_connection.js
Normal file
@ -0,0 +1,58 @@
|
||||
describe('Http Connector', function () {
|
||||
|
||||
var Host = require('../../src/lib/host');
|
||||
var HttpConnection = require('../../src/lib/connectors/http');
|
||||
|
||||
describe('#makeReqParams', function () {
|
||||
|
||||
it('properly reads the host object', function () {
|
||||
var host = new Host('john:dude@pizza.com:9200/pizza/cheese?shrooms=true');
|
||||
var con = new HttpConnection(host, {});
|
||||
var reqParams = con.makeReqParams();
|
||||
|
||||
reqParams.should.eql({
|
||||
method: 'GET',
|
||||
protocol: 'http:',
|
||||
auth: 'john:dude',
|
||||
hostname: 'pizza.com',
|
||||
port: 9200,
|
||||
path: '/pizza/cheese?shrooms=true',
|
||||
headers: host.headers,
|
||||
agent: con.agent
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts merges a query object with the hosts\'', function () {
|
||||
var con = new HttpConnection(new Host({
|
||||
query: {
|
||||
user_id: 123
|
||||
}
|
||||
}));
|
||||
|
||||
var reqParams = con.makeReqParams({
|
||||
query: {
|
||||
jvm: 'yes'
|
||||
}
|
||||
});
|
||||
|
||||
reqParams.should.include({
|
||||
path: '/?jvm=yes&user_id=123'
|
||||
});
|
||||
});
|
||||
|
||||
// it('works with an empty query', function () {
|
||||
// var reqParams = con.makeReqParams();
|
||||
|
||||
// reqParams.should.include({
|
||||
// method: 'GET',
|
||||
// path: '/'
|
||||
// });
|
||||
|
||||
// Object.keys(reqParams).should.not.include([
|
||||
// 'host', 'pathname', 'query'
|
||||
// ]);
|
||||
// });
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
259
test/unit/test_log.js
Normal file
259
test/unit/test_log.js
Normal file
@ -0,0 +1,259 @@
|
||||
var Log = require('../../src/lib/log');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('Log class', function () {
|
||||
describe('::parseLevels', function () {
|
||||
it('accepts a string and returns it and the other levels below it', function () {
|
||||
Log.parseLevels('trace').should.eql([
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'debug',
|
||||
'trace'
|
||||
]);
|
||||
});
|
||||
|
||||
it('accepts and validates an array of levels', function () {
|
||||
Log.parseLevels(['warning', 'info']).should.eql(['warning', 'info']);
|
||||
});
|
||||
|
||||
it('throws an error when an invalid string is supplied', function () {
|
||||
(function () {
|
||||
Log.parseLevels('INVALID');
|
||||
}).should.throw(/invalid logging level/);
|
||||
});
|
||||
|
||||
it('throws an error when an invalid string is supplied in side an array', function () {
|
||||
(function () {
|
||||
Log.parseLevels(['error', 'INVALID']);
|
||||
}).should.throw(/invalid logging level/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#addOutput', function () {
|
||||
var log;
|
||||
|
||||
Log.loggers.stub = function (log, config) {
|
||||
this.config = config;
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('returns the newly created logger', function () {
|
||||
log.addOutput({ type: 'stub' }).should.be.an.instanceOf(Log.loggers.stub);
|
||||
});
|
||||
|
||||
it('Accepts a config object with `level: "{{level}}"`', function () {
|
||||
var logger = log.addOutput({
|
||||
type: 'stub',
|
||||
level: 'warning'
|
||||
});
|
||||
|
||||
logger.config.should.include({
|
||||
levels: [
|
||||
'error', 'warning'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('Accepts a config object with `level: ["{{level}}"]`', function () {
|
||||
var logger = log.addOutput({
|
||||
type: 'stub',
|
||||
level: ['warning']
|
||||
});
|
||||
|
||||
logger.config.should.include({
|
||||
levels: [
|
||||
'warning'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('Accepts a config object with `levels: "{{level}}"`', function () {
|
||||
var logger = log.addOutput({
|
||||
type: 'stub',
|
||||
levels: 'warning'
|
||||
});
|
||||
|
||||
logger.config.should.include({
|
||||
levels: [
|
||||
'error', 'warning'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('Accepts a config object with `levels: ["{{level}}"]`', function () {
|
||||
var logger = log.addOutput({
|
||||
type: 'stub',
|
||||
level: ['warning']
|
||||
});
|
||||
|
||||
logger.config.should.include({
|
||||
levels: [
|
||||
'warning'
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#join', function () {
|
||||
it('joins strings together with spaces', function () {
|
||||
Log.join(['foo', 'bar']).should.eql('foo bar');
|
||||
});
|
||||
it('stringifies objects', function () {
|
||||
Log.join([{ foo: 'bar' }]).should.eql('{ foo: \'bar\' }\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('instance without any outputs', function () {
|
||||
var log;
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('should not emit any events', function () {
|
||||
log.emit = function () {
|
||||
throw new Error('Emit should not be called');
|
||||
};
|
||||
|
||||
log.error();
|
||||
log.info();
|
||||
log.warning();
|
||||
log.debug();
|
||||
log.trace();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('instance without one output listening to all events', function () {
|
||||
var log, call;
|
||||
beforeEach(function () {
|
||||
call = void 0;
|
||||
log = new Log({
|
||||
log: [
|
||||
{
|
||||
type: function (log, config) {
|
||||
log.on('error', _.noop);
|
||||
log.on('warning', _.noop);
|
||||
log.on('info', _.noop);
|
||||
log.on('debug', _.noop);
|
||||
log.on('trace', _.noop);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
log.emit = function (eventName) {
|
||||
call = {
|
||||
event : eventName,
|
||||
args: Array.prototype.slice.call(arguments, 1)
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
it('should emit an "error" event with an Error object arg', function () {
|
||||
var err = new Error('error');
|
||||
log.error(err);
|
||||
call.event.should.eql('error');
|
||||
call.args[0].should.be.exactly(err);
|
||||
|
||||
call = void 0;
|
||||
|
||||
log.error('error');
|
||||
call.event.should.eql('error');
|
||||
call.args[0].should.be.an.instanceOf(Error);
|
||||
call.args[0].message.should.eql('error');
|
||||
});
|
||||
|
||||
it('should emit a "warning" event with a single message arg for #warning calls', function () {
|
||||
log.warning('shit!');
|
||||
call.event.should.eql('warning');
|
||||
call.args.should.have.length(1);
|
||||
call.args[0].should.eql('shit!');
|
||||
});
|
||||
|
||||
it('should emit a "info" event with a single message arg for #info calls', function () {
|
||||
log.info('look out!');
|
||||
call.event.should.eql('info');
|
||||
call.args.should.have.length(1);
|
||||
call.args[0].should.eql('look out!');
|
||||
});
|
||||
|
||||
it('should emit a "debug" event with a single message arg for #debug calls', function () {
|
||||
log.debug('here');
|
||||
call.event.should.eql('debug');
|
||||
call.args.should.have.length(1);
|
||||
call.args[0].should.eql('here');
|
||||
});
|
||||
|
||||
it('should emit a trace event for trace events, with message and curlCall args', function () {
|
||||
log.trace('GET', 'http://localhost:9200/_cluster/nodes', '', '', 200);
|
||||
call.event.should.eql('trace');
|
||||
call.args.should.have.length(2);
|
||||
call.args[0].should.match(/^<- 200/);
|
||||
call.args[1].should.match(/^curl /);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('looks for output config options at config.log', function () {
|
||||
var log = new Log({ log: { type: process.browser ? 'console' : 'stdio', level: 'error' } });
|
||||
log.listenerCount('error').should.eql(1);
|
||||
log.listenerCount('warning').should.eql(0);
|
||||
log.listenerCount('info').should.eql(0);
|
||||
log.listenerCount('debug').should.eql(0);
|
||||
log.listenerCount('trace').should.eql(0);
|
||||
});
|
||||
|
||||
it('accepts a string and treat it as a log level', function () {
|
||||
var log = new Log({ log: 'error' });
|
||||
log.listenerCount('error').should.eql(1);
|
||||
log.listenerCount('warning').should.eql(0);
|
||||
log.listenerCount('info').should.eql(0);
|
||||
log.listenerCount('debug').should.eql(0);
|
||||
log.listenerCount('trace').should.eql(0);
|
||||
});
|
||||
|
||||
it('accepts an array of strings and treat it as a log level config', function () {
|
||||
var log = new Log({ log: ['error', 'trace'] });
|
||||
log.listenerCount('error').should.eql(1);
|
||||
log.listenerCount('warning').should.eql(0);
|
||||
log.listenerCount('info').should.eql(0);
|
||||
log.listenerCount('debug').should.eql(0);
|
||||
log.listenerCount('trace').should.eql(1);
|
||||
});
|
||||
|
||||
it('accepts an array of output config objects', function () {
|
||||
var log = new Log({ log: [{ level: 'error' }, { level: 'trace'}] });
|
||||
log.listenerCount('error').should.eql(2);
|
||||
log.listenerCount('warning').should.eql(1);
|
||||
log.listenerCount('info').should.eql(1);
|
||||
log.listenerCount('debug').should.eql(1);
|
||||
log.listenerCount('trace').should.eql(1);
|
||||
});
|
||||
|
||||
it('rejects numbers and other truthy data-types', function () {
|
||||
(function () {
|
||||
var log = new Log({ log: 1515 });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
(function () {
|
||||
var log = new Log({ log: /regexp/ });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
(function () {
|
||||
var log = new Log({ log: new Date() });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
(function () {
|
||||
var log = new Log({ log: [1515] });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
(function () {
|
||||
var log = new Log({ log: [/regexp/] });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
(function () {
|
||||
var log = new Log({ log: [new Date()] });
|
||||
}).should.throw(/invalid logging output config/i);
|
||||
});
|
||||
});
|
||||
});
|
||||
19
test/unit/test_random_selector.js
Normal file
19
test/unit/test_random_selector.js
Normal file
@ -0,0 +1,19 @@
|
||||
var randomSelector = require('../../src/lib/selectors/random');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('Random Selector', function () {
|
||||
it('chooses a selection by random', function () {
|
||||
var log = { a: 0, b: 0, c: 0 };
|
||||
var choices = _.keys(log);
|
||||
|
||||
_.times(1000, function () {
|
||||
var choice = randomSelector(choices);
|
||||
log[choice]++;
|
||||
});
|
||||
|
||||
_.filter(log, function (count) {
|
||||
return count < 200 || count > 400;
|
||||
}).should.have.length(0);
|
||||
|
||||
});
|
||||
});
|
||||
16
test/unit/test_round_robin_selector.js
Normal file
16
test/unit/test_round_robin_selector.js
Normal file
@ -0,0 +1,16 @@
|
||||
var selector = require('../../src/lib/selectors/round_robin');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('Round Robin Selector', function () {
|
||||
it('chooses options in order', function () {
|
||||
var options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
|
||||
var expected = _.clone(options);
|
||||
var selections = [];
|
||||
|
||||
_.times(options.length, function () {
|
||||
selections.push(selector(options));
|
||||
});
|
||||
|
||||
selections.should.eql(expected);
|
||||
});
|
||||
});
|
||||
71
test/unit/test_stdio_logger.js
Normal file
71
test/unit/test_stdio_logger.js
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
var es = require('../../src/elasticsearch');
|
||||
var Log = require('../../src/lib/log');
|
||||
var StdioLogger = require('../../src/lib/loggers/stdio');
|
||||
var _ = require('../../src/lib/utils');
|
||||
var expect = require('expect.js');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
describe('Stdio Logger', function () {
|
||||
var log, logger;
|
||||
|
||||
// pulled from chalk's stripColor function.
|
||||
var hasColorRE = /\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/;
|
||||
|
||||
function listenerCount(emitter, event) {
|
||||
if (EventEmitter.listenerCount) {
|
||||
return EventEmitter.listenerCount(emitter, event);
|
||||
} else {
|
||||
return emitter.listeners(event).length;
|
||||
}
|
||||
}
|
||||
|
||||
describe('pays attention to the level setting', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
|
||||
log.emit = function (name/*, ...args */) {
|
||||
log._emission = {
|
||||
name: name,
|
||||
args: Array.prototype.slice(arguments, 1)
|
||||
};
|
||||
};
|
||||
|
||||
// new logger in warning mode
|
||||
logger = new StdioLogger(log, {
|
||||
levels: Log.parseLevels('trace')
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
log.close();
|
||||
});
|
||||
|
||||
it('listenes for all the events', function () {
|
||||
listenerCount(log, 'error').should.eql(1);
|
||||
listenerCount(log, 'warning').should.eql(1);
|
||||
listenerCount(log, 'info').should.eql(1);
|
||||
listenerCount(log, 'debug').should.eql(1);
|
||||
listenerCount(log, 'trace').should.eql(1);
|
||||
});
|
||||
|
||||
it('emits events because something is listening', function () {
|
||||
log.error(new Error('error message'));
|
||||
log._emission.name.should.eql('error');
|
||||
|
||||
log.warning('warning');
|
||||
log._emission.name.should.eql('warning');
|
||||
|
||||
log.info('info');
|
||||
log._emission.name.should.eql('info');
|
||||
|
||||
log.debug('debug');
|
||||
log._emission.name.should.eql('debug');
|
||||
|
||||
log.trace('GET', {}, '', '', 200);
|
||||
log._emission.name.should.eql('trace');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
294
test/unit/test_utils.js
Normal file
294
test/unit/test_utils.js
Normal file
@ -0,0 +1,294 @@
|
||||
var _ = require('../../src/lib/utils');
|
||||
var should = require('should');
|
||||
|
||||
describe('Utils', function () {
|
||||
|
||||
describe('Additional Type Checkers', function () {
|
||||
_.forEach({
|
||||
Object: {
|
||||
is: [[], /regexp/]
|
||||
},
|
||||
PlainObject: {
|
||||
is: [{}, {}]
|
||||
},
|
||||
String: {
|
||||
is: ['steamy', 'poop'],
|
||||
not: {}
|
||||
},
|
||||
Array: {
|
||||
is: [['im'], ['usefull']],
|
||||
},
|
||||
Finite: {
|
||||
is: [11123, 666],
|
||||
not: Infinity
|
||||
},
|
||||
Function: {
|
||||
is: [console.error, console.log],
|
||||
},
|
||||
RegExp: {
|
||||
is: [/.*/, new RegExp('a')],
|
||||
}
|
||||
},
|
||||
function (thing, name) {
|
||||
|
||||
describe('#isArrayOf' + name, function (test) {
|
||||
it('likes arrays of ' + name, function () {
|
||||
should(_['isArrayOf' + name + 's'](thing.is)).be.ok;
|
||||
});
|
||||
|
||||
it('dislikes when there is even one non ' + name, function () {
|
||||
// notice a string in the array
|
||||
thing.is.push(thing.not || ' not ');
|
||||
should(_['isArrayOf' + name + 's'](thing.is)).not.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isNumeric', function () {
|
||||
it('likes integer literals', function () {
|
||||
should(_.isNumeric('-10')).be.ok;
|
||||
should(_.isNumeric('0')).be.ok;
|
||||
should(_.isNumeric('5')).be.ok;
|
||||
should(_.isNumeric(-16)).be.ok;
|
||||
should(_.isNumeric(0)).be.ok;
|
||||
should(_.isNumeric(32)).be.ok;
|
||||
should(_.isNumeric('040')).be.ok;
|
||||
should(_.isNumeric(0144)).be.ok;
|
||||
should(_.isNumeric('0xFF')).be.ok;
|
||||
should(_.isNumeric(0xFFF)).be.ok;
|
||||
});
|
||||
|
||||
it('likes float literals', function () {
|
||||
should(_.isNumeric('-1.6')).be.ok;
|
||||
should(_.isNumeric('4.536')).be.ok;
|
||||
should(_.isNumeric(-2.6)).be.ok;
|
||||
should(_.isNumeric(3.1415)).be.ok;
|
||||
should(_.isNumeric(8e5)).be.ok;
|
||||
should(_.isNumeric('123e-2')).be.ok;
|
||||
});
|
||||
|
||||
it('dislikes non-numeric stuff', function () {
|
||||
should(_.isNumeric('')).not.be.ok;
|
||||
should(_.isNumeric(' ')).not.be.ok;
|
||||
should(_.isNumeric('\t\t')).not.be.ok;
|
||||
should(_.isNumeric('abcdefghijklm1234567890')).not.be.ok;
|
||||
should(_.isNumeric('xabcdefx')).not.be.ok;
|
||||
should(_.isNumeric(true)).not.be.ok;
|
||||
should(_.isNumeric(false)).not.be.ok;
|
||||
should(_.isNumeric('bcfed5.2')).not.be.ok;
|
||||
should(_.isNumeric('7.2acdgs')).not.be.ok;
|
||||
should(_.isNumeric(undefined)).not.be.ok;
|
||||
should(_.isNumeric(null)).not.be.ok;
|
||||
should(_.isNumeric(NaN)).not.be.ok;
|
||||
should(_.isNumeric(Infinity)).not.be.ok;
|
||||
should(_.isNumeric(Number.POSITIVE_INFINITY)).not.be.ok;
|
||||
should(_.isNumeric(Number.NEGATIVE_INFINITY)).not.be.ok;
|
||||
should(_.isNumeric(new Date(2009, 1, 1))).not.be.ok;
|
||||
should(_.isNumeric([])).not.be.ok;
|
||||
should(_.isNumeric([1, 2, 3, 4])).not.be.ok;
|
||||
should(_.isNumeric({})).not.be.ok;
|
||||
should(_.isNumeric(function () {})).not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#isInterval', function () {
|
||||
_.forEach({
|
||||
M: 'months',
|
||||
w: 'weeks',
|
||||
d: 'days',
|
||||
h: 'hours',
|
||||
m: 'minutes',
|
||||
s: 'seconds',
|
||||
y: 'years'
|
||||
},
|
||||
function (name, unit) {
|
||||
it('likes ' + name, function () {
|
||||
should(_.isInterval('1' + unit)).be.ok;
|
||||
});
|
||||
|
||||
it('likes decimal ' + name, function () {
|
||||
should(_.isInterval('1.5' + unit)).be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('dislikes more than one unit', function () {
|
||||
should(_.isInterval('1my')).not.be.ok;
|
||||
});
|
||||
|
||||
it('dislikes spaces', function () {
|
||||
should(_.isInterval('1 m')).not.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('String Transformers', function () {
|
||||
|
||||
describe('#camelCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
_.camelCase('Neil Patrick.Harris-is_a.dog').should.eql('neilPatrickHarrisIsADog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
_.camelCase('Json_parser').should.eql('jsonParser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
_.camelCase('_thing_one_').should.eql('thingOne');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#studlyCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
_.studlyCase('Neil Patrick.Harris-is_a.dog').should.eql('NeilPatrickHarrisIsADog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
_.studlyCase('Json_parser').should.eql('JsonParser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
_.studlyCase('_thing_one_').should.eql('ThingOne');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#snakeCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
_.snakeCase('Neil Patrick.Harris-is_a.dog').should.eql('neil_patrick_harris_is_a_dog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
_.snakeCase('Json_parser').should.eql('json_parser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
_.snakeCase('_thing_one_').should.eql('thing_one');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toLowerString', function () {
|
||||
it('transforms normal strings', function () {
|
||||
_.toLowerString('PASTA').should.eql('pasta');
|
||||
});
|
||||
|
||||
it('ignores long form empty vals (null, false, undef)', function () {
|
||||
_.toLowerString(null).should.eql('');
|
||||
_.toLowerString(false).should.eql('');
|
||||
_.toLowerString(void 0).should.eql('');
|
||||
});
|
||||
|
||||
it('uses the objects own toString', function () {
|
||||
_.toLowerString(['A', 'B']).should.eql('a,b');
|
||||
});
|
||||
|
||||
it('sorta kinda works on objects', function () {
|
||||
_.toLowerString({a: 'thing'}).should.eql('[object object]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toUpperString', function () {
|
||||
it('transforms normal strings', function () {
|
||||
_.toUpperString('PASTA').should.eql('PASTA');
|
||||
});
|
||||
|
||||
it('ignores long form empty vals (null, false, undef)', function () {
|
||||
_.toUpperString(null).should.eql('');
|
||||
_.toUpperString(false).should.eql('');
|
||||
_.toUpperString(void 0).should.eql('');
|
||||
});
|
||||
|
||||
it('uses the objects own toString', function () {
|
||||
_.toUpperString(['A', 'B']).should.eql('A,B');
|
||||
});
|
||||
|
||||
it('sorta kinda works on objects', function () {
|
||||
_.toUpperString({a: 'thing'}).should.eql('[OBJECT OBJECT]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#repeat', function () {
|
||||
it('repeats strings', function () {
|
||||
_.repeat(' ', 5).should.eql(' ');
|
||||
_.repeat('foobar', 2).should.eql('foobarfoobar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#ucfirst', function () {
|
||||
it('only capitalized the first letter, lowercases everything else', function () {
|
||||
_.ucfirst('ALGER').should.eql('Alger');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('#deepMerge', function () {
|
||||
|
||||
it('returns the same object that was passed', function () {
|
||||
var obj = {
|
||||
foo: 'bar'
|
||||
};
|
||||
_.deepMerge(obj, { bar: 'baz' }).should.eql(obj);
|
||||
});
|
||||
|
||||
it('concats arrays', function () {
|
||||
var obj = {
|
||||
foo: ['bax', 'boz']
|
||||
};
|
||||
_.deepMerge(obj, { foo: ['boop'] });
|
||||
obj.foo.should.have.a.lengthOf(3);
|
||||
});
|
||||
|
||||
it('wont merge values of different types', function () {
|
||||
var obj = {
|
||||
foo: ['stop', 'foo', 'stahp']
|
||||
};
|
||||
_.deepMerge(obj, { foo: 'string' });
|
||||
obj.foo.should.have.a.lengthOf(3);
|
||||
});
|
||||
|
||||
it('works recursively', function () {
|
||||
var obj = {
|
||||
foo: 'bar',
|
||||
bax: {
|
||||
foo: ['bax', 'boz']
|
||||
}
|
||||
};
|
||||
_.deepMerge(obj, { bax: { foo: ['poo'] }});
|
||||
obj.bax.foo.should.have.a.lengthOf(3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#createArray', function () {
|
||||
it('accepts an array of things and simply returns a copy of it', function () {
|
||||
var inp = [{ a: 1 }, 'pizza'];
|
||||
var out = _.createArray(inp);
|
||||
out.should.eql(inp);
|
||||
out.should.not.be.exactly(inp);
|
||||
});
|
||||
it('accepts a primitive value and calls the the transform function', function (done) {
|
||||
var out = _.createArray('str', function (val) {
|
||||
val.should.be.exactly('str');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('wraps any non-array in an array', function () {
|
||||
_.createArray({}).should.eql([{}]);
|
||||
_.createArray('').should.eql(['']);
|
||||
_.createArray(123).should.eql([123]);
|
||||
_.createArray(/abc/).should.eql([/abc/]);
|
||||
_.createArray(false).should.eql([false]);
|
||||
});
|
||||
it('returns false when the transform function returns undefined', function () {
|
||||
_.createArray(['str', 1], function (val) {
|
||||
if (_.isString(val)) {
|
||||
return {
|
||||
val: val
|
||||
};
|
||||
}
|
||||
}).should.be.exactly(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,220 +0,0 @@
|
||||
var _ = require('../../src/lib/utils')
|
||||
, expect = require('expect.js');
|
||||
|
||||
describe('Utils', function () {
|
||||
|
||||
describe('Additional Type Checkers', function () {
|
||||
_.forEach({
|
||||
Object: {
|
||||
is: [[], console.log]
|
||||
},
|
||||
PlainObject: {
|
||||
is: [{}, {}]
|
||||
},
|
||||
String: {
|
||||
is: ['steamy', 'poop'],
|
||||
not: {}
|
||||
},
|
||||
Array: {
|
||||
is: [['im'], ['usefull']],
|
||||
},
|
||||
Finite: {
|
||||
is: [11123, 666],
|
||||
not: Infinity
|
||||
},
|
||||
Function: {
|
||||
is: [console.error, console.log],
|
||||
},
|
||||
RegExp: {
|
||||
is: [/.*/, new RegExp('a')],
|
||||
}
|
||||
},
|
||||
function (thing, name) {
|
||||
|
||||
describe('#isArrayOf' + name, function (test) {
|
||||
it('likes arrays of ' + name, function () {
|
||||
expect(_['isArrayOf' + name + 's'](thing.is)).to.be.true;
|
||||
});
|
||||
|
||||
it('dislikes when there is even one non ' + name, function () {
|
||||
// notice a string in the array
|
||||
thing.is.push(thing.not || ' not ');
|
||||
expect(_['isArrayOf' + name + 's'](thing.is)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isNumeric', function () {
|
||||
it('likes Infinity', function () {
|
||||
expect(_.isNumeric(Infinity)).to.be.true;
|
||||
});
|
||||
|
||||
it('likes strings', function () {
|
||||
expect(_.isNumeric('100')).to.be.true;
|
||||
});
|
||||
|
||||
it('likes integers', function () {
|
||||
expect(_.isNumeric(100)).to.be.true;
|
||||
});
|
||||
|
||||
it('likes floats', function () {
|
||||
expect(_.isNumeric(100.1)).to.be.true;
|
||||
});
|
||||
|
||||
it('likes exponentials', function () {
|
||||
expect(_.isNumeric(100e1)).to.be.true;
|
||||
});
|
||||
|
||||
it('likes hexidecimals', function () {
|
||||
expect(_.isNumeric(0x100)).to.be.true;
|
||||
});
|
||||
|
||||
it('likes imaginary numbers', function () {
|
||||
expect(_.isNumeric('yeah right')).to.be.false;
|
||||
});
|
||||
|
||||
it('dislikes strings with words', function () {
|
||||
expect(_.isNumeric('100heat')).to.be.false;
|
||||
});
|
||||
|
||||
it('dislikes strings with words even if they are seperate', function () {
|
||||
expect(_.isNumeric('100 pasta')).to.be.false;
|
||||
});
|
||||
|
||||
it('dislikes null', function () {
|
||||
expect(_.isNumeric(null)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#isInterval', function () {
|
||||
_.forEach({
|
||||
M: 'months',
|
||||
w: 'weeks',
|
||||
d: 'days',
|
||||
h: 'hours',
|
||||
m: 'minutes',
|
||||
s: 'seconds',
|
||||
y: 'years'
|
||||
},
|
||||
function (name, unit) {
|
||||
it('likes ' + name, function () { expect(_.isInterval('1' + unit)).to.be.true; });
|
||||
it('likes decimal ' + name, function () { expect(_.isInterval('1.5' + unit)).to.be.true; });
|
||||
});
|
||||
|
||||
it('dislikes more than one unit', function () {
|
||||
expect(_.isInterval('1my')).to.be.false;
|
||||
});
|
||||
|
||||
it('dislikes spaces', function () {
|
||||
expect(_.isInterval('1 m')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('String Transformers', function () {
|
||||
|
||||
describe('#camelCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
expect(_.camelCase('Neil Patrick.Harris-is_a.dog')).to.eql('neilPatrickHarrisIsADog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
expect(_.camelCase('Json_parser')).to.eql('jsonParser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
expect(_.camelCase('_thing_one_')).to.eql('thingOne');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#studlyCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
expect(_.studlyCase('Neil Patrick.Harris-is_a.dog')).to.eql('NeilPatrickHarrisIsADog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
expect(_.studlyCase('Json_parser')).to.eql('JsonParser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
expect(_.studlyCase('_thing_one_')).to.eql('ThingOne');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#snakeCase', function () {
|
||||
it('find spaces, underscores, and other natural word breaks', function () {
|
||||
expect(_.snakeCase('Neil Patrick.Harris-is_a.dog')).to.eql('neil_patrick_harris_is_a_dog');
|
||||
});
|
||||
|
||||
it('ignores abreviations', function () {
|
||||
expect(_.snakeCase('Json_parser')).to.eql('json_parser');
|
||||
});
|
||||
|
||||
it('handles trailing _', function () {
|
||||
expect(_.snakeCase('_thing_one_')).to.eql('thing_one');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toLowerString', function () {
|
||||
it('transforms normal strings', function () {
|
||||
expect(_.toLowerString('PASTA')).to.eql('pasta');
|
||||
});
|
||||
|
||||
it('ignores long form empty vals (null, false, undef)', function () {
|
||||
expect(_.toLowerString(null)).to.eql('');
|
||||
expect(_.toLowerString(false)).to.eql('');
|
||||
expect(_.toLowerString(void 0)).to.eql('');
|
||||
});
|
||||
|
||||
it('uses the objects own toString', function () {
|
||||
expect(_.toLowerString(['A', 'B'])).to.eql('a,b');
|
||||
});
|
||||
|
||||
it('sorta kinda works on objects', function () {
|
||||
expect(_.toLowerString({a: 'thing'})).to.eql('[object object]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#deepMerge', function () {
|
||||
|
||||
it('returns the same object that was passed', function () {
|
||||
var obj = {
|
||||
foo: 'bar'
|
||||
};
|
||||
expect(_.deepMerge(obj, { bar: 'baz' })).to.eql(obj);
|
||||
});
|
||||
|
||||
it('concats arrays', function () {
|
||||
var obj = {
|
||||
foo: ['bax', 'boz']
|
||||
};
|
||||
_.deepMerge(obj, { foo: ['boop'] });
|
||||
expect(obj.foo).to.have.length(3);
|
||||
});
|
||||
|
||||
it('wont merge values of different types', function () {
|
||||
var obj = {
|
||||
foo: ['stop', 'foo', 'stahp']
|
||||
};
|
||||
_.deepMerge(obj, { foo: 'string' });
|
||||
expect(obj.foo).to.have.length(3);
|
||||
});
|
||||
|
||||
it('works recursively', function () {
|
||||
var obj = {
|
||||
foo: 'bar',
|
||||
bax: {
|
||||
foo: ['bax', 'boz']
|
||||
}
|
||||
};
|
||||
_.deepMerge(obj, { bax: { foo: ['poo'] }});
|
||||
expect(obj.bax.foo).to.have.length(3);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user