Adding tests for the elasticsearch plugin

This commit is contained in:
Chris Cowan 2015-05-08 14:47:46 -07:00
parent 259b6f7910
commit 1d7749f3df
23 changed files with 1386 additions and 365 deletions

0
src/hapi/bin/kibana.js Normal file → Executable file
View file

View file

@ -55,6 +55,7 @@ module.exports = Joi.object({
cert: Joi.string(),
key: Joi.string()
}).default(),
minimumVerison: Joi.string().default('1.4.4')
}).default(),
logging: Joi.object({
quiet: Joi.boolean().default(false),

View file

@ -16,8 +16,6 @@ module.exports = new kibana.Plugin({
// Expose the client to the server
exposeClient(server);
// Set up the health check service
healthCheck(this, server);
createProxy(server, 'GET', '/elasticsearch/{paths*}');
createProxy(server, 'POST', '/elasticsearch/_mget');
@ -42,5 +40,8 @@ module.exports = new kibana.Plugin({
}
);
// Set up the health check service and start it.
healthCheck(this, server).start();
}
});

View file

@ -0,0 +1,38 @@
var _ = require('lodash');
_.mixin(require('lodash-deep'));
var esBool = require('./es_bool');
var versionMath = require('./version_math');
var SetupError = require('./setup_error');
module.exports = function (server) {
return function () {
var client = server.plugins.elasticsearch.client;
var minimumElasticsearchVersion = server.config().get('elasticsearch.minimumVerison');
return client.nodes.info()
.then(function (info) {
var badNodes = _.filter(info.nodes, function (node) {
// remove client nodes (Logstash)
var isClient = _.deepGet(node, 'attributes.client');
if (isClient != null && esBool(isClient) === true) {
return false;
}
// remove nodes that are gte the min version
var v = node.version.split('-')[0];
return !versionMath.gte(minimumElasticsearchVersion, v);
});
if (!badNodes.length) return true;
var badNodeNames = badNodes.map(function (node) {
return 'Elasticsearch v' + node.version + ' @ ' + node.http_address + ' (' + node.ip + ')';
});
var message = 'This version of Kibana requires Elasticsearch ' +
minimumElasticsearchVersion + ' or higher on all nodes. ' +
'I found the following incompatible nodes in your cluster: ' +
badNodeNames.join(',');
server.plugins.elasticsearch.status.red(message);
throw new SetupError(server, message);
});
};
};

View file

@ -0,0 +1,40 @@
var SetupError = require('./setup_error');
var format = require('util').format;
module.exports = function (server) {
var client = server.plugins.elasticsearch.client;
var index = server.config().get('kibana.index');
function handleError(message) {
return function (err) {
throw new SetupError(server, message, err);
};
}
return client.indices.create({
index: index,
body: {
settings: {
number_of_shards: 1,
number_of_replicas: 1
},
mappings: {
config: {
properties: {
buildNum: {
type: 'string',
index: 'not_analyzed'
}
}
}
}
}
})
.catch(handleError('Unable to create Kibana index "<%= kibana.index %>"'))
.then(function () {
return client.cluster.health({
waitForStatus: 'yellow',
index: index
})
.catch(handleError('Waiting for Kibana index "<%= kibana.index %>" to come online failed.'));
});
};

View file

@ -0,0 +1,17 @@
var map = {
'false': false,
'off': false,
'no': false,
'0': false,
'true': true,
'on': true,
'yes': true,
'1': true
};
module.exports = function (str) {
var bool = map[String(str)];
if (typeof bool !== 'boolean') {
throw new TypeError('"' + str + '" does not map to an esBool');
}
return bool;
};

View file

@ -1,6 +1,9 @@
var Promise = require('bluebird');
var elasticsearch = require('elasticsearch');
var exposeClient = require('./expose_client');
var migrateConfig = require('./migrate_config');
var createKibanaIndex = require('./create_kibana_index');
var checkEsVersion = require('./check_es_version');
var NoConnections = elasticsearch.errors.NoConnections;
var util = require('util');
var format = util.format;
@ -30,8 +33,8 @@ module.exports = function (plugin, server) {
// if "timed_out" === true then elasticsearch could not
// find any idices matching our filter within 5 seconds
if (resp.timed_out) {
plugin.status.red('No existing Kibana index found');
return;
plugin.status.yellow('No existing Kibana index found');
return createKibanaIndex(server);
}
// If status === "red" that means that index(es) were found
@ -46,17 +49,42 @@ module.exports = function (plugin, server) {
});
}
var running = false;
function runHealthCheck() {
setTimeout(healthCheck, 2500);
if (running) {
setTimeout(function () {
healthCheck()
.then(runHealthCheck)
.catch(function (err) {
server.log('error', err);
runHealthCheck();
});
}, 2500);
}
}
function healthCheck() {
waitForPong()
return waitForPong()
.then(checkEsVersion(server))
.then(waitForShards)
.then(runHealthCheck)
.catch(runHealthCheck);
.then(migrateConfig(server));
}
healthCheck();
return {
isRunning: function () {
return running;
},
run: function () {
return healthCheck();
},
start: function () {
running = true;
return healthCheck().then(runHealthCheck, runHealthCheck);
},
stop: function () {
running = false;
}
};
};

View file

@ -0,0 +1,33 @@
var semver = require('semver');
var rcVersionRegex = /(\d+\.\d+\.\d+)\-rc(\d+)/i;
module.exports = function (server, doc) {
var config = server.config();
if (/beta|snapshot/i.test(doc._id)) return false;
if (!doc._id) return false;
if (doc._id === config.get('kibana.package.version')) return false;
var packageRcRelease = Infinity;
var rcRelease = Infinity;
var packageVersion = config.get('kibana.package.version');
var version = doc._id;
var matches = doc._id.match(rcVersionRegex);
var packageMatches = config.get('kibana.package.version').match(rcVersionRegex);
if (matches) {
version = matches[1];
rcRelease = parseInt(matches[2], 10);
}
if (packageMatches) {
packageVersion = packageMatches[1];
packageRcRelease = parseInt(packageMatches[2], 10);
}
try {
if (semver.gte(version, packageVersion) && rcRelease >= packageRcRelease) return false;
} catch (e) {
return false;
}
return true;
};

View file

@ -0,0 +1,29 @@
var upgrade = require('./upgrade_config');
module.exports = function (server) {
return function () {
var config = server.config();
var client = server.plugins.elasticsearch.client;
var options = {
index: config.get('kibana.index'),
type: 'config',
body: {
size: 1000,
sort: [ { buildNum: { order: 'desc' } } ],
query: {
filtered: {
filter: {
bool: {
must_not: [ { query: { match: { _id: '@@version' } } } ]
}
}
}
}
}
};
return client.search(options).then(upgrade(server));
};
};

View file

@ -0,0 +1,18 @@
var _ = require('lodash');
var util = require('util');
function SetupError(server, template, err) {
var config = server.config().get();
// don't override other setup errors
if (err && err instanceof SetupError) return err;
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = _.template(template, config);
if (err) {
this.origError = err;
if (err.stack) this.stack = err.stack;
}
}
util.inherits(SetupError, Error);
module.exports = SetupError;

View file

@ -0,0 +1,42 @@
var Promise = require('bluebird');
var isUpgradeable = require('./is_upgradeable');
var _ = require('lodash');
var format = require('util').format;
module.exports = function (server) {
var client = server.plugins.elasticsearch.client;
var config = server.config();
return function (response) {
var newConfig = {};
// Check to see if there are any doc. If not then we can assume
// nothing needs to be done
if (response.hits.hits.length === 0) return Promise.resolve();
// if we already have a the current version in the index then we need to stop
if (_.find(response.hits.hits, { _id: config.get('kibana.package.version') })) {
return Promise.resolve();
}
// Look for upgradeable configs. If none of them are upgradeable
// then resolve with null.
var body = _.find(response.hits.hits, isUpgradeable.bind(null, server));
if (!body) return Promise.resolve();
// if the build number is still the template string (which it wil be in development)
// then we need to set it to the max interger. Otherwise we will set it to the build num
body._source.buildNum = Math.pow(2, 53) - 1;
if (!/^@@/.test(config.get('kibana.buildNum'))) {
body._source.buildNum = parseInt(config.get('kibana.buildNum'), 10);
}
var logMsg = format('[ elasticsearch ] Upgrade config from %s to %s', body._id, config.get('kibana.package.version'));
server.log('plugin', logMsg);
return client.create({
index: config.get('kibana.index'),
type: 'config',
body: body._source,
id: config.get('kibana.package.version')
});
};
};

View file

@ -0,0 +1,139 @@
var _ = require('lodash');
function VersionMathException(message) {
this.message = message;
this.name = 'VersionMathException';
}
// Determine if a specific version meets the minimum requirement
var compare = function (required, installed) {
if (_.isUndefined(installed)) {
return;
}
if (!required || !installed) {
return undefined;
}
var a = installed.split('.');
var b = required.split('.');
var i;
// leave suffixes as is ("RC1 or -SNAPSHOT")
for (i = 0; i < Math.min(a.length, 3); ++i) {
a[i] = Number(a[i]);
}
for (i = 0; i < Math.min(b.length, 3); ++i) {
b[i] = Number(b[i]);
}
if (a.length === 2) {
a[2] = 0;
}
if (a[0] > b[0]) { return true; }
if (a[0] < b[0]) { return false; }
if (a[1] > b[1]) { return true; }
if (a[1] < b[1]) { return false; }
if (a[2] > b[2]) { return true; }
if (a[2] < b[2]) { return false; }
if (a.length > 3) {
// rc/beta suffix
if (b.length <= 3) {
return false;
} // no suffix on b -> a<b
return a[3] >= b[3];
}
if (b.length > 3) {
// b has a suffix but a not -> a>b
return true;
}
return true;
};
// Sort versions from lowest to highest
var sortVersions = function (versions) {
if (!_.isArray(versions)) versions = [versions];
return _.uniq(versions).sort(function (a, b) {
return compare(a, b) ? -1 : 1;
});
};
// Get the max version in this cluster
var max = function (versions) {
return sortVersions(versions).pop();
};
// Return the lowest version in the cluster
var min = function (versions) {
return sortVersions(versions).shift();
};
// Check if the lowest version in the cluster is >= to `version`
var gte = function (version, versions) {
var _versions = sortVersions(versions);
return compare(version, min(_versions));
};
// Check if the highest version in the cluster is <= to `version`
var lte = function (version, versions) {
var _versions = sortVersions(versions);
return compare(max(_versions), version);
};
// check if lowest version in cluster = `version`
var eq = function (version, versions) {
var _versions = sortVersions(versions);
return version === min(_versions) ? true : false;
};
// version > lowest version in cluster?
var gt = function (version, versions) {
var _versions = sortVersions(versions);
return version === min(_versions) ? false : gte(version, _versions);
};
// version < highest version in cluster?
var lt = function (version, versions) {
var _versions = sortVersions(versions);
return version === max(_versions) ? false : lte(version, _versions);
};
/*
Takes a version string with one of the following optional comparison prefixes: >,>=,<.<=
and evaluates if the cluster meets the requirement. If the prefix is omitted exact match
is assumed
*/
var is = function (equation, versions) {
var _versions = sortVersions(versions);
var _v = equation;
var _cf;
if (_v.charAt(0) === '>') {
_cf = _v.charAt(1) === '=' ? gte(_v.slice(2), _versions) : gt(_v.slice(1), _versions);
} else if (_v.charAt(0) === '<') {
_cf = _v.charAt(1) === '=' ? lte(_v.slice(2), _versions) : lt(_v.slice(1), _versions);
} else {
_cf = eq(_v, _versions);
}
return _cf;
};
module.exports = {
min: min,
max: max,
is: is,
eq: eq,
gt: gt,
gte: gte,
lt: lt,
lte: lte
};

View file

@ -1,80 +0,0 @@
var root = require('requirefrom')('');
var upgradeConfig = root('src/server/lib/upgradeConfig');
var expect = require('expect.js');
var sinon = require('sinon');
var sinonAsPromised = require('sinon-as-promised')(require('bluebird'));
var util = require('util');
var package = root('package.json');
var config = root('src/server/config');
var upgradeFrom4_0_0_to_4_0_1 = root('test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1.json');
var upgradeFrom4_0_0_to_4_0_1_snapshot = root('test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json');
var upgradeFrom4_0_0 = root('test/unit/fixtures/config_upgrade_from_4.0.0.json');
upgradeFrom4_0_0_to_4_0_1.hits.hits[0]._index = config.kibana.kibana_index;
upgradeFrom4_0_0_to_4_0_1.hits.hits[1]._index = config.kibana.kibana_index;
upgradeFrom4_0_0_to_4_0_1_snapshot.hits.hits[0]._index = config.kibana.kibana_index;
upgradeFrom4_0_0_to_4_0_1_snapshot.hits.hits[1]._index = config.kibana.kibana_index;
upgradeFrom4_0_0.hits.hits[0]._index = config.kibana.kibana_index;
describe('lib/upgradeConfig', function () {
var client, oldPackageVersion, oldBuildNum;
beforeEach(function () {
oldPackageVersion = config.package.version;
oldBuildNum = config.buildNum;
client = { create: sinon.stub() };
});
afterEach(function () {
config.package.version = oldPackageVersion;
config.buildNum = oldBuildNum;
});
it('should not upgrade if the current version of the config exits', function () {
config.package.version = '4.0.1';
var fn = upgradeConfig(client);
client.create.rejects(new Error('DocumentAlreadyExistsException'));
return fn(upgradeFrom4_0_0_to_4_0_1).finally(function () {
sinon.assert.notCalled(client.create);
});
});
it('should not upgrade if there are no hits', function () {
config.package.version = '4.0.1';
var fn = upgradeConfig(client);
return fn({ hits: { hits: [] } }).finally(function () {
sinon.assert.notCalled(client.create);
});
});
it('should not upgrade even if a snapshot exists', function () {
config.package.version = '4.0.1-snapshot';
client.create.rejects(new Error('DocumentAlreadyExistsException'));
var fn = upgradeConfig(client);
return fn(upgradeFrom4_0_0_to_4_0_1_snapshot).finally(function () {
sinon.assert.notCalled(client.create);
});
});
it('should upgrade from 4.0.0 to 4.0.1', function () {
config.package.version = '4.0.1';
config.buildNum = 5921;
var fn = upgradeConfig(client);
client.create.resolves({ _index: config.kibana.kibana_index, _type: 'config', _id: '4.0.1', _version: 1, created: true });
return fn(upgradeFrom4_0_0).finally(function () {
sinon.assert.calledOnce(client.create);
var body = client.create.args[0][0];
expect(body).to.eql({
index: config.kibana.kibana_index,
type: 'config',
id: '4.0.1',
body: {
'buildNum': 5921,
'defaultIndex': 'logstash-*'
}
});
});
});
});

View file

@ -1,194 +0,0 @@
var _ = require('lodash');
var root = require('requirefrom')('');
var validateRequest = root('src/hapi/plugins/elasticsearch/lib/validate');
var expect = require('expect.js');
var server = { config: root('src/hapi/lib/config') };
var config = server.config();
describe('lib/isValid', function () {
function del(path, body, valid) {
run('delEte', path, body, valid);
run('delete', path, body, valid);
}
function send(path, body, valid) {
run('POST', path, body, valid);
run('post', path, body, valid);
run('PUT', path, body, valid);
run('put', path, body, valid);
}
function run(method, path, body, valid) {
if (typeof body === 'boolean') {
valid = body;
body = null;
}
if (_.isArray(body)) body = body.map(JSON.stringify).join('\n') + '\n';
if (_.isObject(body)) body = JSON.stringify(body);
var pass = false;
try {
validateRequest(server, {
method: method.toLowerCase(),
path: path,
payload: body
});
pass = true;
} catch (e) {}
if (pass !== Boolean(valid)) {
var msg = 'Expected ' + method + ' ' +
path + ' ' + (body ? 'with body ' : '') +
'to ' + (!valid ? 'not ' : '') + 'validate';
if (body) {
msg += ' ' + body;
}
throw new Error(msg);
}
}
describe('index management', function () {
it('allows creating kibana index', function () {
send('/' + config.get('kibana.index'), true);
});
it('allows deleting the kibana index', function () {
del('/' + config.get('kibana.index'), true);
});
it('blocks creating a non-kibana index', function () {
send('/app-index', false);
});
it('blocks deleting a non-kibana indices', function () {
del('/app-data', false);
});
});
describe('doc management', function () {
it('allows indexing to the kibana index', function () {
send('/' + config.get('kibana.index'), true);
send('/' + config.get('kibana.index') + '/index-patterns', true);
send('/' + config.get('kibana.index') + '/index-patterns/pattern-id', true);
});
it('allows deleting kibana documents', function () {
del('/' + config.get('kibana.index') + '/index-patterns', true);
del('/' + config.get('kibana.index') + '/index-patterns/pattern-id', true);
});
});
describe('allows any destructive non-bulk requests against kibana index', function () {
it('refresh', function () {
send('/' + config.get('kibana.index') + '/_refresh', true);
});
it('delete', function () {
del('/' + config.get('kibana.index') + '/pasta/lasagna', true);
});
});
describe('assorted methods that are non-destructive', function () {
it('validate', function () {
run('GET', '/_search?search_type=count', true);
run('GET', '/index/type/id', true);
run('GET', '/index/type/_mapping/field/field1', true);
run('GET', '/_aliases', true);
run('GET', '/_nodes/', true);
run('HEAD', '/', true);
run('HEAD', '/' + config.get('kibana.index'), true);
run('HEAD', '/other-index', true);
run('GET', '/_cluster/health', true);
run('POST', '/' + config.get('kibana.index') + '/__notRealIndex__/_validate/query?q=foo:bar', true);
run('POST', '/_validate', true);
run('POST', '/_search', true);
});
});
describe('bulk indexing', function () {
it('valid', function () {
send('/_bulk', [
{ create: { _index: config.get('kibana.index'), _type: 'index-pattern' } },
{ fields: [] },
{ create: { _index: config.get('kibana.index'), _type: 'vis' } },
{ aggs: [] }
], true);
send('/' + config.get('kibana.index') + '/_bulk', [
// implicit index
{ create: { _type: 'index-pattern' } },
{ fields: [] },
// explicit index
{ create: { _index: config.get('kibana.index'), _type: 'vis' } },
{ aggs: [] }
], true);
});
it('rejects bulks including even one other index', function () {
send('/' + config.get('kibana.index') + '/_bulk', [
// implicit index
{ create: { _type: 'index-pattern' } },
{ fields: [] },
// explicit index
{ create: { _index: 'app-data', _type: 'vis' } },
{ aggs: [] }
], false);
});
it('rejects malformed bulk bodies', function () {
send('/_bulk', '{}\n{ "_index": "john" }\n', false);
send('/_bulk', '{}\n{}\n', false);
send('/_bulk', '{ "field": "value" }', false);
send('/_bulk', '{ "field": "v', false);
});
});
describe('msearch', function () {
it('requires a bulk-formatted body', function () {
send('/_msearch', false);
send('/_msearch', '{}', false);
send('/_msearch', '{}\n{}\n', true);
send('/_msearch', '{}\n{}\n{}\n', false);
});
it('allows searching any index', function () {
send('/app-index/_msearch', [
{},
{ query: { match_all: {} } }
], true);
send('/app-index/data-type/_msearch', [
{},
{ query: { match_all: {} } }
], true);
send('/_msearch', [
{ _index: 'app-index', _type: 'data-type' },
{ query: { match_all: {} } },
{ _index: 'IT-index', _type: 'logs' },
{ query: { match_all: {} } },
{ _index: 'L33t', _type: '?' },
{ query: { match_all: {} } },
], true);
});
});
describe('mget', function () {
it('requires a valid json body', function () {
send('/_mget', false);
send('/_mget', '{}', true);
send('/_mget', '{}\n{}\n', false);
});
it('allows reading from any index', function () {
send('/app-index/_mget', { docs: { match_all: {} } }, true);
send('/app-index/data-type/_mget', { docs: [ {} ] }, true);
});
});
});

View file

@ -0,0 +1,70 @@
var root = require('requirefrom')('');
var _ = require('lodash');
var checkEsVersion = root('src/hapi/plugins/elasticsearch/lib/check_es_version');
var Promise = require('bluebird');
var sinon = require('sinon');
describe('plugins/elasticsearch', function () {
describe('lib/check_es_version', function () {
var server;
beforeEach(function () {
var get = sinon.stub().withArgs('elasticserach.minimumVerison').returns('1.4.3');
var config = function () { return { get: get }; };
server = { config: config, plugins: { elasticsearch: { client: { nodes: {} } } } };
});
function setNodes(/* ...versions */) {
var versions = _.shuffle(arguments);
var nodes = {};
var i = 0;
while (versions.length) {
var name = 'node-' + (++i);
var version = versions.shift();
var node = {
version: version,
http_address: 'http_address',
ip: 'ip'
};
if (!_.isString(version)) _.assign(node, version);
nodes[name] = node;
}
var client = server.plugins.elasticsearch.client;
client.nodes.info = sinon.stub().returns(Promise.resolve({ nodes: nodes }));
}
it('passes with single a node that matches', function () {
setNodes('1.4.3');
return checkEsVersion(server)();
});
it('passes with multiple nodes that satisfy', function () {
setNodes('1.4.3', '1.4.4', '1.4.3-Beta1');
return checkEsVersion(server)();
});
it('fails with a single node that is out of date', function () {
setNodes('1.4.4', '1.4.2', '1.4.5');
return checkEsVersion(server)()
.then(function () {
throw new Error('expected validation to fail');
}, _.noop);
});
it('passes if that single node is a client node', function () {
setNodes(
'1.4.4',
{ version: '1.4.2', attributes: { client: 'true' } },
'1.4.5'
);
return checkEsVersion(server)();
});
});
});

View file

@ -0,0 +1,140 @@
var root = require('requirefrom')('');
var _ = require('lodash');
_.mixin(require('lodash-deep'));
var sinon = require('sinon');
var expect = require('expect.js');
var createKibanaIndex = root('src/hapi/plugins/elasticsearch/lib/create_kibana_index');
var SetupError = root('src/hapi/plugins/elasticsearch/lib/setup_error');
var Promise = require('bluebird');
describe('plugins/elasticsearch', function () {
describe('lib/create_kibana_index', function () {
var server, client, config;
beforeEach(function () {
server = {};
client = {};
var config = { kibana: { index: '.my-kibana' } };
var get = sinon.stub();
get.returns(config);
get.withArgs('kibana.index').returns(config.kibana.index);
config = function () { return { get: get }; };
_.deepSet(client, 'indices.create', sinon.stub());
_.deepSet(client, 'cluster.health', sinon.stub());
_.deepSet(server, 'plugins.elasticsearch.client', client);
_.deepSet(server, 'config', config);
});
describe('successful requests', function () {
beforeEach(function () {
client.indices.create.returns(Promise.resolve());
client.cluster.health.returns(Promise.resolve());
});
it('should check cluster.health upon successful index creation', function () {
var fn = createKibanaIndex(server);
return fn.then(function () {
sinon.assert.calledOnce(client.cluster.health);
});
});
it('should be created with mappings for config.buildNum', function () {
var fn = createKibanaIndex(server);
return fn.then(function () {
var params = client.indices.create.args[0][0];
expect(params)
.to.have.property('body');
expect(params.body)
.to.have.property('mappings');
expect(params.body.mappings)
.to.have.property('config');
expect(params.body.mappings.config)
.to.have.property('properties');
expect(params.body.mappings.config.properties)
.to.have.property('buildNum');
expect(params.body.mappings.config.properties.buildNum)
.to.have.property('type', 'string');
expect(params.body.mappings.config.properties.buildNum)
.to.have.property('index', 'not_analyzed');
});
});
it('should be created with 1 shard and 1 replica', function () {
var fn = createKibanaIndex(server);
return fn.then(function () {
var params = client.indices.create.args[0][0];
expect(params)
.to.have.property('body');
expect(params.body)
.to.have.property('settings');
expect(params.body.settings)
.to.have.property('number_of_shards', 1);
expect(params.body.settings)
.to.have.property('number_of_replicas', 1);
});
});
it('should be created with 1 shard and 1 replica', function () {
var fn = createKibanaIndex(server);
return fn.then(function () {
var params = client.indices.create.args[0][0];
expect(params)
.to.have.property('body');
expect(params.body)
.to.have.property('settings');
expect(params.body.settings)
.to.have.property('number_of_shards', 1);
expect(params.body.settings)
.to.have.property('number_of_replicas', 1);
});
});
it('should be created with index name set in the config', function () {
var fn = createKibanaIndex(server);
return fn.then(function () {
var params = client.indices.create.args[0][0];
expect(params)
.to.have.property('index', '.my-kibana');
});
});
});
describe('failure requests', function () {
it('should reject with a SetupError', function () {
var error = new Error('Oops!');
client.indices.create.returns(Promise.reject(error));
var fn = createKibanaIndex(server);
return fn.catch(function (err) {
expect(err).to.be.a(SetupError);
});
});
it('should reject with an error if index creation fails', function () {
var error = new Error('Oops!');
client.indices.create.returns(Promise.reject(error));
var fn = createKibanaIndex(server);
return fn.catch(function (err) {
expect(err.message).to.be('Unable to create Kibana index ".my-kibana"');
expect(err).to.have.property('origError', error);
});
});
it('should reject with an error if health check fails', function () {
var error = new Error('Oops!');
client.indices.create.returns(Promise.resolve());
client.cluster.health.returns(Promise.reject(error));
var fn = createKibanaIndex(server);
return fn.catch(function (err) {
expect(err.message).to.be('Waiting for Kibana index ".my-kibana" to come online failed.');
expect(err).to.have.property('origError', error);
});
});
});
});
});

View file

@ -0,0 +1,147 @@
var root = require('requirefrom')('');
var _ = require('lodash');
var healthCheck = root('src/hapi/plugins/elasticsearch/lib/health_check');
var Promise = require('bluebird');
var sinon = require('sinon');
var root = require('requirefrom')('');
var _ = require('lodash');
var healthCheck = root('src/hapi/plugins/elasticsearch/lib/health_check');
var Promise = require('bluebird');
var sinon = require('sinon');
var expect = require('expect.js');
var NoConnections = require('elasticsearch').errors.NoConnections;
describe('plugins/elasticsearch', function () {
describe('lib/health_check', function () {
var health, plugin, server, get, client;
beforeEach(function () {
// setup the plugin stub
plugin = {
name: 'elasticsearch',
status: {
red: sinon.stub(),
green: sinon.stub(),
yellow: sinon.stub()
}
};
// setup the config().get() stub
get = sinon.stub();
// set up the elasticsearch client stub
client = {
cluster: { health: sinon.stub() },
indices: { create: sinon.stub() },
nodes: { info: sinon.stub() },
ping: sinon.stub(),
create: sinon.stub(),
search: sinon.stub().returns(Promise.resolve({ hits: { hits: [] } })),
};
client.nodes.info.returns(Promise.resolve({
nodes: {
'node-01': {
version: '1.5.0',
http_address: 'inet[/127.0.0.1:9200]',
ip: '127.0.0.1'
}
}
}));
// Setup the server mock
server = {
log: sinon.stub(),
config: function () { return { get: get }; },
plugins: { elasticsearch: { client: client } }
};
health = healthCheck(plugin, server);
});
it('should set the cluster green if everything is ready', function () {
get.withArgs('elasticsearch.minimumVerison').returns('1.4.4');
get.withArgs('kibana.index').returns('.my-kibana');
client.ping.returns(Promise.resolve());
client.cluster.health.returns(Promise.resolve({ timed_out: false, status: 'green' }));
return health.run()
.then(function () {
sinon.assert.calledOnce(plugin.status.yellow);
expect(plugin.status.yellow.args[0][0]).to.be('Waiting for Elasticsearch');
sinon.assert.calledOnce(client.ping);
sinon.assert.calledOnce(client.nodes.info);
sinon.assert.calledOnce(client.cluster.health);
sinon.assert.calledOnce(plugin.status.green);
expect(plugin.status.green.args[0][0]).to.be('Kibana index ready');
});
});
it('should set the cluster red if the ping fails, then to green', function () {
this.timeout(3000);
get.withArgs('elasticsearch.url').returns('http://localhost:9200');
get.withArgs('elasticsearch.minimumVerison').returns('1.4.4');
get.withArgs('kibana.index').returns('.my-kibana');
client.ping.onCall(0).returns(Promise.reject(new NoConnections()));
client.ping.onCall(1).returns(Promise.resolve());
client.cluster.health.returns(Promise.resolve({ timed_out: false, status: 'green' }));
return health.run()
.then(function () {
sinon.assert.calledOnce(plugin.status.yellow);
expect(plugin.status.yellow.args[0][0]).to.be('Waiting for Elasticsearch');
sinon.assert.calledOnce(plugin.status.red);
/* jscs:disable maximumLineLength */
expect(plugin.status.red.args[0][0]).to.be('Unable to connect to Elasticsearch at http://localhost:9200. Retrying in 2.5 seconds.');
/* jscs:enable maximumLineLength */
sinon.assert.calledTwice(client.ping);
sinon.assert.calledOnce(client.nodes.info);
sinon.assert.calledOnce(client.cluster.health);
sinon.assert.calledOnce(plugin.status.green);
expect(plugin.status.green.args[0][0]).to.be('Kibana index ready');
});
});
it('should set the cluster red if the health check status is red, then to green', function () {
this.timeout(3000);
get.withArgs('elasticsearch.url').returns('http://localhost:9200');
get.withArgs('elasticsearch.minimumVerison').returns('1.4.4');
get.withArgs('kibana.index').returns('.my-kibana');
client.ping.returns(Promise.resolve());
client.cluster.health.onCall(0).returns(Promise.resolve({ timed_out: false, status: 'red' }));
client.cluster.health.onCall(1).returns(Promise.resolve({ timed_out: false, status: 'green' }));
return health.run()
.then(function () {
sinon.assert.calledOnce(plugin.status.yellow);
expect(plugin.status.yellow.args[0][0]).to.be('Waiting for Elasticsearch');
sinon.assert.calledOnce(plugin.status.red);
/* jscs:disable maximumLineLength */
expect(plugin.status.red.args[0][0]).to.be('Elasticsearch is still initializing the kibana index... Trying again in 2.5 second.');
/* jscs:enable maximumLineLength */
sinon.assert.calledOnce(client.ping);
sinon.assert.calledOnce(client.nodes.info);
sinon.assert.calledTwice(client.cluster.health);
sinon.assert.calledOnce(plugin.status.green);
expect(plugin.status.green.args[0][0]).to.be('Kibana index ready');
});
});
it('should set the cluster yellow if the health check timed_out and create index', function () {
this.timeout(3000);
get.withArgs('elasticsearch.url').returns('http://localhost:9200');
get.withArgs('elasticsearch.minimumVerison').returns('1.4.4');
get.withArgs('kibana.index').returns('.my-kibana');
client.ping.returns(Promise.resolve());
client.cluster.health.onCall(0).returns(Promise.resolve({ timed_out: true, status: 'red' }));
client.cluster.health.onCall(1).returns(Promise.resolve({ timed_out: false, status: 'green' }));
client.indices.create.returns(Promise.resolve());
return health.run()
.then(function () {
sinon.assert.calledTwice(plugin.status.yellow);
expect(plugin.status.yellow.args[0][0]).to.be('Waiting for Elasticsearch');
expect(plugin.status.yellow.args[1][0]).to.be('No existing Kibana index found');
sinon.assert.calledOnce(client.ping);
sinon.assert.calledOnce(client.indices.create);
sinon.assert.calledOnce(client.nodes.info);
sinon.assert.calledTwice(client.cluster.health);
});
});
});
});

View file

@ -0,0 +1,69 @@
var root = require('requirefrom')('');
var isUpgradeable = root('src/hapi/plugins/elasticsearch/lib/is_upgradeable');
var expect = require('expect.js');
var util = require('util');
var package = root('package.json');
var sinon = require('sinon');
describe('plugins/elasticsearch', function () {
describe('lib/isUpgradeable', function () {
var config = {};
var server = {
config: function () {
return config;
}
};
function upgradeDoc(_id, version, bool) {
it(util.format('should return %s for %s <= %s', bool, _id, version), function () {
var doc = { _id: _id };
config.get = sinon.stub().withArgs('kibana.package.version').returns(version);
expect(isUpgradeable(server, doc)).to.be(bool);
});
}
upgradeDoc('1.0.0-beta1', package.version, false);
upgradeDoc(package.version, package.version, false);
upgradeDoc('4.0.0-RC1', '4.0.0-RC2', true);
upgradeDoc('4.0.0-rc2', '4.0.0-rc1', false);
upgradeDoc('4.0.0-rc2', '4.0.0', true);
upgradeDoc('4.0.0-rc2', '4.0.2', true);
upgradeDoc('4.0.1', '4.1.0-rc', true);
upgradeDoc('4.0.0-rc1', '4.0.0', true);
upgradeDoc('4.0.0-rc1-snapshot', '4.0.0', false);
upgradeDoc('4.1.0-rc1-snapshot', '4.1.0-rc1', false);
it('should handle missing _id field', function () {
config.get = sinon.stub().withArgs('kibana.package.version').returns(package.version);
var doc = {
'_index': '.kibana',
'_type': 'config',
'_score': 1,
'_source': {
'buildNum': 1.7976931348623157e+308,
'defaultIndex': '[logstash-]YYYY.MM.DD'
}
};
expect(isUpgradeable(server, doc)).to.be(false);
});
it('should handle _id of @@version', function () {
config.get = sinon.stub().withArgs('kibana.package.version').returns(package.version);
var doc = {
'_index': '.kibana',
'_type': 'config',
'_id': '@@version',
'_score': 1,
'_source': {
'buildNum': 1.7976931348623157e+308,
'defaultIndex': '[logstash-]YYYY.MM.DD'
}
};
expect(isUpgradeable(server, doc)).to.be(false);
});
});
});

View file

@ -7,95 +7,97 @@ var util = require('util');
var format = util.format;
describe('elasticsearch plugin', function () {
var server, config;
describe('plugins/elasticsearch', function () {
describe('routes', function () {
var server, config;
beforeEach(function () {
return findPort(7000, 8000).then(function (port) {
config = { 'kibana.server.port': port, 'logging.quiet': true };
return kibana.start(config).then(function (_server) {
server = _server;
});
});
});
afterEach(function (done) {
server.stop(done);
});
function testRoute(options) {
var statusCode = options.statusCode || 200;
describe(format('%s %s', options.method, options.url), function () {
it('should should return ' + statusCode, function (done) {
server.inject(options, function (res) {
expect(res.statusCode).to.be(statusCode);
done();
beforeEach(function () {
return findPort(7000, 8000).then(function (port) {
config = { 'kibana.server.port': port, 'logging.quiet': true };
return kibana.start(config).then(function (_server) {
server = _server;
});
});
});
}
afterEach(function (done) {
server.stop(done);
});
function testRoute(options) {
var statusCode = options.statusCode || 200;
describe(format('%s %s', options.method, options.url), function () {
it('should should return ' + statusCode, function (done) {
server.inject(options, function (res) {
expect(res.statusCode).to.be(statusCode);
done();
});
});
});
}
testRoute({
method: 'GET',
url: '/elasticsearch/_nodes'
testRoute({
method: 'GET',
url: '/elasticsearch/_nodes'
});
testRoute({
method: 'GET',
url: '/elasticsearch/'
});
testRoute({
method: 'GET',
url: '/elasticsearch/.kibana'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana',
payload: '{settings: {number_of_shards: 1, number_of_replicas: 1}}',
statusCode: 201
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/_bulk',
payload: '{}',
statusCode: 400
});
testRoute({
method: 'GET',
url: '/elasticsearch/.kibana/_mapping/*/field/_source'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/index-pattern/_search?fields=',
payload: '{query: {match_all: {}}, size: 2147483647}'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/__kibanaQueryValidator/_validate/query?explain=true&ignore_unavailable=true',
payload: '{query: {query_string: {analyze_wildcard: true, query: "*"}}}'
});
testRoute({
method: 'POST',
url: '/elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1429574531063',
payload: '{docs: [{_index: ".kibana", _type: "index-pattern", _id: "[logstash-]YYYY.MM.DD"}]}'
});
/* jscs:disable maximumLineLength */
testRoute({
method: 'POST',
url: '/elasticsearch/_msearch?timeout=0&ignore_unavailable=true&preference=1429577952339',
payload: '{"index":"logstash-2015.04.21","ignore_unavailable":true}\n{"size":500,"sort":{"@timestamp":"desc"},"query":{"filtered":{"query":{"query_string":{"analyze_wildcard":true,"query":"*"}},"filter":{"bool":{"must":[{"range":{"@timestamp":{"gte":1429577068175,"lte":1429577968175}}}],"must_not":[]}}}},"highlight":{"pre_tags":["@kibana-highlighted-field@"],"post_tags":["@/kibana-highlighted-field@"],"fields":{"*":{}}},"aggs":{"2":{"date_histogram":{"field":"@timestamp","interval":"30s","pre_zone":"-07:00","pre_zone_adjust_large_interval":true,"min_doc_count":0,"extended_bounds":{"min":1429577068175,"max":1429577968175}}}},"fields":["*","_source"],"script_fields":{},"fielddata_fields":["timestamp_offset","@timestamp","utc_time"]}\n'
});
/* jscs:enable maximumLineLength */
});
testRoute({
method: 'GET',
url: '/elasticsearch/'
});
testRoute({
method: 'GET',
url: '/elasticsearch/.kibana'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana',
payload: '{settings: {number_of_shards: 1, number_of_replicas: 1}}',
statusCode: 201
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/_bulk',
payload: '{}',
statusCode: 400
});
testRoute({
method: 'GET',
url: '/elasticsearch/.kibana/_mapping/*/field/_source'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/index-pattern/_search?fields=',
payload: '{query: {match_all: {}}, size: 2147483647}'
});
testRoute({
method: 'POST',
url: '/elasticsearch/.kibana/__kibanaQueryValidator/_validate/query?explain=true&ignore_unavailable=true',
payload: '{query: {query_string: {analyze_wildcard: true, query: "*"}}}'
});
testRoute({
method: 'POST',
url: '/elasticsearch/_mget?timeout=0&ignore_unavailable=true&preference=1429574531063',
payload: '{docs: [{_index: ".kibana", _type: "index-pattern", _id: "[logstash-]YYYY.MM.DD"}]}'
});
/* jscs:disable maximumLineLength */
testRoute({
method: 'POST',
url: '/elasticsearch/_msearch?timeout=0&ignore_unavailable=true&preference=1429577952339',
payload: '{"index":"logstash-2015.04.21","ignore_unavailable":true}\n{"size":500,"sort":{"@timestamp":"desc"},"query":{"filtered":{"query":{"query_string":{"analyze_wildcard":true,"query":"*"}},"filter":{"bool":{"must":[{"range":{"@timestamp":{"gte":1429577068175,"lte":1429577968175}}}],"must_not":[]}}}},"highlight":{"pre_tags":["@kibana-highlighted-field@"],"post_tags":["@/kibana-highlighted-field@"],"fields":{"*":{}}},"aggs":{"2":{"date_histogram":{"field":"@timestamp","interval":"30s","pre_zone":"-07:00","pre_zone_adjust_large_interval":true,"min_doc_count":0,"extended_bounds":{"min":1429577068175,"max":1429577968175}}}},"fields":["*","_source"],"script_fields":{},"fielddata_fields":["timestamp_offset","@timestamp","utc_time"]}\n'
});
/* jscs:enable maximumLineLength */
});

View file

@ -0,0 +1,53 @@
var root = require('requirefrom')('');
var _ = require('lodash');
var SetupError = root('src/hapi/plugins/elasticsearch/lib/setup_error');
var expect = require('expect.js');
describe('plugins/elasticsearch', function () {
describe('lib/setup_error', function () {
var server = {
config: function () {
return {
get: function () {
return { kibana: { index: '.my-kibana' } };
}
};
}
};
var err = new SetupError(server, 'Oops! <%= kibana.index %>');
it('should allow config values in the message template', function () {
expect(err).to.have.property('message', 'Oops! .my-kibana');
});
it('should set the name of the error', function () {
expect(err).to.have.property('name', 'SetupError');
});
it('should set the stack trace', function () {
expect(err).to.have.property('stack');
expect(err.stack).to.match(/^SetupError/);
});
it('should return the passed error if it is a SetupError', function () {
var error = new SetupError(server, 'Oh Boy!', err);
expect(error).to.have.property('message', 'Oops! .my-kibana');
});
it('should store the original error', function () {
var origError = new Error('Boom!');
var error = new SetupError(server, 'Oh Boy!', origError);
expect(error).to.have.property('origError', origError);
});
it('should copy the stack from the origError', function () {
var origError = new Error('Boom!');
var error = new SetupError(server, 'Oh Boy!', origError);
expect(error).to.have.property('stack', origError.stack);
});
});
});

View file

@ -0,0 +1,96 @@
var root = require('requirefrom')('');
var _ = require('lodash');
var upgradeConfig = root('src/hapi/plugins/elasticsearch/lib/upgrade_config');
var Promise = require('bluebird');
var sinon = require('sinon');
var expect = require('expect.js');
describe('pluigns/elasticsearch', function () {
describe('lib/upgrade_config', function () {
var get, server, client, config, upgrade;
beforeEach(function () {
get = sinon.stub();
get.withArgs('kibana.package.version').returns('4.0.1');
get.withArgs('kibana.index').returns('.my-kibana');
client = { create: sinon.stub() };
server = {
log: sinon.stub(),
config: function () { return { get: get }; },
plugins: { elasticsearch: { client: client } }
};
upgrade = upgradeConfig(server);
});
it('should resolve with undefined if nothing is found', function () {
return upgrade({ hits: { hits:[] } }).then(function (resp) {
expect(resp).to.be(undefined);
});
});
it('should resolve with undefined if the current version is found', function () {
var response = { hits: { hits: [ { _id: '4.0.1' } ] } };
return upgrade(response).then(function (resp) {
expect(resp).to.be(undefined);
});
});
it('should resolve with undefined if the nothing is upgradeable', function () {
var response = { hits: { hits: [ { _id: '4.0.1-beta1' }, { _id: '4.0.0-snapshot1' } ] } };
return upgrade(response).then(function (resp) {
expect(resp).to.be(undefined);
});
});
it('should update the build number on the new config', function () {
get.withArgs('kibana.buildNum').returns(5801);
client.create.returns(Promise.resolve());
var response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1 } } ] } };
return upgrade(response).then(function (resp) {
sinon.assert.calledOnce(client.create);
var params = client.create.args[0][0];
expect(params).to.have.property('body');
expect(params.body).to.have.property('buildNum', 5801);
expect(params).to.have.property('index', '.my-kibana');
expect(params).to.have.property('type', 'config');
expect(params).to.have.property('id', '4.0.1');
});
});
it('should update the build number to max integer if buildNum is template string', function () {
get.withArgs('kibana.buildNum').returns('@@buildNum');
client.create.returns(Promise.resolve());
var response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1 } } ] } };
return upgrade(response).then(function (resp) {
sinon.assert.calledOnce(client.create);
var params = client.create.args[0][0];
expect(params).to.have.property('body');
expect(params.body).to.have.property('buildNum', Math.pow(2, 53) - 1);
});
});
it('should log a message for upgrades', function () {
get.withArgs('kibana.buildNum').returns(5801);
client.create.returns(Promise.resolve());
var response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1 } } ] } };
return upgrade(response).then(function (resp) {
sinon.assert.calledOnce(server.log);
expect(server.log.args[0][0]).to.be('plugin');
expect(server.log.args[0][1]).to.be('[ elasticsearch ] Upgrade config from 4.0.0 to 4.0.1');
});
});
it('should copy attributes from old config', function () {
get.withArgs('kibana.buildNum').returns(5801);
client.create.returns(Promise.resolve());
var response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1, defaultIndex: 'logstash-*' } } ] } };
return upgrade(response).then(function (resp) {
sinon.assert.calledOnce(client.create);
var params = client.create.args[0][0];
expect(params).to.have.property('body');
expect(params.body).to.have.property('defaultIndex', 'logstash-*');
});
});
});
});

View file

@ -0,0 +1,197 @@
var _ = require('lodash');
var root = require('requirefrom')('');
var validateRequest = root('src/hapi/plugins/elasticsearch/lib/validate');
var expect = require('expect.js');
var server = { config: root('src/hapi/lib/config') };
var config = server.config();
describe('plugins/elasticsearch', function () {
describe('lib/validate', function () {
function del(path, body, valid) {
run('delEte', path, body, valid);
run('delete', path, body, valid);
}
function send(path, body, valid) {
run('POST', path, body, valid);
run('post', path, body, valid);
run('PUT', path, body, valid);
run('put', path, body, valid);
}
function run(method, path, body, valid) {
if (typeof body === 'boolean') {
valid = body;
body = null;
}
if (_.isArray(body)) body = body.map(JSON.stringify).join('\n') + '\n';
if (_.isObject(body)) body = JSON.stringify(body);
var pass = false;
try {
validateRequest(server, {
method: method.toLowerCase(),
path: path,
payload: body
});
pass = true;
} catch (e) {}
if (pass !== Boolean(valid)) {
var msg = 'Expected ' + method + ' ' +
path + ' ' + (body ? 'with body ' : '') +
'to ' + (!valid ? 'not ' : '') + 'validate';
if (body) {
msg += ' ' + body;
}
throw new Error(msg);
}
}
describe('index management', function () {
it('allows creating kibana index', function () {
send('/' + config.get('kibana.index'), true);
});
it('allows deleting the kibana index', function () {
del('/' + config.get('kibana.index'), true);
});
it('blocks creating a non-kibana index', function () {
send('/app-index', false);
});
it('blocks deleting a non-kibana indices', function () {
del('/app-data', false);
});
});
describe('doc management', function () {
it('allows indexing to the kibana index', function () {
send('/' + config.get('kibana.index'), true);
send('/' + config.get('kibana.index') + '/index-patterns', true);
send('/' + config.get('kibana.index') + '/index-patterns/pattern-id', true);
});
it('allows deleting kibana documents', function () {
del('/' + config.get('kibana.index') + '/index-patterns', true);
del('/' + config.get('kibana.index') + '/index-patterns/pattern-id', true);
});
});
describe('allows any destructive non-bulk requests against kibana index', function () {
it('refresh', function () {
send('/' + config.get('kibana.index') + '/_refresh', true);
});
it('delete', function () {
del('/' + config.get('kibana.index') + '/pasta/lasagna', true);
});
});
describe('assorted methods that are non-destructive', function () {
it('validate', function () {
run('GET', '/_search?search_type=count', true);
run('GET', '/index/type/id', true);
run('GET', '/index/type/_mapping/field/field1', true);
run('GET', '/_aliases', true);
run('GET', '/_nodes/', true);
run('HEAD', '/', true);
run('HEAD', '/' + config.get('kibana.index'), true);
run('HEAD', '/other-index', true);
run('GET', '/_cluster/health', true);
run('POST', '/' + config.get('kibana.index') + '/__notRealIndex__/_validate/query?q=foo:bar', true);
run('POST', '/_validate', true);
run('POST', '/_search', true);
});
});
describe('bulk indexing', function () {
it('valid', function () {
send('/_bulk', [
{ create: { _index: config.get('kibana.index'), _type: 'index-pattern' } },
{ fields: [] },
{ create: { _index: config.get('kibana.index'), _type: 'vis' } },
{ aggs: [] }
], true);
send('/' + config.get('kibana.index') + '/_bulk', [
// implicit index
{ create: { _type: 'index-pattern' } },
{ fields: [] },
// explicit index
{ create: { _index: config.get('kibana.index'), _type: 'vis' } },
{ aggs: [] }
], true);
});
it('rejects bulks including even one other index', function () {
send('/' + config.get('kibana.index') + '/_bulk', [
// implicit index
{ create: { _type: 'index-pattern' } },
{ fields: [] },
// explicit index
{ create: { _index: 'app-data', _type: 'vis' } },
{ aggs: [] }
], false);
});
it('rejects malformed bulk bodies', function () {
send('/_bulk', '{}\n{ "_index": "john" }\n', false);
send('/_bulk', '{}\n{}\n', false);
send('/_bulk', '{ "field": "value" }', false);
send('/_bulk', '{ "field": "v', false);
});
});
describe('msearch', function () {
it('requires a bulk-formatted body', function () {
send('/_msearch', false);
send('/_msearch', '{}', false);
send('/_msearch', '{}\n{}\n', true);
send('/_msearch', '{}\n{}\n{}\n', false);
});
it('allows searching any index', function () {
send('/app-index/_msearch', [
{},
{ query: { match_all: {} } }
], true);
send('/app-index/data-type/_msearch', [
{},
{ query: { match_all: {} } }
], true);
send('/_msearch', [
{ _index: 'app-index', _type: 'data-type' },
{ query: { match_all: {} } },
{ _index: 'IT-index', _type: 'logs' },
{ query: { match_all: {} } },
{ _index: 'L33t', _type: '?' },
{ query: { match_all: {} } },
], true);
});
});
describe('mget', function () {
it('requires a valid json body', function () {
send('/_mget', false);
send('/_mget', '{}', true);
send('/_mget', '{}\n{}\n', false);
});
it('allows reading from any index', function () {
send('/app-index/_mget', { docs: { match_all: {} } }, true);
send('/app-index/data-type/_mget', { docs: [ {} ] }, true);
});
});
});
});

View file

@ -0,0 +1,135 @@
var root = require('requirefrom')('');
var _ = require('lodash');
var versionMath = root('src/hapi/plugins/elasticsearch/lib/version_math');
var expect = require('expect.js');
var versions = [
'1.1.12',
'1.1.12',
'1.1.12',
'1.1.12',
'0.90.0',
'0.90.1',
'1.0.0',
'1.0',
'1.2.3',
'2.0.0',
'2.0.1',
'2.3.1'
];
describe('plugins/elasticsearch', function () {
describe('lib/version_math', function () {
describe('version math (0.90.0 - 2.3.1)', function () {
var methods = 'max,min,eq,is,lt,lte,gt,gte'.split(',');
describe('methods', function () {
it('should have ' + methods.join(', ') + ' methods', function () {
_.each(methods, function (method) {
expect(versionMath[method]).to.be.a(Function);
});
});
});
describe('min & max', function () {
it('has a max of 2.3.1', function () {
expect(versionMath.max(versions)).to.be('2.3.1');
});
it('has a min of 0.90.0', function () {
expect(versionMath.min(versions)).to.be('0.90.0');
});
});
describe('eq / lowest version', function () {
it('should be true for 0.90.0', function () {
expect(versionMath.eq('0.90.0', versions)).to.be(true);
});
it('should be false for 1.0', function () {
expect(versionMath.eq('1.0', versions)).to.be(false);
});
});
describe('gt / lowest version', function () {
it('is > 0.20.3', function () {
expect(versionMath.gt('0.20.3', versions)).to.be(true);
});
it('is not > 0.90.0', function () {
expect(versionMath.gt('0.90.0', versions)).to.be(false);
});
it('is not > 1.0.0', function () {
expect(versionMath.gt('1.0.0', versions)).to.be(false);
});
});
describe('gte / lowest version', function () {
it('is >= 0.20.3', function () {
expect(versionMath.gte('0.20.3', versions)).to.be(true);
});
it('is >= 0.90.0', function () {
expect(versionMath.gte('0.90.0', versions)).to.be(true);
});
it('is not >= 1.0.0', function () {
expect(versionMath.gte('1.0.0', versions)).to.be(false);
});
});
describe('lt / highest version', function () {
it('is not < 0.20.3', function () {
expect(versionMath.lt('0.20.3', versions)).to.be(false);
});
it('is not < 2.3.1', function () {
expect(versionMath.lt('2.3.1', versions)).to.be(false);
});
it('is < 2.5', function () {
expect(versionMath.lt('2.5', versions)).to.be(true);
});
});
describe('lte / highest version', function () {
it('is not =< 0.20.3', function () {
expect(versionMath.lte('0.20.3', versions)).to.be(false);
});
it('is =< 2.3.1', function () {
expect(versionMath.lte('2.3.1', versions)).to.be(true);
});
it('is =< 2.5', function () {
expect(versionMath.lte('2.5', versions)).to.be(true);
});
});
describe('is', function () {
it('exactly, <, <=, >, >=', function () {
expect(versionMath.is('0.90.0', versions)).to.be(true);
expect(versionMath.is('0.20.0', versions)).to.be(false);
expect(versionMath.is('>0.20.0', versions)).to.be(true);
expect(versionMath.is('>0.90.0', versions)).to.be(false);
expect(versionMath.is('>0.90.1', versions)).to.be(false);
expect(versionMath.is('>=0.20.0', versions)).to.be(true);
expect(versionMath.is('>=0.90.0', versions)).to.be(true);
expect(versionMath.is('>=0.90.1', versions)).to.be(false);
expect(versionMath.is('<2.5', versions)).to.be(true);
expect(versionMath.is('<2.3.1', versions)).to.be(false);
expect(versionMath.is('<0.90.1', versions)).to.be(false);
expect(versionMath.is('<=2.5', versions)).to.be(true);
expect(versionMath.is('<=2.3.1', versions)).to.be(true);
expect(versionMath.is('<=0.90.1', versions)).to.be(false);
});
});
});
});
});