From f019e2a01d68f2688199ef83ada5ef0cf74f6569 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 7 Dec 2021 08:20:48 +0100 Subject: [PATCH] mock `elastic-apm-node` in `@kbn/test` jest preset (#120324) * mock `elastic-apm-node` in `@kbn/test` jest preset * adapt kbn-apm-config-loader tests * use TS for agent mock Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/init_apm.test.ts | 19 +++--- packages/kbn-test/BUILD.bazel | 1 + packages/kbn-test/jest-preset.js | 1 + .../kbn-test/src/jest/mocks/apm_agent_mock.ts | 63 +++++++++++++++++++ 4 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 packages/kbn-test/src/jest/mocks/apm_agent_mock.ts diff --git a/packages/kbn-apm-config-loader/src/init_apm.test.ts b/packages/kbn-apm-config-loader/src/init_apm.test.ts index 95f0a15a448c8..cabab421519bd 100644 --- a/packages/kbn-apm-config-loader/src/init_apm.test.ts +++ b/packages/kbn-apm-config-loader/src/init_apm.test.ts @@ -12,13 +12,13 @@ import { initApm } from './init_apm'; import apm from 'elastic-apm-node'; describe('initApm', () => { - let apmAddFilterSpy: jest.SpyInstance; - let apmStartSpy: jest.SpyInstance; + let apmAddFilterMock: jest.Mock; + let apmStartMock: jest.Mock; let getConfig: jest.Mock; beforeEach(() => { - apmAddFilterSpy = jest.spyOn(apm, 'addFilter').mockImplementation(() => undefined); - apmStartSpy = jest.spyOn(apm, 'start').mockImplementation(() => undefined as any); + apmAddFilterMock = apm.addFilter as jest.Mock; + apmStartMock = apm.start as jest.Mock; getConfig = jest.fn(); mockLoadConfiguration.mockImplementation(() => ({ @@ -27,7 +27,8 @@ describe('initApm', () => { }); afterEach(() => { - jest.restoreAllMocks(); + apmAddFilterMock.mockReset(); + apmStartMock.mockReset(); mockLoadConfiguration.mockReset(); }); @@ -48,8 +49,8 @@ describe('initApm', () => { it('registers a filter using `addFilter`', () => { initApm(['foo', 'bar'], 'rootDir', true, 'service-name'); - expect(apmAddFilterSpy).toHaveBeenCalledTimes(1); - expect(apmAddFilterSpy).toHaveBeenCalledWith(expect.any(Function)); + expect(apmAddFilterMock).toHaveBeenCalledTimes(1); + expect(apmAddFilterMock).toHaveBeenCalledWith(expect.any(Function)); }); it('starts apm with the config returned from `getConfig`', () => { @@ -60,7 +61,7 @@ describe('initApm', () => { initApm(['foo', 'bar'], 'rootDir', true, 'service-name'); - expect(apmStartSpy).toHaveBeenCalledTimes(1); - expect(apmStartSpy).toHaveBeenCalledWith(config); + expect(apmStartMock).toHaveBeenCalledTimes(1); + expect(apmStartMock).toHaveBeenCalledWith(config); }); }); diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index c42c33483703e..1d1d95d639861 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -76,6 +76,7 @@ TYPES_DEPS = [ "//packages/kbn-i18n-react:npm_module_types", "//packages/kbn-utils", "@npm//@elastic/elasticsearch", + "@npm//elastic-apm-node", "@npm//del", "@npm//form-data", "@npm//jest", diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index db64f070b37d9..e2607100babc5 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -28,6 +28,7 @@ module.exports = { moduleNameMapper: { '@elastic/eui/lib/(.*)?': '/node_modules/@elastic/eui/test-env/$1', '@elastic/eui$': '/node_modules/@elastic/eui/test-env', + 'elastic-apm-node': '/node_modules/@kbn/test/target_node/jest/mocks/apm_agent_mock.js', '\\.module.(css|scss)$': '/node_modules/@kbn/test/target_node/jest/mocks/css_module_mock.js', '\\.(css|less|scss)$': '/node_modules/@kbn/test/target_node/jest/mocks/style_mock.js', diff --git a/packages/kbn-test/src/jest/mocks/apm_agent_mock.ts b/packages/kbn-test/src/jest/mocks/apm_agent_mock.ts new file mode 100644 index 0000000000000..1615f710504ad --- /dev/null +++ b/packages/kbn-test/src/jest/mocks/apm_agent_mock.ts @@ -0,0 +1,63 @@ +/* + * 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 type { Agent } from 'elastic-apm-node'; + +/** + * `elastic-apm-node` patches the runtime at import time + * causing memory leak with jest module sandbox, so it + * needs to be mocked for tests + */ +const agent: jest.Mocked = { + start: jest.fn().mockImplementation(() => agent), + isStarted: jest.fn().mockReturnValue(false), + getServiceName: jest.fn().mockReturnValue('mock-service'), + setFramework: jest.fn(), + addPatch: jest.fn(), + removePatch: jest.fn(), + clearPatches: jest.fn(), + lambda: jest.fn(), + handleUncaughtExceptions: jest.fn(), + captureError: jest.fn(), + currentTraceparent: null, + currentTraceIds: {}, + startTransaction: jest.fn().mockReturnValue(null), + setTransactionName: jest.fn(), + endTransaction: jest.fn(), + currentTransaction: null, + startSpan: jest.fn(), + currentSpan: null, + setLabel: jest.fn().mockReturnValue(false), + addLabels: jest.fn().mockReturnValue(false), + setUserContext: jest.fn(), + setCustomContext: jest.fn(), + addFilter: jest.fn(), + addErrorFilter: jest.fn(), + addSpanFilter: jest.fn(), + addTransactionFilter: jest.fn(), + addMetadataFilter: jest.fn(), + flush: jest.fn(), + destroy: jest.fn(), + registerMetric: jest.fn(), + setTransactionOutcome: jest.fn(), + setSpanOutcome: jest.fn(), + middleware: { + connect: jest.fn().mockReturnValue(jest.fn()), + }, + logger: { + fatal: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + trace: jest.fn(), + }, +}; + +// eslint-disable-next-line import/no-default-export +export default agent;