diff --git a/src/plugins/data/server/search/es_search/es_search_service.test.ts b/src/plugins/data/server/search/es_search/es_search_service.test.ts new file mode 100644 index 0000000000000..faf9487159c15 --- /dev/null +++ b/src/plugins/data/server/search/es_search/es_search_service.test.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { coreMock } from '../../../../../core/server/mocks'; +import { EsSearchService } from './es_search_service'; +import { PluginInitializerContext } from '../../../../../core/server'; +import { searchSetupMock } from '../mocks'; + +describe('ES search strategy service', () => { + let service: EsSearchService; + + const mockCoreSetup = coreMock.createSetup(); + const opaqueId = Symbol(); + const context: PluginInitializerContext = { + opaqueId, + config: { + createIfExists: jest.fn(), + create: jest.fn(), + }, + env: { + mode: { + dev: false, + name: 'development', + prod: false, + }, + }, + logger: { + get: jest.fn(), + }, + }; + + beforeEach(() => { + service = new EsSearchService(context); + }); + + describe('setup()', () => { + it('registers the ES search strategy', async () => { + service.setup(mockCoreSetup, { + search: searchSetupMock, + }); + expect(searchSetupMock.registerSearchStrategyProvider).toBeCalled(); + }); + }); +}); diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts new file mode 100644 index 0000000000000..619a28df839bd --- /dev/null +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { coreMock } from '../../../../../core/server/mocks'; +import { esSearchStrategyProvider } from './es_search_strategy'; + +describe('ES search strategy', () => { + const mockCoreSetup = coreMock.createSetup(); + const mockApiCaller = jest.fn().mockResolvedValue({ + _shards: { + total: 10, + failed: 1, + skipped: 2, + successful: 7, + }, + }); + const mockSearch = jest.fn(); + + beforeEach(() => { + mockApiCaller.mockClear(); + mockSearch.mockClear(); + }); + + it('returns a strategy with `search`', () => { + const esSearch = esSearchStrategyProvider( + { + core: mockCoreSetup, + }, + mockApiCaller, + mockSearch + ); + + expect(typeof esSearch.search).toBe('function'); + }); + + it('logs the response if `debug` is set to `true`', () => { + const spy = jest.spyOn(console, 'log'); + const esSearch = esSearchStrategyProvider( + { + core: mockCoreSetup, + }, + mockApiCaller, + mockSearch + ); + + expect(spy).not.toBeCalled(); + + esSearch.search({ params: {}, debug: true }); + + expect(spy).toBeCalled(); + }); + + it('calls the API caller with the params', () => { + const params = { index: 'logstash-*' }; + const esSearch = esSearchStrategyProvider( + { + core: mockCoreSetup, + }, + mockApiCaller, + mockSearch + ); + + esSearch.search({ params }); + + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toBe('search'); + expect(mockApiCaller.mock.calls[0][1]).toEqual(params); + }); + + it('returns total, loaded, and raw response', async () => { + const params = { index: 'logstash-*' }; + const esSearch = esSearchStrategyProvider( + { + core: mockCoreSetup, + }, + mockApiCaller, + mockSearch + ); + + const response = await esSearch.search({ params }); + + expect(response).toHaveProperty('total'); + expect(response).toHaveProperty('loaded'); + expect(response).toHaveProperty('rawResponse'); + }); +}); diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index ac585363495c6..136e7a1d580c9 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -20,5 +20,7 @@ export const searchSetupMock = { registerSearchStrategyContext: jest.fn(), registerSearchStrategyProvider: jest.fn(), - __LEGACY: jest.fn(), + __LEGACY: { + search: jest.fn(), + }, }; diff --git a/src/plugins/data/server/search/routes.test.ts b/src/plugins/data/server/search/routes.test.ts new file mode 100644 index 0000000000000..0923b52565097 --- /dev/null +++ b/src/plugins/data/server/search/routes.test.ts @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { httpServiceMock, httpServerMock } from '../../../../../src/core/server/mocks'; +import { registerSearchRoute } from './routes'; +import { IRouter, ScopedClusterClient } from 'kibana/server'; + +describe('Search service', () => { + let routerMock: jest.Mocked; + + beforeEach(() => { + routerMock = httpServiceMock.createRouter(); + }); + + it('registers a post route', async () => { + registerSearchRoute(routerMock); + expect(routerMock.post).toBeCalled(); + }); + + it('handler calls context.search.search with the given request and strategy', async () => { + const mockSearch = jest.fn().mockResolvedValue('yay'); + const mockContext = { + core: { + elasticsearch: { + dataClient: {} as ScopedClusterClient, + adminClient: {} as ScopedClusterClient, + }, + }, + search: { + search: mockSearch, + }, + }; + const mockBody = { params: {} }; + const mockParams = { strategy: 'foo' }; + const mockRequest = httpServerMock.createKibanaRequest({ + body: mockBody, + params: mockParams, + }); + const mockResponse = httpServerMock.createResponseFactory(); + + registerSearchRoute(routerMock); + const handler = routerMock.post.mock.calls[0][1]; + await handler(mockContext, mockRequest, mockResponse); + + expect(mockSearch).toBeCalled(); + expect(mockSearch.mock.calls[0][0]).toStrictEqual(mockBody); + expect(mockSearch.mock.calls[0][1]).toBe(mockParams.strategy); + expect(mockResponse.ok).toBeCalled(); + expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: 'yay' }); + }); + + it('handler throws internal error if the search throws an error', async () => { + const mockSearch = jest.fn().mockRejectedValue('oh no'); + const mockContext = { + core: { + elasticsearch: { + dataClient: {} as ScopedClusterClient, + adminClient: {} as ScopedClusterClient, + }, + }, + search: { + search: mockSearch, + }, + }; + const mockBody = { params: {} }; + const mockParams = { strategy: 'foo' }; + const mockRequest = httpServerMock.createKibanaRequest({ + body: mockBody, + params: mockParams, + }); + const mockResponse = httpServerMock.createResponseFactory(); + + registerSearchRoute(routerMock); + const handler = routerMock.post.mock.calls[0][1]; + await handler(mockContext, mockRequest, mockResponse); + + expect(mockSearch).toBeCalled(); + expect(mockSearch.mock.calls[0][0]).toStrictEqual(mockBody); + expect(mockSearch.mock.calls[0][1]).toBe(mockParams.strategy); + expect(mockResponse.internalError).toBeCalled(); + expect(mockResponse.internalError.mock.calls[0][0]).toEqual({ body: 'oh no' }); + }); +});