Files
elasticsearch-js/test/unit/specs/client_action.js
2016-05-19 14:33:04 -07:00

868 lines
21 KiB
JavaScript

var ca = require('../../../src/lib/client_action').factory;
var proxy = require('../../../src/lib/client_action').proxyFactory;
var expect = require('expect.js');
var _ = require('lodash');
var Promise = require('bluebird');
/**
* 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 Promise.resolve(params);
}
},
defer: function () {
return Promise.defer();
}
}
};
}
/**
* 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 proxyFactory 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(proxy(fn, spec || {}), mockClient());
}
describe('Client Action runner', function () {
var action;
// used to check that params are not clobbered
var params = (function () {
var _stash = {};
afterEach(function () { _stash = {}; });
var make = function (params) {
_stash.orig = params;
_stash.copy = _.clone(params);
return params;
};
make.check = function () {
expect(_stash.orig).to.eql(_stash.copy);
};
return make;
}());
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) {
expect(params.query).to.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');
});
expect(function () {
action({}, function () {});
}).to.throwError('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) {
expect(client.transport.request).to.be.a('function');
done();
});
});
it('handles passing just the callback', function () {
var action = makeClientActionProxy(function (params, cb) {
expect(_.isObject(params)).to.be.ok();
expect(cb).to.be.a('function');
});
action(function () {});
});
it('supports a param transformation function', function () {
var action = makeClientActionProxy(function (params, cb) {
expect(params).to.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;
});
expect(action()).to.be(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; }
expect(params.query.one).to.eql(1500);
expect(params.query.two).to.eql('500');
expect(params.query.three).to.eql('15m');
done();
});
});
it('rejects date values', function (done) {
action({
one: new Date()
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects array', function (done) {
action({
one: ['one'],
two: [1304]
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects object', function (done) {
action({
one: { but: 'duration' }
}, function (err, params) {
expect(err).to.be.a(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; }
expect(params.query).to.eql({
one: 'some,strings',
two: 1430,
three: 'some,strings'
});
done();
});
});
it('it rejects regexp', function (done) {
action({
one: /regexp!/g
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('it rejects objects', function (done) {
action({
one: {
pasta: 'sauce'
}
}, function (err, params) {
expect(err).to.be.a(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; }
expect(params.query.one).to.eql('opt');
done();
});
});
it('accepts any value kind of in the list', function (done) {
action({
one: 150
}, function (err, params) {
if (err) { throw err; }
expect(params.query.one).to.be('150');
done();
});
});
it('it rejects things not in the list', function (done) {
action({
one: 'not an opt'
}, function (err, params) {
expect(err).to.be.a(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; }
expect(params.query.one).to.be(false);
expect(params.query.two).to.be(false);
expect(params.query.three).to.be(false);
expect(params.query.four).to.be(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; }
expect(params.query.one).to.be(true);
expect(params.query.two).to.be(true);
expect(params.query.three).to.be(true);
expect(params.query.four).to.be(true);
expect(params.query.five).to.be(true);
expect(params.query.six).to.be(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; }
expect(params.query.one).to.be(42);
expect(params.query.two).to.be(-69);
expect(params.query.three).to.be(15);
expect(params.query.four).to.be(-100);
expect(params.query.five).to.be(255);
expect(params.query.six).to.be(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; }
expect(params.query.one).to.be(-1.6);
expect(params.query.two).to.be(4.536);
expect(params.query.three).to.be(-2.6);
expect(params.query.four).to.be(3.1415);
expect(params.query.five).to.be(800000);
expect(params.query.six).to.be(1.23);
done();
});
});
it('rejects dates', function (done) {
action({
one: new Date()
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects objects', function (done) {
action({
one: {}
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects arrays', function (done) {
action({
one: []
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects regexp', function (done) {
action({
one: /pasta/g
}, function (err, params) {
expect(err).to.be.a(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; }
expect(params.query.one).to.be('42');
expect(params.query.two).to.be('-69');
expect(params.query.three).to.be('15');
expect(params.query.four).to.be('-100');
expect(params.query.five).to.be('0xFF');
expect(params.query.six).to.be('4095');
done();
});
});
it('rejects dates', function (done) {
action({
one: new Date()
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects objects', function (done) {
action({
one: {}
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects arrays', function (done) {
action({
one: []
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects regexp', function (done) {
action({
one: /pasta/g
}, function (err, params) {
expect(err).to.be.a(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('2013-03-01T01:10:00Z')
}, function (err, params) {
if (err) { throw err; }
expect(params.query.one).to.be('42');
expect(params.query.two).to.be('-69');
expect(params.query.three).to.be('15');
expect(params.query.four).to.be('' + now.getTime());
expect(params.query.five).to.be('1362100200000');
done();
});
});
it('rejects objects', function (done) {
action({
one: {}
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects arrays', function (done) {
action({
one: []
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('rejects regexp', function (done) {
action({
one: /pasta/g
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
});
});
describe('passing of control params from spec', function () {
it('passes bulkBody', function (done) {
var action = makeClientAction({
bulkBody: true
});
action({}, function (err, params) {
expect(params.bulkBody).to.be(true);
done();
});
});
it('sets castExists when the method in the spec is HEAD', function (done) {
var action = makeClientAction({
method: 'HEAD'
});
action({}, function (err, params) {
expect(params.castExists).to.be(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) {
expect(params.body).to.be(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) {
expect(err).to.be.a(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) {
expect(params.method).to.be('GET');
done();
});
});
it('uppercases and passed the default method', function (done) {
var action = makeClientAction({
method: 'POST'
});
action({}, function (err, params) {
expect(params.method).to.be('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) {
expect(params.ignore).to.eql([404]);
done();
});
});
});
describe('passing requestTimeout', function () {
it('passes passes the spec value by default', function (done) {
var action = makeClientAction({
requestTimeout: 100
});
action({}, function (err, params) {
expect(params.requestTimeout).to.be(100);
done();
});
});
it('passes the provided value', function (done) {
var action = makeClientAction({
requestTimeout: 100
});
action({ requestTimeout: 3000 }, function (err, params) {
expect(params.requestTimeout).to.be(3000);
done();
});
});
it('passes nothing be default', function (done) {
var action = makeClientAction({});
action({}, function (err, params) {
expect(params.requestTimeout).be(void 0);
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(params({
type: ['type1', 'type2']
}), function (err, resp) {
expect(err).to.be.a(TypeError);
params.check();
done();
});
});
it('uses the default value for optional params', function (done) {
action(params({
index: 'index1',
id: '1'
}), function (err, resp) {
if (err) { throw err; }
expect(resp.path).to.be('/index1/_all/1/');
params.check();
done();
});
});
it('casts both optional and required args', function (done) {
action(params({
index: ['index1', 'index2'],
id: '123',
type: ['_all', '-pizza'],
thing: 'poo'
}), function (err, resp) {
if (err) { throw err; }
expect(resp.path).to.be('/index1%2Cindex2/_all%2C-pizza/123/poo');
params.check();
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(params({
a: 'pizza',
b: '1M'
}), function (err, resp) {
if (err) { throw err; }
expect(resp.query).to.eql({
a: 'pizza',
b: '1M'
});
params.check();
done();
});
});
it('includes extra params', function (done) {
action(params({
a: 'pizza',
b: '3w',
c: 'popular',
}), function (err, resp) {
if (err) { throw err; }
expect(resp.query).to.eql({
a: 'pizza',
b: '3w',
c: 'popular'
});
params.check();
done();
});
});
it('excludes default values', function (done) {
action(params({
a: 'pizza',
b: '15m',
}), function (err, resp) {
if (err) { throw err; }
expect(resp.query).to.eql({
a: 'pizza'
});
params.check();
done();
});
});
it('does not include non-query param keys', function (done) {
action(params({
a: 'pizza',
b: '3w',
q: 'beep',
body: '{ "mmm": "json" }',
requestTimeout: 1000,
method: 'head',
ignore: 201
}), function (err, resp) {
if (err) { throw err; }
expect(resp.query).to.eql({
a: 'pizza',
b: '3w',
q: 'beep'
});
params.check();
done();
});
});
it('enforces required params', function (done) {
action(params({
b: '3w'
}), function (err, resp) {
expect(err).to.be.a(TypeError);
params.check();
done();
});
});
it('does not modify the incoming params object', function () {
var action = makeClientAction({
url: {
req: {
index: { type: 'string' }
},
fmt: '/<%= index %>'
}
});
action(params({
index: 'index'
}), function (err, resp) {
expect(resp).to.eql({
method: 'GET',
path: '/index',
query: {}
});
params.check();
});
});
});
});