From 2eb5d09d81e8b5666b0976036830bc77f52aff94 Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 4 Jul 2019 12:24:07 +0200 Subject: [PATCH 1/3] add mocks - expose Elatissearch mocks, KibanaRequest mock - Logging service mock should accept LoggerFactory - Provide mocks for session storage - add return for registerAuth mock - add mocks for http lifecycle toolkits --- .../elasticsearch_service.mock.ts | 20 +++++-- .../http/cookie_session_storage.mocks.ts | 46 ++++++++++++++++ src/core/server/http/http_server.mocks.ts | 51 +++++++++++++++++- src/core/server/http/http_service.mock.ts | 54 +++++++++++++------ .../server/logging/logging_service.mock.ts | 18 ++++--- src/core/server/mocks.ts | 4 +- 6 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 src/core/server/http/cookie_session_storage.mocks.ts diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index 672331199ffa0..88bd426b893a7 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -19,18 +19,30 @@ import { BehaviorSubject } from 'rxjs'; import { ClusterClient } from './cluster_client'; +import { ScopedClusterClient } from './scoped_cluster_client'; import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchService, ElasticsearchServiceSetup } from './elasticsearch_service'; +const createScopedClusterClientMock = (): jest.Mocked> => ({ + callAsInternalUser: jest.fn(), + callAsCurrentUser: jest.fn(), +}); + +const createClusterClientMock = (): jest.Mocked> => ({ + callAsInternalUser: jest.fn(), + asScoped: jest.fn().mockImplementation(createScopedClusterClientMock), + close: jest.fn(), +}); + const createSetupContractMock = () => { const setupContract: ElasticsearchServiceSetup = { legacy: { config$: new BehaviorSubject({} as ElasticsearchConfig), }, - createClient: jest.fn(), - adminClient$: new BehaviorSubject({} as ClusterClient), - dataClient$: new BehaviorSubject({} as ClusterClient), + createClient: jest.fn().mockImplementation(createClusterClientMock), + adminClient$: new BehaviorSubject((createClusterClientMock() as unknown) as ClusterClient), + dataClient$: new BehaviorSubject((createClusterClientMock() as unknown) as ClusterClient), }; return setupContract; }; @@ -50,4 +62,6 @@ const createMock = () => { export const elasticsearchServiceMock = { create: createMock, createSetupContract: createSetupContractMock, + createClusterClient: createClusterClientMock, + createScopedClusterClient: createScopedClusterClientMock, }; diff --git a/src/core/server/http/cookie_session_storage.mocks.ts b/src/core/server/http/cookie_session_storage.mocks.ts new file mode 100644 index 0000000000000..2752cbb9dd5f5 --- /dev/null +++ b/src/core/server/http/cookie_session_storage.mocks.ts @@ -0,0 +1,46 @@ +/* + * 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 { SessionStorageFactory, SessionStorage } from './session_storage'; + +const creatSessionStorageMock = (): jest.Mocked> => ({ + get: jest.fn().mockResolvedValue({}), + set: jest.fn(), + clear: jest.fn(), +}); + +type ReturnMocked = { + [K in keyof T]: T[K] extends (...args: any[]) => infer U + ? (...args: any[]) => jest.Mocked + : T[K]; +}; + +type DeepMocked = jest.Mocked>; + +const creatSessionStorageFactoryMock = () => { + const mocked: DeepMocked> = { + asScoped: jest.fn(), + }; + mocked.asScoped.mockImplementation(creatSessionStorageMock); + return mocked; +}; + +export const sessionStorageMock = { + create: creatSessionStorageMock, + createFactory: creatSessionStorageFactoryMock, +}; diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index df1620b021836..b56af3aa768b6 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -18,8 +18,55 @@ */ import { Request, ResponseToolkit } from 'hapi'; import { merge } from 'lodash'; +import url from 'url'; +import querystring from 'querystring'; -import { KibanaRequest } from './router'; +import { schema } from '@kbn/config-schema'; + +import { KibanaRequest, RouteMethod } from './router'; + +interface RequestFixtureOptions { + headers?: Record; + params?: Record; + body?: Record; + query?: Record; + path?: string; + method?: RouteMethod; +} + +function createKibanaRequestMock({ + path = '/path', + headers = { accept: 'something/html' }, + params = {}, + body = {}, + query, + method = 'get', +}: RequestFixtureOptions = {}) { + return KibanaRequest.from( + { + headers, + params, + query: query || {}, + payload: body, + path, + method, + url: { + path, + query: query ? querystring.stringify(query) : query, + search: query ? `?${querystring.stringify(query)}` : query, + }, + route: { settings: {} }, + raw: { + req: {}, + }, + } as any, + { + params: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { allowUnknowns: true }), + } + ); +} type DeepPartial = T extends any[] ? DeepPartialArray @@ -54,7 +101,7 @@ function createRawResponseToolkitMock(customization: DeepPartial KibanaRequest.from(createRawRequestMock()), + createKibanaRequest: createKibanaRequestMock, createRawRequest: createRawRequestMock, createRawResponseToolkit: createRawResponseToolkitMock, }; diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 9b241e8679318..5dba7a16c8592 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -21,10 +21,22 @@ import { Server } from 'hapi'; import { HttpService } from './http_service'; import { HttpServerSetup } from './http_server'; import { HttpServiceSetup } from './http_service'; +import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; +import { AuthToolkit } from './lifecycle/auth'; +import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; +import { sessionStorageMock } from './cookie_session_storage.mocks'; type ServiceSetupMockType = jest.Mocked & { basePath: jest.Mocked; }; + +const createBasePathMock = (): jest.Mocked => ({ + get: jest.fn(), + set: jest.fn(), + prepend: jest.fn(), + remove: jest.fn(), +}); + const createSetupContractMock = () => { const setupContract: ServiceSetupMockType = { // we can mock some hapi server method when we need it @@ -33,12 +45,7 @@ const createSetupContractMock = () => { registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), registerRouter: jest.fn(), - basePath: { - get: jest.fn(), - set: jest.fn(), - prepend: jest.fn(), - remove: jest.fn(), - }, + basePath: createBasePathMock(), auth: { get: jest.fn(), isAuthenticated: jest.fn(), @@ -47,17 +54,12 @@ const createSetupContractMock = () => { createNewServer: jest.fn(), }; setupContract.createNewServer.mockResolvedValue({} as HttpServerSetup); + setupContract.registerAuth.mockResolvedValue({ + sessionStorageFactory: sessionStorageMock.createFactory(), + }); return setupContract; }; -const createStartContractMock = () => { - const startContract = { - isListening: jest.fn(), - }; - startContract.isListening.mockReturnValue(true); - return startContract; -}; - type HttpServiceContract = PublicMethodsOf; const createHttpServiceMock = () => { const mocked: jest.Mocked = { @@ -66,12 +68,32 @@ const createHttpServiceMock = () => { stop: jest.fn(), }; mocked.setup.mockResolvedValue(createSetupContractMock()); - mocked.start.mockResolvedValue(createStartContractMock()); return mocked; }; +const createOnPreAuthToolkitMock = (): jest.Mocked => ({ + next: jest.fn(), + redirected: jest.fn(), + rejected: jest.fn(), +}); + +const createAuthToolkitMock = (): jest.Mocked => ({ + authenticated: jest.fn(), + redirected: jest.fn(), + rejected: jest.fn(), +}); + +const createOnPostAuthToolkitMock = (): jest.Mocked => ({ + next: jest.fn(), + redirected: jest.fn(), + rejected: jest.fn(), +}); + export const httpServiceMock = { create: createHttpServiceMock, + createBasePath: createBasePathMock, createSetupContract: createSetupContractMock, - createStartContract: createStartContractMock, + createOnPreAuthToolkit: createOnPreAuthToolkitMock, + createAuthToolkit: createAuthToolkitMock, + createOnPostAuthToolkit: createOnPostAuthToolkitMock, }; diff --git a/src/core/server/logging/logging_service.mock.ts b/src/core/server/logging/logging_service.mock.ts index 4bef2fe745c7d..d423e6b064e5f 100644 --- a/src/core/server/logging/logging_service.mock.ts +++ b/src/core/server/logging/logging_service.mock.ts @@ -20,6 +20,7 @@ // Test helpers to simplify mocking logs and collecting all their outputs import { Logger } from './logger'; import { LoggingService } from './logging_service'; +import { LoggerFactory } from './logger_factory'; type LoggingServiceContract = PublicMethodsOf; type MockedLogger = jest.Mocked; @@ -50,8 +51,8 @@ const createLoggingServiceMock = () => { return mocked; }; -const collectLoggingServiceMock = (mocked: ReturnType) => { - const mockLog = mocked.get() as MockedLogger; +const collectLoggingServiceMock = (loggerFactory: LoggerFactory) => { + const mockLog = loggerFactory.get() as MockedLogger; return { debug: mockLog.debug.mock.calls, error: mockLog.error.mock.calls, @@ -63,13 +64,14 @@ const collectLoggingServiceMock = (mocked: ReturnType) => { - const mockLog = mocked.get() as MockedLogger; - mocked.get.mockClear(); - mocked.asLoggerFactory.mockClear(); - mocked.upgrade.mockClear(); - mocked.stop.mockClear(); +const clearLoggingServiceMock = (loggerFactory: LoggerFactory) => { + const mockedLoggerFactory = (loggerFactory as unknown) as jest.Mocked; + mockedLoggerFactory.get.mockClear(); + mockedLoggerFactory.asLoggerFactory.mockClear(); + mockedLoggerFactory.upgrade.mockClear(); + mockedLoggerFactory.stop.mockClear(); + const mockLog = loggerFactory.get() as MockedLogger; mockLog.debug.mockClear(); mockLog.info.mockClear(); mockLog.warn.mockClear(); diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index af0eed6ba833d..518221d2b9bd5 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -22,6 +22,8 @@ import { loggingServiceMock } from './logging/logging_service.mock'; import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; import { httpServiceMock } from './http/http_service.mock'; +export { httpServerMock } from './http/http_server.mocks'; +export { sessionStorageMock } from './http/cookie_session_storage.mocks'; export { configServiceMock } from './config/config_service.mock'; export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; export { httpServiceMock } from './http/http_service.mock'; @@ -38,7 +40,7 @@ export function pluginInitializerContextConfigMock(config: T) { } function pluginInitializerContextMock(config: T) { - const mock: jest.Mocked> = { + const mock: PluginInitializerContext = { logger: loggingServiceMock.create(), env: { mode: { From c7f6999e41692161d4a4fbeedc90b41f91504a3f Mon Sep 17 00:00:00 2001 From: restrry Date: Mon, 8 Jul 2019 18:09:10 +0200 Subject: [PATCH 2/3] remove leftovers --- src/core/server/http/http_server.mocks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index b56af3aa768b6..4bb40f9d85d82 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -18,7 +18,7 @@ */ import { Request, ResponseToolkit } from 'hapi'; import { merge } from 'lodash'; -import url from 'url'; + import querystring from 'querystring'; import { schema } from '@kbn/config-schema'; From 4d457f430c09ab110abd3c07994b32a508f994ca Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 10 Jul 2019 16:21:05 +0200 Subject: [PATCH 3/3] address @eli comments --- src/core/server/http/cookie_session_storage.mocks.ts | 6 +++--- src/core/server/http/http_server.mocks.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/server/http/cookie_session_storage.mocks.ts b/src/core/server/http/cookie_session_storage.mocks.ts index 2752cbb9dd5f5..3d1ddb08b6053 100644 --- a/src/core/server/http/cookie_session_storage.mocks.ts +++ b/src/core/server/http/cookie_session_storage.mocks.ts @@ -18,7 +18,7 @@ */ import { SessionStorageFactory, SessionStorage } from './session_storage'; -const creatSessionStorageMock = (): jest.Mocked> => ({ +const createSessionStorageMock = (): jest.Mocked> => ({ get: jest.fn().mockResolvedValue({}), set: jest.fn(), clear: jest.fn(), @@ -36,11 +36,11 @@ const creatSessionStorageFactoryMock = () => { const mocked: DeepMocked> = { asScoped: jest.fn(), }; - mocked.asScoped.mockImplementation(creatSessionStorageMock); + mocked.asScoped.mockImplementation(createSessionStorageMock); return mocked; }; export const sessionStorageMock = { - create: creatSessionStorageMock, + create: createSessionStorageMock, createFactory: creatSessionStorageFactoryMock, }; diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index 4bb40f9d85d82..fbf98aa4f9c99 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -39,21 +39,22 @@ function createKibanaRequestMock({ headers = { accept: 'something/html' }, params = {}, body = {}, - query, + query = {}, method = 'get', }: RequestFixtureOptions = {}) { + const queryString = querystring.stringify(query); return KibanaRequest.from( { headers, params, - query: query || {}, + query, payload: body, path, method, url: { path, - query: query ? querystring.stringify(query) : query, - search: query ? `?${querystring.stringify(query)}` : query, + query: queryString, + search: queryString ? `?${queryString}` : queryString, }, route: { settings: {} }, raw: {