Skip to content

Commit

Permalink
[Security Solution][Endpoint] Suppress some of the jest console.error…
Browse files Browse the repository at this point in the history
… noise created by endpoint list middelware (elastic#102535)

* Mock KibanaServices with services from app context renderer + fix error noise in endpoint host tests
* silence more error noise to the console
* Added options to the ApiHandlerMock function to be able to suppress errors to the console
* Endpoint list - Refactor Fleet mocks for reuse
* Add default http mocks to the app context render test utility
  • Loading branch information
paul-tavares authored and kibanamachine committed Jun 17, 2021
1 parent 9042493 commit 575c564
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { SUB_PLUGINS_REDUCER, mockGlobalState, createSecuritySolutionStorageMock
import { ExperimentalFeatures } from '../../../../common/experimental_features';
import { PLUGIN_ID } from '../../../../../fleet/common';
import { APP_ID } from '../../../../common/constants';
import { KibanaContextProvider } from '../../lib/kibana';
import { KibanaContextProvider, KibanaServices } from '../../lib/kibana';
import { MANAGEMENT_APP_ID } from '../../../management/common/constants';
import { fleetGetPackageListHttpMock } from '../../../management/pages/endpoint_hosts/mocks';

type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;

Expand Down Expand Up @@ -120,6 +121,7 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
</AppRootProvider>
</KibanaContextProvider>
);

const render: UiRender = (ui, options) => {
return reactRender(ui, {
wrapper: AppWrapper as React.ComponentType,
Expand All @@ -134,6 +136,23 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
});
};

// Initialize the singleton `KibanaServices` with global services created for this test instance.
// The module (`../../lib/kibana`) could have been mocked at the test level via `jest.mock()`,
// and if so, then we set the return value of `KibanaServices.get` instead of calling `KibanaServices.init()`
const globalKibanaServicesParams = {
...startServices,
kibanaVersion: '8.0.0',
};

if (jest.isMockFunction(KibanaServices.get)) {
(KibanaServices.get as jest.Mock).mockReturnValue(globalKibanaServicesParams);
} else {
KibanaServices.init(globalKibanaServicesParams);
}

// Some APIs need to be mocked right from the start because they are called as soon as the store is initialized
applyDefaultCoreHttpMocks(coreStart.http);

return {
store,
history,
Expand Down Expand Up @@ -166,3 +185,10 @@ const createCoreStartMock = (): ReturnType<typeof coreMock.createStart> => {

return coreStart;
};

const applyDefaultCoreHttpMocks = (http: AppContextTestRender['coreStart']['http']) => {
// Need to mock getting the endpoint package from the fleet API because it is used as soon
// as the store middleware for Endpoint list is initialized, thus mocking it here would avoid
// unnecessary errors being output to the console
fleetGetPackageListHttpMock(http, { ignoreUnMockedApiRouteErrors: true });
};
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ type HttpMethods = keyof Pick<
const HTTP_METHODS: HttpMethods[] = ['delete', 'fetch', 'get', 'post', 'put', 'head', 'patch'];

export type ApiHandlerMock<R extends ResponseProvidersInterface = ResponseProvidersInterface> = (
http: jest.Mocked<HttpStart>
http: jest.Mocked<HttpStart>,
options?: { ignoreUnMockedApiRouteErrors?: boolean }
) => MockedApi<R>;

interface RouteMock<R extends ResponseProvidersInterface = ResponseProvidersInterface> {
Expand Down Expand Up @@ -132,8 +133,9 @@ export type ApiHandlerMockFactoryProps<
export const httpHandlerMockFactory = <R extends ResponseProvidersInterface = {}>(
mocks: ApiHandlerMockFactoryProps<R>
): ApiHandlerMock<R> => {
return (http) => {
return (http, options) => {
let inflightApiCalls = 0;
const { ignoreUnMockedApiRouteErrors = false } = options ?? {};
const apiDoneListeners: Array<() => void> = [];
const markApiCallAsHandled = async (delay?: RouteMock['delay']) => {
inflightApiCalls++;
Expand Down Expand Up @@ -221,6 +223,10 @@ export const httpHandlerMockFactory = <R extends ResponseProvidersInterface = {}
return priorMockedFunction(...args);
}

if (ignoreUnMockedApiRouteErrors) {
return;
}

const err = new ApiRouteNotMocked(`API [${method.toUpperCase()} ${path}] is not MOCKED!`);
// Append additional stack calling data from when this API mock was applied
err.stack += `\n${testContextStackTrace}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,30 @@ export const endpointPolicyResponseHttpMock = httpHandlerMockFactory<EndpointPol
]
);

type FleetApisHttpMockInterface = ResponseProvidersInterface<{
agentPolicy: () => GetAgentPoliciesResponse;
export type FleetGetPackageListHttpMockInterface = ResponseProvidersInterface<{
packageList: () => GetPackagesResponse;
}>;
export const fleetApisHttpMock = httpHandlerMockFactory<FleetApisHttpMockInterface>([
export const fleetGetPackageListHttpMock = httpHandlerMockFactory<FleetGetPackageListHttpMockInterface>(
[
{
id: 'packageList',
method: 'get',
path: EPM_API_ROUTES.LIST_PATTERN,
handler() {
const generator = new EndpointDocGenerator('seed');

return {
response: [generator.generateEpmPackage()],
};
},
},
]
);

export type FleetGetAgentPolicyListHttpMockInterface = ResponseProvidersInterface<{
agentPolicy: () => GetAgentPoliciesResponse;
}>;
export const fleetGetAgentPolicyListHttpMock = httpHandlerMockFactory([
{
id: 'agentPolicy',
path: AGENT_POLICY_API_ROUTES.LIST_PATTERN,
Expand All @@ -127,18 +146,13 @@ export const fleetApisHttpMock = httpHandlerMockFactory<FleetApisHttpMockInterfa
};
},
},
{
id: 'packageList',
method: 'get',
path: EPM_API_ROUTES.LIST_PATTERN,
handler() {
const generator = new EndpointDocGenerator('seed');
]);

return {
response: [generator.generateEpmPackage()],
};
},
},
type FleetApisHttpMockInterface = FleetGetPackageListHttpMockInterface &
FleetGetAgentPolicyListHttpMockInterface;
export const fleetApisHttpMock = composeHttpHandlerMocks<FleetApisHttpMockInterface>([
fleetGetPackageListHttpMock,
fleetGetAgentPolicyListHttpMock,
]);

type EndpointPageHttpMockInterface = EndpointMetadataHttpMocksInterface &
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
HostResultList,
HostStatus,
MetadataQueryStrategyVersions,
PendingActionsResponse,
} from '../../../../../common/endpoint/types';
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
import {
Expand All @@ -28,6 +29,8 @@ import {
GetAgentsResponse,
} from '../../../../../../fleet/common/types/rest_spec';
import { GetPolicyListResponse } from '../../policy/types';
import { pendingActionsResponseMock } from '../../../../common/lib/endpoint_pending_actions/mocks';
import { ACTION_STATUS_ROUTE } from '../../../../../common/endpoint/constants';

const generator = new EndpointDocGenerator('seed');

Expand Down Expand Up @@ -159,6 +162,11 @@ const endpointListApiPathHandlerMocks = ({
perPage: 10,
};
},

// Pending Actions
[ACTION_STATUS_ROUTE]: (): PendingActionsResponse => {
return pendingActionsResponseMock();
},
};

// Build a GET route handler for each endpoint details based on the list of Endpoints passed on input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ describe('when on the endpoint list page', () => {
afterAll(() => {
abortSpy.mockRestore();
});

beforeEach(() => {
const mockedContext = createAppRootMockRenderer();
({ history, store, coreStart, middlewareSpy } = mockedContext);
Expand Down Expand Up @@ -915,7 +916,10 @@ describe('when on the endpoint list page', () => {
history.push('/endpoints?selected_endpoint=1&show=isolate');
});
renderResult = await renderAndWaitForData();
// Need to reset `http.post` and adjust it so that the mock for http host
// isolation api does not output error noise to the console
coreStart.http.post.mockReset();
coreStart.http.post.mockImplementation(async () => null);
isolateApiMock = hostIsolationHttpMocks(coreStart.http);
});

Expand Down

0 comments on commit 575c564

Please sign in to comment.