From a1c5603421ee4b0ac9164575e614c9f3abf30fa9 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 14 Jun 2023 16:54:23 +0200 Subject: [PATCH] [http] Enhance versioned router mock for easier introspection (#159669) ## Summary Adds a helper/util to the Versioned router mock so that tests can more easily be expressed against registered versioned routes. ### Usage See included test. Thanks @paul-tavares for providing some prior art, I adapted slightly to rather return all the versions. Let me know what you think! CC @pgayvallet @paul-tavares --- .../core-http-router-server-mocks/index.ts | 5 ++ .../src/versioned_router.mock.ts | 87 ++++++++++++++++--- .../src/versioned_router_mock_route.test.ts | 77 ++++++++++++++++ 3 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 packages/core/http/core-http-router-server-mocks/src/versioned_router_mock_route.test.ts diff --git a/packages/core/http/core-http-router-server-mocks/index.ts b/packages/core/http/core-http-router-server-mocks/index.ts index 6b88b7565964d6..71b633048d4458 100644 --- a/packages/core/http/core-http-router-server-mocks/index.ts +++ b/packages/core/http/core-http-router-server-mocks/index.ts @@ -8,4 +8,9 @@ export { mockRouter } from './src/router.mock'; export { createVersionedRouterMock } from './src/versioned_router.mock'; +export type { + MockedVersionedRoute, + MockedVersionedRouter, + RegisteredVersionedRoute, +} from './src/versioned_router.mock'; export type { RouterMock, RequestFixtureOptions } from './src/router.mock'; diff --git a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts index 26ff16903ac09c..fb444d30a00008 100644 --- a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts +++ b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts @@ -6,19 +6,84 @@ * Side Public License, v 1. */ -import type { VersionedRouter, VersionedRoute } from '@kbn/core-http-server'; +import type { + VersionedRouter, + VersionedRoute, + VersionedRouteConfig, + AddVersionOpts, + RequestHandler, + KibanaResponseFactory, +} from '@kbn/core-http-server'; -const createMockVersionedRoute = (): VersionedRoute => { - const api: VersionedRoute = { addVersion: jest.fn(() => api) }; +export type MockedVersionedRoute = jest.Mocked; + +const createMockVersionedRoute = (): MockedVersionedRoute => { + const addVersion = jest.fn((_, __) => api); + const api: MockedVersionedRoute = { addVersion }; return api; }; -export type MockedVersionedRouter = jest.Mocked>; +export type MockedVersionedRouter = jest.Mocked> & { + getRoute: (method: keyof VersionedRouter, path: string) => RegisteredVersionedRoute; +}; + +const createMethodHandler = () => jest.fn((_) => createMockVersionedRoute()); + +export const createVersionedRouterMock = (): MockedVersionedRouter => { + const router: Omit = { + delete: createMethodHandler(), + get: createMethodHandler(), + patch: createMethodHandler(), + post: createMethodHandler(), + put: createMethodHandler(), + }; + + return { + ...router, + getRoute: getRoute.bind(null, router), + }; +}; + +export interface RegisteredVersionedRoute { + config: VersionedRouteConfig; + versions: { + [version: string]: { + config: AddVersionOpts; + handler: RequestHandler; + }; + }; +} +const getRoute = ( + router: Omit, + method: keyof VersionedRouter, + path: string +): RegisteredVersionedRoute => { + if (!router[method].mock.calls.length) { + throw new Error(`No routes registered for [${method.toUpperCase()} ${path}]`); + } + + let route: undefined | RegisteredVersionedRoute; + for (let x = 0; x < router[method].mock.calls.length; x++) { + const [routeConfig] = router[method].mock.calls[x]; + if (routeConfig.path === path) { + const mockedAddVersion = router[method].mock.results[x].value as MockedVersionedRoute; + route = { + config: routeConfig, + versions: mockedAddVersion.addVersion.mock.calls.reduce( + (acc, [config, handler]) => ({ + ...acc, + [config.version]: { config, handler }, + }), + {} + ), + }; + break; + } + } -export const createVersionedRouterMock = (): MockedVersionedRouter => ({ - delete: jest.fn((_) => createMockVersionedRoute()), - get: jest.fn((_) => createMockVersionedRoute()), - patch: jest.fn((_) => createMockVersionedRoute()), - post: jest.fn((_) => createMockVersionedRoute()), - put: jest.fn((_) => createMockVersionedRoute()), -}); + if (!route) { + throw new Error(`No routes registered for [${method.toUpperCase()} ${path}]`); + } + + return route; +}; diff --git a/packages/core/http/core-http-router-server-mocks/src/versioned_router_mock_route.test.ts b/packages/core/http/core-http-router-server-mocks/src/versioned_router_mock_route.test.ts new file mode 100644 index 00000000000000..45ebd1c44be5f6 --- /dev/null +++ b/packages/core/http/core-http-router-server-mocks/src/versioned_router_mock_route.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createVersionedRouterMock } from './versioned_router.mock'; + +describe('createVersionedRouterMock#getRoute', () => { + it('throws if no routes are registered', () => { + const versionedRouter = createVersionedRouterMock(); + expect(() => versionedRouter.getRoute('get', '/foo')).toThrow(/No routes registered/); + versionedRouter.get({ path: '/foo', access: 'internal' }); + expect(() => versionedRouter.getRoute('get', '/foo')).not.toThrow(); + expect(() => versionedRouter.getRoute('get', '/bar')).toThrow(/No routes registered/); + }); + it('allows versioned routes to be introspected', () => { + const versionedRouter = createVersionedRouterMock(); + const route = versionedRouter.get({ path: '/foo', access: 'internal' }); + + // Empty case + expect(versionedRouter.getRoute('get', '/foo')).toMatchInlineSnapshot(` + Object { + "config": Object { + "access": "internal", + "path": "/foo", + }, + "versions": Object {}, + } + `); + + const myHandler = jest.fn(); + route + .addVersion({ validate: false, version: '1' }, myHandler) + .addVersion({ validate: false, version: '2' }, myHandler) + .addVersion({ validate: false, version: '3' }, myHandler); + + const introspectedRoute = versionedRouter.getRoute('get', '/foo'); + expect(introspectedRoute).toMatchInlineSnapshot(` + Object { + "config": Object { + "access": "internal", + "path": "/foo", + }, + "versions": Object { + "1": Object { + "config": Object { + "validate": false, + "version": "1", + }, + "handler": [MockFunction], + }, + "2": Object { + "config": Object { + "validate": false, + "version": "2", + }, + "handler": [MockFunction], + }, + "3": Object { + "config": Object { + "validate": false, + "version": "3", + }, + "handler": [MockFunction], + }, + }, + } + `); + + expect(introspectedRoute.versions['3'].handler).toBe(myHandler); + expect(introspectedRoute.versions['3'].config.version).toBe('3'); + expect(introspectedRoute.versions['3'].config.validate).toBe(false); + }); +});