Skip to content

Commit

Permalink
[http] Enhance versioned router mock for easier introspection (#159669)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
jloleysens committed Jun 14, 2023
1 parent ea982bb commit a1c5603
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 11 deletions.
5 changes: 5 additions & 0 deletions packages/core/http/core-http-router-server-mocks/index.ts
Expand Up @@ -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';
Expand Up @@ -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;
};

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

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

0 comments on commit a1c5603

Please sign in to comment.