Files
elasticsearch-js/test/unit/test_client_action.js
Spencer Alger b36a6590e8 Stopped overriding the ping method provided by the 1.0 API, but it didn't match the "castExists" regex so...
Fixed the lack of castExists for ping/1.0 by removing castExists all together from the API. It was the
case anyway that all HEAD requests needed to be cast, so now the clientAction module will set that
param when the spec is set to make HEAD requests. The transport.request still expects that parameter.

Switched the apiVersion implementation to use funcEnum, and exposed the options on the Client constructor.
Docs to come.
2014-01-16 15:43:33 -07:00

816 lines
20 KiB
JavaScript

var ca = require('../../src/lib/client_action');
var expect = require('expect.js');
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) {
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({
type: ['type1', 'type2']
}, function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
it('uses the default value for optional params', function (done) {
action({
index: 'index1',
id: '1'
}, function (err, params) {
if (err) { throw err; }
expect(params.path).to.be('/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; }
expect(params.path).to.be('/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; }
expect(params.query).to.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; }
expect(params.query).to.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; }
expect(params.query).to.eql({
a: 'pizza'
});
done();
});
});
it('does not include non-query param keys', function (done) {
action({
a: 'pizza',
b: '3w',
q: 'beep',
body: '{ "mmm": "json" }',
requestTimeout: 1000,
method: 'head',
ignore: 201
},
function (err, params) {
if (err) { throw err; }
expect(params.query).to.eql({
a: 'pizza',
b: '3w',
q: 'beep'
});
done();
});
});
it('enforces required params', function (done) {
action({
b: '3w'
},
function (err, params) {
expect(err).to.be.a(TypeError);
done();
});
});
});
});