[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
This commit is contained in:
Jean-Louis Leysens 2023-06-14 16:54:23 +02:00 committed by GitHub
parent ea982bb92a
commit a1c5603421
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 158 additions and 11 deletions

View file

@ -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';

View file

@ -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<VersionedRoute>;
const createMockVersionedRoute = (): MockedVersionedRoute => {
const addVersion = jest.fn((_, __) => api);
const api: MockedVersionedRoute = { addVersion };
return api;
};
export type MockedVersionedRouter = jest.Mocked<VersionedRouter<any>>;
export type MockedVersionedRouter = jest.Mocked<VersionedRouter<any>> & {
getRoute: (method: keyof VersionedRouter, path: string) => RegisteredVersionedRoute;
};
export const createVersionedRouterMock = (): MockedVersionedRouter => ({
delete: jest.fn((_) => createMockVersionedRoute()),
get: jest.fn((_) => createMockVersionedRoute()),
patch: jest.fn((_) => createMockVersionedRoute()),
post: jest.fn((_) => createMockVersionedRoute()),
put: jest.fn((_) => createMockVersionedRoute()),
});
const createMethodHandler = () => jest.fn((_) => createMockVersionedRoute());
export const createVersionedRouterMock = (): MockedVersionedRouter => {
const router: Omit<MockedVersionedRouter, 'getRoute'> = {
delete: createMethodHandler(),
get: createMethodHandler(),
patch: createMethodHandler(),
post: createMethodHandler(),
put: createMethodHandler(),
};
return {
...router,
getRoute: getRoute.bind(null, router),
};
};
export interface RegisteredVersionedRoute {
config: VersionedRouteConfig<any>;
versions: {
[version: string]: {
config: AddVersionOpts<any, any, any>;
handler: RequestHandler<any, any, any, any, any, KibanaResponseFactory>;
};
};
}
const getRoute = (
router: Omit<MockedVersionedRouter, 'getRoute'>,
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;
}
}
if (!route) {
throw new Error(`No routes registered for [${method.toUpperCase()} ${path}]`);
}
return route;
};

View file

@ -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);
});
});