@boris-turner/amphora-search
Version:
Making it easier to use Elastic Search with Amphora
560 lines (458 loc) • 16.8 kB
JavaScript
'use strict';
var filename = __filename.split('/').pop().split('.').shift(),
lib = require('./' + filename),
client = createFakeClientClass(),
logMock = jest.fn();
/**
* Create a fake elasticsearch client.
* @returns {*}
*/
function createFakeClientClass() {
return {
bulk: jest.fn(),
search: jest.fn(),
msearch: jest.fn(),
delete: jest.fn(),
index: jest.fn(),
ping: jest.fn(),
search: jest.fn(),
exists: jest.fn(),
get: jest.fn(),
update: jest.fn(),
cat: {
aliases: jest.fn()
},
indices: {
create: jest.fn(),
delete: jest.fn(),
exists: jest.fn(),
existsAlias: jest.fn(),
existsMapping: jest.fn(),
putAlias: jest.fn(),
create: jest.fn(),
getMapping: jest.fn(),
putMapping: jest.fn(),
putSettings: jest.fn(),
close: jest.fn(),
open: jest.fn()
}
};
}
beforeEach(() => {
lib.setup(client);
lib.setLog(logMock);
});
describe(filename, () => {
describe('setup', () => {
test('creates an Elastic client if an endpoint is defined and no override is passed in', () => {
return expect(lib.setup()).rejects.toThrow();
});
test('returns an instance with fake client if one is passed in', () => {
lib.setup(client);
expect(lib.client).toEqual(client);
});
test('creates an Elastic client if an endpoint is defined and no override is passed in', () => {
lib.endpoint = 'whatever';
lib.setup();
expect(lib.client).not.toEqual({});
});
});
describe('healthCheck', () => {
const fn = lib.healthCheck;
test('pings unsuccessfully', () => {
client.ping.mockRejectedValue(new Error('failed'));
return expect(fn(client)).rejects.toBeInstanceOf(Error);
});
test('pings successfully', () => {
client.ping.mockResolvedValue();
return expect(fn(client)).resolves.toBeUndefined();
});
});
describe('existsMapping', () => {
const fn = lib.existsMapping;
it('is called successfully', () => {
client.indices.getMapping.mockResolvedValue(true);
return expect(fn('index', 'type')).resolves.toBeTruthy();
});
});
describe('initIndex', () => {
const fn = lib.initIndex;
test('is called successfully', () => {
client.indices.create.mockResolvedValue(true);
return fn('index', 'type')
.then(() => {
expect(logMock).toHaveBeenCalledTimes(1);
});
});
test('handles error properly', () => {
client.indices.create.mockRejectedValue(false);
return fn('index', 'mappings')
.catch(() => {
expect(client.indices.create).toHaveBeenCalledTimes(1);
});
});
test('uses and empty object if no settings are passed in', () => {
client.indices.create.mockRejectedValue(false);
return fn('index')
.catch(() => {
expect(client.indices.create).toHaveBeenCalledTimes(1);
});
});
});
describe('initAlias', () => {
const fn = lib.initAlias;
test('is called successfully', () => {
client.indices.putAlias.mockResolvedValue(true);
return fn('name', 'index')
.then(() => {
expect(client.indices.putAlias).toHaveBeenCalledTimes(1);
});
});
test('handles error properly', () => {
client.indices.putAlias.mockRejectedValue(false);
return fn('name', 'index')
.catch(() => {
expect(client.indices.putAlias).toHaveBeenCalledTimes(1);
});
});
});
describe('initMapping', () => {
const fn = lib.initMapping;
test('is called successfully', () => {
client.indices.putMapping.mockResolvedValue(true);
return fn('name', 'index')
.then(() => {
expect(client.indices.putMapping).toHaveBeenCalledTimes(1);
});
});
test('handles error properly', () => {
client.indices.putMapping.mockRejectedValue(false);
return fn('name', 'index')
.catch(resp => {
expect(resp).toBeFalsy();
});
});
});
describe('createAliasIfNone', () => {
const fn = lib.createAliasIfNone;
test('calls existsAlias successfully', () => {
lib.existsAlias = jest.fn().mockResolvedValue(true);
return fn('index')
.then(() => {
expect(lib.existsAlias).toHaveBeenCalledTimes(1);
});
});
test('calls initAlias successfully', () => {
lib.existsAlias = jest.fn().mockResolvedValue(false);
lib.initAlias = jest.fn().mockResolvedValue(true);
return fn('index')
.then(() => {
expect(lib.initAlias).toHaveBeenCalledTimes(1);
expect(logMock).toHaveBeenCalledTimes(1);
});
});
test('calls initAlias successfully and catches if rejected', () => {
lib.existsAlias = jest.fn().mockResolvedValue(false);
lib.initAlias = jest.fn().mockRejectedValue(false);
return fn('index')
.then(() => {
expect(logMock).toHaveBeenCalledTimes(1);
expect(logMock).toHaveBeenCalledWith('error', false);
});
});
});
describe('createMappingIfNone', () => {
const fn = lib.createMappingIfNone;
test('calls existsMapping successfully', () => {
lib.existsMapping = jest.fn().mockResolvedValue({
index: { mappings: { _doc: { properties: {} } } }
});
lib.initMapping = jest.fn().mockResolvedValue(false);
return fn('index', 'mapping')
.then(() => {
expect(lib.existsMapping).toHaveBeenCalledTimes(1);
});
});
test('calls initMapping successfully', () => {
lib.existsMapping = jest.fn().mockResolvedValue();
lib.initMapping = jest.fn().mockResolvedValue(true);
return fn('index', 'mapping')
.then(() => {
expect(lib.initMapping).toHaveBeenCalledTimes(1);
});
});
test('catches on initMapping if there is a rejection', () => {
lib.existsMapping = jest.fn().mockResolvedValue();
lib.initMapping = jest.fn().mockRejectedValue(false);
return fn('index', 'mapping')
.then(() => {
expect(lib.initMapping).toHaveBeenCalledTimes(1);
});
});
test('logs if the mapping is already found', () => {
lib.existsMapping = jest.fn().mockResolvedValue({
index: { mappings: { _doc: { properties: {} } } }
});
lib.initMapping = jest.fn().mockRejectedValue(false);
return fn('index', 'mapping')
.then(() => expect(lib.initMapping).not.toHaveBeenCalled());
});
});
describe('createIndexName', () => {
const fn = lib.createIndexName;
it('returns a string', () => {
expect(fn('alias')).toBe('alias_v1');
});
});
describe('createIndexIfNone', () => {
const fn = lib.createIndexIfNone;
it('calls the createIndexIfNone method', () => {
client.indices.exists.mockResolvedValue(true);
return fn('index')
.then(() => expect(client.indices.exists).toHaveBeenCalledTimes(1));
});
it('calls the initIndex function', () => {
client.indices.exists.mockResolvedValue(false);
lib.initIndex = jest.fn().mockResolvedValue(true);
return fn('index')
.then(() => expect(lib.initIndex).toHaveBeenCalledTimes(1));
});
it('when initIndex is called it will catch if it is rejected', () => {
client.indices.exists.mockResolvedValue(false);
lib.initIndex = jest.fn().mockRejectedValue(false);
return fn('index')
.then(() => expect(lib.initIndex).toHaveBeenCalledTimes(1));
});
});
describe('existsIndex', () => {
const fn = lib.existsIndex;
it('calls the existsIndex method provided by the ES client', () => {
client.indices.exists.mockResolvedValue('ES Client `existsIndex` called');
return fn('name')
.then(() => expect(client.indices.exists).toHaveBeenCalledTimes(1));
});
});
describe('existsDocument', () => {
const fn = lib.existsDocument;
test('rejects if no id is provided', () => {
return expect(fn('pages')).rejects.toThrow();
});
test('calls the exists method provided by the ES client', () => {
client.exists.mockResolvedValue('ES Client `exists` called');
return fn('pages', 'site/_pages/foo')
.then(() => expect(client.exists).toHaveBeenCalledTimes(1));
});
});
describe('getDocument', () => {
const fn = lib.getDocument;
it('rejects if no id is provided', () => {
const errMsg = 'Cannot get a document without the id';
return fn('pages')
.catch(err => {
expect(err).toHaveProperty('message', errMsg);
expect(logMock).toHaveBeenCalledTimes(1);
});
});
it('calls the exists method provided by the ES client', () => {
client.get.mockResolvedValue(Promise.resolve('ES Client `get` called'));
return fn('pages', 'site/_pages/foo')
.then(() => {
expect(client.get).toHaveBeenCalledTimes(1);
});
});
});
describe('update', () => {
var fn = lib.update;
it('rejects if no data is provided', () => {
const errMsg = 'Updating an Elastic document requires a data object';
return fn('pages', 'site/_pages/foo')
.catch(err => expect(err).toHaveProperty('message', errMsg));
});
it('calls the update method provided by the ES client', () => {
client.update.mockResolvedValue('ES Client `update` called');
return fn('pages', 'site/_pages/foo', {})
.then(() => expect(client.update).toHaveBeenCalledTimes(1));
});
it('calls the update method provided by the ES client with refresh param', () => {
client.update.mockResolvedValue('ES Client `update` called');
return fn('pages', 'site/_pages/foo', {}, true)
.then(()=> expect(client.update).toHaveBeenCalled());
});
});
describe('validateIndices', () => {
const fn = lib.validateIndices,
mapping = {
pages: {
_doc: {
dynamic: 'strict',
properties: {
prop: { type: 'string', index: 'not_analyzed' },
}
}
}
};
test('calls createIndexIfNone, createAliasIfNone and createMappingIfNone', () => {
lib.createIndexIfNone = jest.fn().mockResolvedValue('index');
lib.createAliasIfNone = jest.fn().mockResolvedValue('alias');
lib.initAlias = jest.fn().mockResolvedValue({});
lib.createMappingIfNone = jest.fn().mockResolvedValue(mapping);
return fn(mapping, {})
.then(() => {
expect(lib.createIndexIfNone).toHaveBeenCalled();
expect(lib.createAliasIfNone).not.toHaveBeenCalled();
expect(lib.createMappingIfNone).toHaveBeenCalled();
});
});
test('it logs if something fails', () => {
lib.createIndexIfNone = jest.fn().mockResolvedValue('index');
lib.createAliasIfNone = jest.fn().mockResolvedValue('alias');
lib.initAlias = jest.fn().mockRejectedValue(new Error('foo'));
lib.createMappingIfNone = jest.fn().mockResolvedValue(mapping);
return fn(mapping, {})
.catch(() => {
expect(lib.createIndexIfNone).toHaveBeenCalled();
expect(lib.createAliasIfNone).not.toHaveBeenCalled();
expect(lib.createMappingIfNone).toHaveBeenCalled();
});
});
test('it logs if something fails', () => {
lib.existsAlias = jest.fn().mockRejectedValue(new Error('foo'));
return fn(mapping, {})
.catch(() => {
expect(logMock).toHaveBeenCalled();
});
});
test('calls createIndexIfNone, createAliasIfNone and createMappingIfNone', () => {
lib.existsAlias.mockResolvedValue(true);
lib.createMappingIfNone = jest.fn().mockResolvedValue(mapping);
return fn(mapping, {})
.then(() => {
expect(lib.createIndexIfNone).not.toHaveBeenCalled();
expect(lib.createAliasIfNone).not.toHaveBeenCalled();
expect(logMock).toHaveBeenCalledWith('debug', 'Elasticsearch alias exists at pages_v1');
});
});
});
describe('existsAlias', () => {
const fn = lib.existsAlias;
test('calls the existsAlias method provided by the ES client', () => {
client.indices.existsAlias.mockResolvedValue('ES Client `existsAlias` called');
return fn('name')
.then(() => expect(client.indices.existsAlias).toHaveBeenCalled());
});
});
describe('query', () => {
const fn = lib.query;
test('calls the search method provided by the ES client', () => {
client.search.mockResolvedValue();
return fn('index', 'query', 'type')
.then(() => expect(client.search).toHaveBeenCalled());
});
test('logs an error if one arises', () => {
client.search.mockRejectedValue(new Error('foo'));
return fn('index', 'query', 'type')
.catch(() => {
expect(logMock).toHaveBeenCalled();
});
});
});
describe('del', () => {
const fn = lib.del;
test('calls the delete method provided by the ES client', () => {
client.delete.mockResolvedValue('ES Client `delete` called');
return fn('index', 'type', 'some/ref')
.then(() => expect(client.delete).toHaveBeenCalled());
});
test('logs an error if one arises', () => {
client.delete.mockRejectedValue(new Error('foo'));
return fn('index', 'query', 'type')
.catch(() => {
expect(logMock).toHaveBeenCalled();
});
});
});
describe('put', () => {
const fn = lib.put;
test('calls the index method provided by the ES client', () => {
client.index.mockResolvedValue('ES Client `put` called');
return fn('index', 'type', 'some/ref')
.then(() => expect(client.index).toHaveBeenCalled());
});
test('logs an error if one arises', () => {
client.index.mockRejectedValue(new Error('foo'));
return fn('index', 'query', 'type')
.catch(() => {
expect(logMock).toHaveBeenCalled();
});
});
});
describe('convertRedisBatchtoElasticBatch', () => {
const fn = lib.convertRedisBatchtoElasticBatch;
test('logs an error if op.value is a string', () => {
const ops = [{ value: 'value' }];
fn('index', ops);
expect(logMock).toHaveBeenCalled();
});
test('returns an array of ops if type property equals "put"', () => {
var ops = [{ value: {}, type: 'put' }];
expect(fn('index', ops)).toEqual([{ index: { _index: 'index', _type: '_doc' } }, {}]);
});
test('assigns a "key" property if one is defined in the op', () => {
var ops = [{ value: {}, type: 'put', key: 'key' }];
expect(fn('index', ops)).toEqual([{ index: { _id: 'key', _index: 'index', _type: '_doc' } }, {}]);
});
test('assigns a "key" property if one is defined in the op', () => {
var ops = [{ value: {}, type: 'get', key: 'key' }];
expect(fn('index', ops)).toEqual([]);
});
});
describe('batch', () => {
const fn = lib.batch;
test('calls bulk method successfully', () => {
client.bulk.mockResolvedValue();
return fn([])
.then(() => expect(client.bulk).toHaveBeenCalled());
});
test('bulk rejects and returns an Error if failed', () => {
client.bulk.mockResolvedValue({ errors: true });
return fn([])
.catch(error => expect(error).toBeInstanceOf(Error));
});
});
describe('putSettings', () => {
const fn = lib.putSettings;
test('calls the `putSettings` method exposed on the client', () => {
client.indices.putSettings.mockResolvedValue(true);
return fn('pages', {})
.then(() => expect(client.indices.putSettings).toHaveBeenCalled());
});
test('putSettings rejects and returns an Error if failed', () => {
client.indices.putSettings.mockRejectedValue(new Error('error message'));
return fn('pages', {})
.catch(error => {
expect(error).toBeInstanceOf(Error);
});
});
});
describe('getInstance', () => {
test('returns an instance of the client', () => {
expect(lib.getInstance()).toEqual(lib.client);
});
});
describe('findIndexFromAlias', () => {
test('given an alias, returns an index', () => {
client.cat.aliases.mockResolvedValue([{ alias: 'foo', index: 'foo_v1'}]);
return lib.findIndexFromAlias('foo')
.then(resp => {
expect(resp).toBe('foo_v1');
});
});
test('returns alias with default version if no index is found', () => {
client.cat.aliases.mockResolvedValue([]);
return lib.findIndexFromAlias('foo')
.then(resp => {
expect(resp).toBe('foo_v1');
});
});
});
});