diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts index 50ac10347e062..f537b22bac1eb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts @@ -4,42 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { loggerMock } from 'src/core/server/logging/logger.mock'; import { getResult } from '../routes/__mocks__/request_responses'; import { rulesNotificationAlertType } from './rules_notification_alert_type'; import { buildSignalsSearchQuery } from './build_signals_query'; -import { AlertInstance } from '../../../../../../../plugins/alerting/server'; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; import { NotificationExecutorOptions } from './types'; jest.mock('./build_signals_query'); describe('rules_notification_alert_type', () => { let payload: NotificationExecutorOptions; let alert: ReturnType; - let alertInstanceMock: Record; - let alertInstanceFactoryMock: () => AlertInstance; - let savedObjectsClient: ReturnType; let logger: ReturnType; - let callClusterMock: jest.Mock; + let alertServices: AlertServicesMock; beforeEach(() => { - alertInstanceMock = { - scheduleActions: jest.fn(), - replaceState: jest.fn(), - }; - alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock); - alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock); - callClusterMock = jest.fn(); - savedObjectsClient = savedObjectsClientMock.create(); + alertServices = alertsMock.createAlertServices(); logger = loggerMock.create(); payload = { alertId: '1111', - services: { - savedObjectsClient, - alertInstanceFactory: alertInstanceFactoryMock, - callCluster: callClusterMock, - }, + services: alertServices, params: { ruleAlertId: '2222' }, state: {}, spaceId: '', @@ -58,7 +43,7 @@ describe('rules_notification_alert_type', () => { describe('executor', () => { it('throws an error if rule alert was not found', async () => { - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', attributes: {}, type: 'type', @@ -72,13 +57,13 @@ describe('rules_notification_alert_type', () => { it('should call buildSignalsSearchQuery with proper params', async () => { const ruleAlert = getResult(); - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], attributes: ruleAlert, }); - callClusterMock.mockResolvedValue({ + alertServices.callCluster.mockResolvedValue({ count: 0, }); @@ -96,36 +81,38 @@ describe('rules_notification_alert_type', () => { it('should not call alertInstanceFactory if signalsCount was 0', async () => { const ruleAlert = getResult(); - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], attributes: ruleAlert, }); - callClusterMock.mockResolvedValue({ + alertServices.callCluster.mockResolvedValue({ count: 0, }); await alert.executor(payload); - expect(alertInstanceFactoryMock).not.toHaveBeenCalled(); + expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled(); }); it('should call scheduleActions if signalsCount was greater than 0', async () => { const ruleAlert = getResult(); - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], attributes: ruleAlert, }); - callClusterMock.mockResolvedValue({ + alertServices.callCluster.mockResolvedValue({ count: 10, }); await alert.executor(payload); - expect(alertInstanceFactoryMock).toHaveBeenCalled(); + expect(alertServices.alertInstanceFactory).toHaveBeenCalled(); + + const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; expect(alertInstanceMock.replaceState).toHaveBeenCalledWith( expect.objectContaining({ signals_count: 10 }) ); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts index 86d1278031695..510667b211d25 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts @@ -5,42 +5,28 @@ */ import { getQueryFilter, getFilter } from './get_filter'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { PartialFilter } from '../types'; -import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('get_filter', () => { - let savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ - attributes: { - query: { query: 'host.name: linux', language: 'kuery' }, - filters: [], - }, - })); - let servicesMock: AlertServices = { - savedObjectsClient, - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - }; + let servicesMock: AlertServicesMock; beforeAll(() => { jest.resetAllMocks(); }); beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock = alertsMock.createAlertServices(); + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: { query: { query: 'host.name: linux', language: 'kuery' }, language: 'kuery', filters: [], }, })); - servicesMock = { - savedObjectsClient, - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - }; }); afterEach(() => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts index 18286dc7754e0..ccd882228d4de 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts @@ -4,22 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { getInputIndex } from './get_input_output_index'; import { defaultIndexPattern } from '../../../../default_index_pattern'; -import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('get_input_output_index', () => { - let savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ - attributes: {}, - })); - let servicesMock: AlertServices = { - savedObjectsClient, - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - }; + let servicesMock: AlertServicesMock; beforeAll(() => { jest.resetAllMocks(); @@ -30,20 +21,21 @@ describe('get_input_output_index', () => { }); beforeEach(() => { - savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock = alertsMock.createAlertServices(); + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: {}, })); - servicesMock = { - savedObjectsClient, - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - }; }); describe('getInputOutputIndex', () => { test('Returns inputIndex if inputIndex is passed in', async () => { - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: {}, })); const inputIndex = await getInputIndex(servicesMock, '8.0.0', ['test-input-index-1']); @@ -51,7 +43,10 @@ describe('get_input_output_index', () => { }); test('Returns a saved object inputIndex if passed in inputIndex is undefined', async () => { - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: { [DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'], }, @@ -61,7 +56,10 @@ describe('get_input_output_index', () => { }); test('Returns a saved object inputIndex if passed in inputIndex is null', async () => { - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: { [DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'], }, @@ -71,7 +69,10 @@ describe('get_input_output_index', () => { }); test('Returns a saved object inputIndex default from constants if inputIndex passed in is null and the key is also null', async () => { - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: { [DEFAULT_INDEX_KEY]: null, }, @@ -81,7 +82,10 @@ describe('get_input_output_index', () => { }); test('Returns a saved object inputIndex default from constants if inputIndex passed in is undefined and the key is also null', async () => { - savedObjectsClient.get = jest.fn().mockImplementation(() => ({ + servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({ + id, + type, + references: [], attributes: { [DEFAULT_INDEX_KEY]: null, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 81600b0b8dd9b..9e2f36fe2653a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -16,20 +16,16 @@ import { } from './__mocks__/es_results'; import { searchAfterAndBulkCreate } from './search_after_bulk_create'; import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; import uuid from 'uuid'; -export const mockService = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), -}; - describe('searchAfterAndBulkCreate', () => { + let mockService: AlertServicesMock; let inputIndexPattern: string[] = []; beforeEach(() => { jest.clearAllMocks(); inputIndexPattern = ['auditbeat-*']; + mockService = alertsMock.createAlertServices(); }); test('if successful with empty search results', async () => { @@ -65,7 +61,7 @@ describe('searchAfterAndBulkCreate', () => { const sampleParams = sampleRuleAlertParams(30); const someGuids = Array.from({ length: 13 }).map(x => uuid.v4()); mockService.callCluster - .mockReturnValueOnce({ + .mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -79,8 +75,8 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3))) - .mockReturnValueOnce({ + .mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3))) + .mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -94,8 +90,8 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6))) - .mockReturnValueOnce({ + .mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6))) + .mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -139,7 +135,7 @@ describe('searchAfterAndBulkCreate', () => { test('if unsuccessful first bulk create', async () => { const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); const sampleParams = sampleRuleAlertParams(10); - mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); + mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult); const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, @@ -169,7 +165,7 @@ describe('searchAfterAndBulkCreate', () => { test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValueOnce({ + mockService.callCluster.mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -212,7 +208,7 @@ describe('searchAfterAndBulkCreate', () => { test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids and 0 total hits', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValueOnce({ + mockService.callCluster.mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -256,7 +252,7 @@ describe('searchAfterAndBulkCreate', () => { const sampleParams = sampleRuleAlertParams(10); const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); mockService.callCluster - .mockReturnValueOnce({ + .mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -270,7 +266,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockReturnValueOnce(sampleDocSearchResultsNoSortId()); + .mockResolvedValueOnce(sampleDocSearchResultsNoSortId()); const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, @@ -301,7 +297,7 @@ describe('searchAfterAndBulkCreate', () => { const sampleParams = sampleRuleAlertParams(10); const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); mockService.callCluster - .mockReturnValueOnce({ + .mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -315,7 +311,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockReturnValueOnce(sampleEmptyDocSearchResults()); + .mockResolvedValueOnce(sampleEmptyDocSearchResults()); const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, @@ -346,7 +342,7 @@ describe('searchAfterAndBulkCreate', () => { const sampleParams = sampleRuleAlertParams(10); const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); mockService.callCluster - .mockReturnValueOnce({ + .mockResolvedValueOnce({ took: 100, errors: false, items: [ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 03fb5832fdf42..31b407da111ea 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -5,11 +5,10 @@ */ import moment from 'moment'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { loggerMock } from 'src/core/server/logging/logger.mock'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { signalRulesAlertType } from './signal_rule_alert_type'; -import { AlertInstance } from '../../../../../../../plugins/alerting/server'; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; import { ruleStatusServiceFactory } from './rule_status_service'; import { getGapBetweenRuns } from './utils'; import { RuleExecutorOptions } from './types'; @@ -28,18 +27,9 @@ jest.mock('../notifications/schedule_notification_actions'); jest.mock('./find_ml_signals'); jest.mock('./bulk_create_ml_signals'); -const getPayload = ( - ruleAlert: RuleAlertType, - alertInstanceFactoryMock: () => AlertInstance, - savedObjectsClient: ReturnType, - callClusterMock: jest.Mock -) => ({ +const getPayload = (ruleAlert: RuleAlertType, services: AlertServicesMock) => ({ alertId: ruleAlert.id, - services: { - savedObjectsClient, - alertInstanceFactory: alertInstanceFactoryMock, - callCluster: callClusterMock, - }, + services, params: { ...ruleAlert.params, actions: [], @@ -78,24 +68,14 @@ describe('rules_notification_alert_type', () => { modulesProvider: jest.fn(), resultsServiceProvider: jest.fn(), }; - let payload: RuleExecutorOptions; + let payload: jest.Mocked; let alert: ReturnType; - let alertInstanceMock: Record; - let alertInstanceFactoryMock: () => AlertInstance; - let savedObjectsClient: ReturnType; let logger: ReturnType; - let callClusterMock: jest.Mock; + let alertServices: AlertServicesMock; let ruleStatusService: Record; beforeEach(() => { - alertInstanceMock = { - scheduleActions: jest.fn(), - replaceState: jest.fn(), - }; - alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock); - alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock); - callClusterMock = jest.fn(); - savedObjectsClient = savedObjectsClientMock.create(); + alertServices = alertsMock.createAlertServices(); logger = loggerMock.create(); ruleStatusService = { success: jest.fn(), @@ -111,20 +91,20 @@ describe('rules_notification_alert_type', () => { searchAfterTimes: [], createdSignalsCount: 10, }); - callClusterMock.mockResolvedValue({ + alertServices.callCluster.mockResolvedValue({ hits: { total: { value: 10 }, }, }); const ruleAlert = getResult(); - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], attributes: ruleAlert, }); - payload = getPayload(ruleAlert, alertInstanceFactoryMock, savedObjectsClient, callClusterMock); + payload = getPayload(ruleAlert, alertServices); alert = signalRulesAlertType({ logger, @@ -164,7 +144,7 @@ describe('rules_notification_alert_type', () => { }, ]; - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], @@ -195,7 +175,7 @@ describe('rules_notification_alert_type', () => { }, ]; - savedObjectsClient.get.mockResolvedValue({ + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], @@ -214,12 +194,7 @@ describe('rules_notification_alert_type', () => { describe('ML rule', () => { it('should throw an error if ML plugin was not available', async () => { const ruleAlert = getMlResult(); - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); alert = signalRulesAlertType({ logger, version, @@ -235,12 +210,7 @@ describe('rules_notification_alert_type', () => { it('should throw an error if machineLearningJobId or anomalyThreshold was not null', async () => { const ruleAlert = getMlResult(); ruleAlert.params.anomalyThreshold = undefined; - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); await alert.executor(payload); expect(logger.error).toHaveBeenCalled(); expect(logger.error.mock.calls[0][0]).toContain( @@ -250,12 +220,7 @@ describe('rules_notification_alert_type', () => { it('should throw an error if Machine learning job summary was null', async () => { const ruleAlert = getMlResult(); - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); jobsSummaryMock.mockResolvedValue([]); await alert.executor(payload); expect(logger.warn).toHaveBeenCalled(); @@ -268,12 +233,7 @@ describe('rules_notification_alert_type', () => { it('should log an error if Machine learning job was not started', async () => { const ruleAlert = getMlResult(); - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); jobsSummaryMock.mockResolvedValue([ { id: 'some_job_id', @@ -297,12 +257,7 @@ describe('rules_notification_alert_type', () => { it('should not call ruleStatusService.success if no anomalies were found', async () => { const ruleAlert = getMlResult(); - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); jobsSummaryMock.mockResolvedValue([]); (findMlSignals as jest.Mock).mockResolvedValue({ hits: { @@ -320,12 +275,7 @@ describe('rules_notification_alert_type', () => { it('should call ruleStatusService.success if signals were created', async () => { const ruleAlert = getMlResult(); - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); + payload = getPayload(ruleAlert, alertServices); jobsSummaryMock.mockResolvedValue([ { id: 'some_job_id', @@ -360,13 +310,8 @@ describe('rules_notification_alert_type', () => { id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', }, ]; - payload = getPayload( - ruleAlert, - alertInstanceFactoryMock, - savedObjectsClient, - callClusterMock - ); - savedObjectsClient.get.mockResolvedValue({ + payload = getPayload(ruleAlert, alertServices); + alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', references: [], diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts index 45365b446cbf0..3401d7417ec62 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts @@ -16,17 +16,13 @@ import { sampleBulkCreateErrorResult, sampleDocWithAncestors, } from './__mocks__/es_results'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; import { singleBulkCreate, filterDuplicateRules } from './single_bulk_create'; - -export const mockService = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), -}; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('singleBulkCreate', () => { + const mockService: AlertServicesMock = alertsMock.createAlertServices(); + beforeEach(() => { jest.clearAllMocks(); }); @@ -135,7 +131,7 @@ describe('singleBulkCreate', () => { test('create successful bulk create', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValueOnce({ + mockService.callCluster.mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -169,7 +165,7 @@ describe('singleBulkCreate', () => { test('create successful bulk create with docs with no versioning', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValueOnce({ + mockService.callCluster.mockResolvedValueOnce({ took: 100, errors: false, items: [ @@ -203,7 +199,7 @@ describe('singleBulkCreate', () => { test('create unsuccessful bulk create due to empty search results', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValue(false); + mockService.callCluster.mockResolvedValue(false); const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleEmptyDocSearchResults(), ruleParams: sampleParams, @@ -230,7 +226,7 @@ describe('singleBulkCreate', () => { test('create successful bulk create when bulk create has duplicate errors', async () => { const sampleParams = sampleRuleAlertParams(); const sampleSearchResult = sampleDocSearchResultsNoSortId; - mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); + mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult); const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleSearchResult(), ruleParams: sampleParams, @@ -259,7 +255,7 @@ describe('singleBulkCreate', () => { test('create successful bulk create when bulk create has multiple error statuses', async () => { const sampleParams = sampleRuleAlertParams(); const sampleSearchResult = sampleDocSearchResultsNoSortId; - mockService.callCluster.mockReturnValue(sampleBulkCreateErrorResult); + mockService.callCluster.mockResolvedValue(sampleBulkCreateErrorResult); const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleSearchResult(), ruleParams: sampleParams, @@ -354,7 +350,7 @@ describe('singleBulkCreate', () => { test('create successful and returns proper createdItemsCount', async () => { const sampleParams = sampleRuleAlertParams(); - mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); + mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult); const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleDocSearchResultsNoSortId(), ruleParams: sampleParams, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts index 9b726c38d3d96..dbeab70595e4f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts @@ -4,28 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { sampleDocSearchResultsNoSortId, mockLogger, sampleDocSearchResultsWithSortId, } from './__mocks__/es_results'; import { singleSearchAfter } from './single_search_after'; - -export const mockService = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), -}; +import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks'; describe('singleSearchAfter', () => { + const mockService: AlertServicesMock = alertsMock.createAlertServices(); + beforeEach(() => { jest.clearAllMocks(); }); test('if singleSearchAfter works without a given sort id', async () => { let searchAfterSortId; - mockService.callCluster.mockReturnValue(sampleDocSearchResultsNoSortId); + mockService.callCluster.mockResolvedValue(sampleDocSearchResultsNoSortId); await expect( singleSearchAfter({ searchAfterSortId, @@ -41,7 +37,7 @@ describe('singleSearchAfter', () => { }); test('if singleSearchAfter works with a given sort id', async () => { const searchAfterSortId = '1234567891111'; - mockService.callCluster.mockReturnValue(sampleDocSearchResultsWithSortId); + mockService.callCluster.mockResolvedValue(sampleDocSearchResultsWithSortId); const { searchResult } = await singleSearchAfter({ searchAfterSortId, index: [], diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts index 55ad722dcf881..a9e224142a632 100644 --- a/x-pack/plugins/alerting/server/mocks.ts +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -6,6 +6,8 @@ import { alertsClientMock } from './alerts_client.mock'; import { PluginSetupContract, PluginStartContract } from './plugin'; +import { savedObjectsClientMock } from '../../../../src/core/server/mocks'; +import { AlertInstance } from './alert_instance'; export { alertsClientMock }; @@ -24,7 +26,44 @@ const createStartMock = () => { return mock; }; +export type AlertInstanceMock = jest.Mocked; +const createAlertInstanceFactoryMock = () => { + const mock = { + hasScheduledActions: jest.fn(), + isThrottled: jest.fn(), + getScheduledActionOptions: jest.fn(), + unscheduleActions: jest.fn(), + getState: jest.fn(), + scheduleActions: jest.fn(), + replaceState: jest.fn(), + updateLastScheduledActions: jest.fn(), + toJSON: jest.fn(), + toRaw: jest.fn(), + }; + + // support chaining + mock.replaceState.mockReturnValue(mock); + mock.unscheduleActions.mockReturnValue(mock); + mock.scheduleActions.mockReturnValue(mock); + + return (mock as unknown) as AlertInstanceMock; +}; + +const createAlertServicesMock = () => { + const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); + return { + alertInstanceFactory: jest + .fn, [string]>() + .mockReturnValue(alertInstanceFactoryMock), + callCluster: jest.fn(), + savedObjectsClient: savedObjectsClientMock.create(), + }; +}; +export type AlertServicesMock = ReturnType; + export const alertsMock = { + createAlertInstanceFactory: createAlertInstanceFactoryMock, createSetup: createSetupMock, createStart: createStartMock, + createAlertServices: createAlertServicesMock, }; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 38cd0cec145f9..000d0823311b3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -8,62 +8,77 @@ import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; import { AlertExecutorOptions } from '../../../../../alerting/server'; +import { + alertsMock, + AlertServicesMock, + AlertInstanceMock, +} from '../../../../../alerting/server/mocks'; const executor = createMetricThresholdExecutor('test') as (opts: { params: AlertExecutorOptions['params']; services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; }) => Promise; -const alertInstances = new Map(); -const services = { - callCluster(_: string, { body, index }: any) { - if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; - const metric = body.query.bool.filter[1]?.exists.field; - if (body.aggs.groupings) { - if (body.aggs.groupings.composite.after) { - return mocks.compositeEndResponse; - } - if (metric === 'test.metric.2') { - return mocks.alternateCompositeResponse; - } - return mocks.basicCompositeResponse; +const services: AlertServicesMock = alertsMock.createAlertServices(); +services.callCluster.mockImplementation((_: string, { body, index }: any) => { + if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; + const metric = body.query.bool.filter[1]?.exists.field; + if (body.aggs.groupings) { + if (body.aggs.groupings.composite.after) { + return mocks.compositeEndResponse; } if (metric === 'test.metric.2') { - return mocks.alternateMetricResponse; + return mocks.alternateCompositeResponse; } - return mocks.basicMetricResponse; - }, - alertInstanceFactory(instanceID: string) { - let state: any; - const actionQueue: any[] = []; - const instance = { - actionQueue: [], - get state() { - return state; - }, - get mostRecentAction() { - return actionQueue.pop(); - }, - }; - alertInstances.set(instanceID, instance); + return mocks.basicCompositeResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateMetricResponse; + } + return mocks.basicMetricResponse; +}); +services.savedObjectsClient.get.mockImplementation(async (type: string, sourceId: string) => { + if (sourceId === 'alternate') return { - instanceID, - scheduleActions(id: string, action: any) { - actionQueue.push({ id, action }); - }, - replaceState(newState: any) { - state = newState; - }, + id: 'alternate', + attributes: { metricAlias: 'alternatebeat-*' }, + type, + references: [], }; - }, - savedObjectsClient: { - get(_: string, sourceId: string) { - if (sourceId === 'alternate') - return { id: 'alternate', attributes: { metricAlias: 'alternatebeat-*' } }; - return { id: 'default', attributes: { metricAlias: 'metricbeat-*' } }; - }, - }, -}; + return { id: 'default', attributes: { metricAlias: 'metricbeat-*' }, type, references: [] }; +}); + +interface AlertTestInstance { + instance: AlertInstanceMock; + actionQueue: any[]; + state: any; +} +const alertInstances = new Map(); +services.alertInstanceFactory.mockImplementation((instanceID: string) => { + const alertInstance: AlertTestInstance = { + instance: alertsMock.createAlertInstanceFactory(), + actionQueue: [], + state: {}, + }; + alertInstances.set(instanceID, alertInstance); + alertInstance.instance.replaceState.mockImplementation((newState: any) => { + alertInstance.state = newState; + return alertInstance.instance; + }); + alertInstance.instance.scheduleActions.mockImplementation((id: string, action: any) => { + alertInstance.actionQueue.push({ id, action }); + return alertInstance.instance; + }); + return alertInstance.instance; +}); + +function mostRecentAction(id: string) { + return alertInstances.get(id)!.actionQueue.pop(); +} + +function getState(id: string) { + return alertInstances.get(id)!.state; +} const baseCriterion = { aggType: 'avg', @@ -90,65 +105,65 @@ describe('The metric threshold alert type', () => { }); test('alerts as expected with the > comparator', async () => { await execute(Comparator.GT, [0.75]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.GT, [1.5]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('alerts as expected with the < comparator', async () => { await execute(Comparator.LT, [1.5]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.LT, [0.75]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('alerts as expected with the >= comparator', async () => { await execute(Comparator.GT_OR_EQ, [0.75]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.GT_OR_EQ, [1.0]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.GT_OR_EQ, [1.5]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('alerts as expected with the <= comparator', async () => { await execute(Comparator.LT_OR_EQ, [1.5]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.LT_OR_EQ, [1.0]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.LT_OR_EQ, [0.75]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('alerts as expected with the between comparator', async () => { await execute(Comparator.BETWEEN, [0, 1.5]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.BETWEEN, [0, 0.75]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('reports expected values to the action context', async () => { await execute(Comparator.GT, [0.75]); - const mostRecentAction = alertInstances.get(instanceID).mostRecentAction; - expect(mostRecentAction.action.group).toBe('*'); - expect(mostRecentAction.action.valueOf.condition0).toBe(1); - expect(mostRecentAction.action.thresholdOf.condition0).toStrictEqual([0.75]); - expect(mostRecentAction.action.metricOf.condition0).toBe('test.metric.1'); + const { action } = mostRecentAction(instanceID); + expect(action.group).toBe('*'); + expect(action.valueOf.condition0).toBe(1); + expect(action.thresholdOf.condition0).toStrictEqual([0.75]); + expect(action.metricOf.condition0).toBe('test.metric.1'); }); test('fetches the index pattern dynamically', async () => { await execute(Comparator.LT, [17], 'alternate'); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.LT, [1.5], 'alternate'); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); }); @@ -171,29 +186,29 @@ describe('The metric threshold alert type', () => { const instanceIdB = 'test-b'; test('sends an alert when all groups pass the threshold', async () => { await execute(Comparator.GT, [0.75]); - expect(alertInstances.get(instanceIdA).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceIdA).state.alertState).toBe(AlertStates.ALERT); - expect(alertInstances.get(instanceIdB).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceIdA).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceIdA).alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceIdB).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceIdB).alertState).toBe(AlertStates.ALERT); }); test('sends an alert when only some groups pass the threshold', async () => { await execute(Comparator.LT, [1.5]); - expect(alertInstances.get(instanceIdA).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceIdA).state.alertState).toBe(AlertStates.ALERT); - expect(alertInstances.get(instanceIdB).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceIdA).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceIdA).alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceIdB)).toBe(undefined); + expect(getState(instanceIdB).alertState).toBe(AlertStates.OK); }); test('sends no alert when no groups pass the threshold', async () => { await execute(Comparator.GT, [5]); - expect(alertInstances.get(instanceIdA).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceIdA).state.alertState).toBe(AlertStates.OK); - expect(alertInstances.get(instanceIdB).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceIdA)).toBe(undefined); + expect(getState(instanceIdA).alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceIdB)).toBe(undefined); + expect(getState(instanceIdB).alertState).toBe(AlertStates.OK); }); test('reports group values to the action context', async () => { await execute(Comparator.GT, [0.75]); - expect(alertInstances.get(instanceIdA).mostRecentAction.action.group).toBe('a'); - expect(alertInstances.get(instanceIdB).mostRecentAction.action.group).toBe('b'); + expect(mostRecentAction(instanceIdA).action.group).toBe('a'); + expect(mostRecentAction(instanceIdB).action.group).toBe('b'); }); }); @@ -226,34 +241,34 @@ describe('The metric threshold alert type', () => { test('sends an alert when all criteria cross the threshold', async () => { const instanceID = 'test-*'; await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); }); test('sends no alert when some, but not all, criteria cross the threshold', async () => { const instanceID = 'test-*'; await execute(Comparator.LT_OR_EQ, [1.0], [3.0]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); test('alerts only on groups that meet all criteria when querying with a groupBy parameter', async () => { const instanceIdA = 'test-a'; const instanceIdB = 'test-b'; await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'something'); - expect(alertInstances.get(instanceIdA).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceIdA).state.alertState).toBe(AlertStates.ALERT); - expect(alertInstances.get(instanceIdB).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceIdA).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceIdA).alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceIdB)).toBe(undefined); + expect(getState(instanceIdB).alertState).toBe(AlertStates.OK); }); test('sends all criteria to the action context', async () => { const instanceID = 'test-*'; await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); - const mostRecentAction = alertInstances.get(instanceID).mostRecentAction; - expect(mostRecentAction.action.valueOf.condition0).toBe(1); - expect(mostRecentAction.action.valueOf.condition1).toBe(3.5); - expect(mostRecentAction.action.thresholdOf.condition0).toStrictEqual([1.0]); - expect(mostRecentAction.action.thresholdOf.condition1).toStrictEqual([3.0]); - expect(mostRecentAction.action.metricOf.condition0).toBe('test.metric.1'); - expect(mostRecentAction.action.metricOf.condition1).toBe('test.metric.2'); + const { action } = mostRecentAction(instanceID); + expect(action.valueOf.condition0).toBe(1); + expect(action.valueOf.condition1).toBe(3.5); + expect(action.thresholdOf.condition0).toStrictEqual([1.0]); + expect(action.thresholdOf.condition1).toStrictEqual([3.0]); + expect(action.metricOf.condition0).toBe('test.metric.1'); + expect(action.metricOf.condition1).toBe('test.metric.2'); }); }); describe('querying with the count aggregator', () => { @@ -275,11 +290,11 @@ describe('The metric threshold alert type', () => { }); test('alerts based on the doc_count value instead of the aggregatedValue', async () => { await execute(Comparator.GT, [2]); - expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); await execute(Comparator.LT, [1.5]); - expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); - expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts b/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts index 6a9ca88437347..bcc1a8abe5cb0 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_state.test.ts @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from 'src/core/server'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { getClusterState } from './cluster_state'; -import { AlertServices } from '../../../alerting/server'; import { ALERT_TYPE_CLUSTER_STATE } from '../../common/constants'; import { AlertCommonParams, AlertCommonState, AlertClusterStatePerClusterState } from './types'; import { getPreparedAlert } from '../lib/alerts/get_prepared_alert'; import { executeActions } from '../lib/alerts/cluster_state.lib'; import { AlertClusterStateState } from './enums'; +import { alertsMock, AlertServicesMock } from '../../../alerting/server/mocks'; jest.mock('../lib/alerts/cluster_state.lib', () => ({ executeActions: jest.fn(), @@ -26,18 +25,8 @@ jest.mock('../lib/alerts/get_prepared_alert', () => ({ }), })); -interface MockServices { - callCluster: jest.Mock; - alertInstanceFactory: jest.Mock; - savedObjectsClient: jest.Mock; -} - describe('getClusterState', () => { - const services: MockServices | AlertServices = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), - }; + const services: AlertServicesMock = alertsMock.createAlertServices(); const params: AlertCommonParams = { dateFormat: 'YYYY', @@ -107,7 +96,7 @@ describe('getClusterState', () => { it('should alert if green -> yellow', async () => { const result = await setupAlert(AlertClusterStateState.Green, AlertClusterStateState.Yellow); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_CLUSTER_STATE), cluster, AlertClusterStateState.Yellow, emailAddress @@ -121,7 +110,7 @@ describe('getClusterState', () => { it('should alert if yellow -> green', async () => { const result = await setupAlert(AlertClusterStateState.Yellow, AlertClusterStateState.Green); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_CLUSTER_STATE), cluster, AlertClusterStateState.Green, emailAddress, @@ -135,7 +124,7 @@ describe('getClusterState', () => { it('should alert if green -> red', async () => { const result = await setupAlert(AlertClusterStateState.Green, AlertClusterStateState.Red); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_CLUSTER_STATE), cluster, AlertClusterStateState.Red, emailAddress @@ -149,7 +138,7 @@ describe('getClusterState', () => { it('should alert if red -> green', async () => { const result = await setupAlert(AlertClusterStateState.Red, AlertClusterStateState.Green); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_CLUSTER_STATE), cluster, AlertClusterStateState.Green, emailAddress, diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts index 92047e300bc1f..f9d2ec3e1d48e 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts @@ -8,8 +8,6 @@ import moment from 'moment-timezone'; import { getLicenseExpiration } from './license_expiration'; import { ALERT_TYPE_LICENSE_EXPIRATION } from '../../common/constants'; import { Logger } from 'src/core/server'; -import { AlertServices } from '../../../alerting/server'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; import { AlertCommonParams, AlertCommonState, @@ -18,6 +16,7 @@ import { } from './types'; import { executeActions } from '../lib/alerts/license_expiration.lib'; import { PreparedAlert, getPreparedAlert } from '../lib/alerts/get_prepared_alert'; +import { alertsMock, AlertServicesMock } from '../../../alerting/server/mocks'; jest.mock('../lib/alerts/license_expiration.lib', () => ({ executeActions: jest.fn(), @@ -32,18 +31,8 @@ jest.mock('../lib/alerts/get_prepared_alert', () => ({ }), })); -interface MockServices { - callCluster: jest.Mock; - alertInstanceFactory: jest.Mock; - savedObjectsClient: jest.Mock; -} - describe('getLicenseExpiration', () => { - const services: MockServices | AlertServices = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), - }; + const services: AlertServicesMock = alertsMock.createAlertServices(); const params: AlertCommonParams = { dateFormat: 'YYYY', @@ -106,6 +95,7 @@ describe('getLicenseExpiration', () => { } afterEach(() => { + jest.clearAllMocks(); (executeActions as jest.Mock).mockClear(); (getPreparedAlert as jest.Mock).mockClear(); }); @@ -135,7 +125,7 @@ describe('getLicenseExpiration', () => { const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS > 0).toBe(true); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_LICENSE_EXPIRATION), cluster, moment.utc(expiryDateMS), dateFormat, @@ -157,7 +147,7 @@ describe('getLicenseExpiration', () => { const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS).toBe(0); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_LICENSE_EXPIRATION), cluster, moment.utc(expiryDateMS), dateFormat, @@ -196,7 +186,7 @@ describe('getLicenseExpiration', () => { const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS > 0).toBe(true); expect(executeActions).toHaveBeenCalledWith( - undefined, + services.alertInstanceFactory(ALERT_TYPE_LICENSE_EXPIRATION), cluster, moment.utc(expiryDateMS), dateFormat, diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 08a3bc75fa8bd..0d90026e59fb1 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -17,6 +17,7 @@ import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCoreSetup } from '../../adapters'; import { defaultDynamicSettings } from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; /** * The alert takes some dependencies as parameters; these are things like @@ -44,16 +45,21 @@ const bootstrapDependencies = (customRequests?: any) => { */ const mockOptions = ( params = { numTimes: 5, locations: [], timerange: { from: 'now-15m', to: 'now' } }, - services = { callCluster: 'mockESFunction', savedObjectsClient: mockSavedObjectsClient }, + services = alertsMock.createAlertServices(), state = {} -): any => ({ - params, - services, - state, -}); - -const mockSavedObjectsClient = { get: jest.fn() }; -mockSavedObjectsClient.get.mockReturnValue(defaultDynamicSettings); +): any => { + services.savedObjectsClient.get.mockResolvedValue({ + id: '', + type: '', + references: [], + attributes: defaultDynamicSettings, + }); + return { + params, + services, + state, + }; +}; describe('status check alert', () => { let toISOStringSpy: jest.SpyInstance; @@ -80,8 +86,10 @@ describe('status check alert', () => { expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": "mockESFunction", - "dynamicSettings": undefined, + "callES": [MockFunction], + "dynamicSettings": Object { + "heartbeatIndices": "heartbeat-7*", + }, "locations": Array [], "numTimes": 5, "timerange": Object { @@ -112,27 +120,19 @@ describe('status check alert', () => { ]); const { server, libs } = bootstrapDependencies({ getMonitorStatus: mockGetter }); const alert = statusCheckAlertFactory(server, libs); - const mockInstanceFactory = jest.fn(); - const mockReplaceState = jest.fn(); - const mockScheduleActions = jest.fn(); - mockInstanceFactory.mockReturnValue({ - replaceState: mockReplaceState, - scheduleActions: mockScheduleActions, - }); const options = mockOptions(); - options.services = { - ...options.services, - alertInstanceFactory: mockInstanceFactory, - }; + const alertServices: AlertServicesMock = options.services; // @ts-ignore the executor can return `void`, but ours never does const state: Record = await alert.executor(options); expect(mockGetter).toHaveBeenCalledTimes(1); - expect(mockInstanceFactory).toHaveBeenCalledTimes(1); + expect(alertServices.alertInstanceFactory).toHaveBeenCalledTimes(1); expect(mockGetter.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { - "callES": "mockESFunction", - "dynamicSettings": undefined, + "callES": [MockFunction], + "dynamicSettings": Object { + "heartbeatIndices": "heartbeat-7*", + }, "locations": Array [], "numTimes": 5, "timerange": Object { @@ -142,8 +142,9 @@ describe('status check alert', () => { }, ] `); - expect(mockReplaceState).toHaveBeenCalledTimes(1); - expect(mockReplaceState.mock.calls[0]).toMatchInlineSnapshot(` + const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; + expect(alertInstanceMock.replaceState).toHaveBeenCalledTimes(1); + expect(alertInstanceMock.replaceState.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "currentTriggerStarted": "foo date string", @@ -170,8 +171,8 @@ describe('status check alert', () => { }, ] `); - expect(mockScheduleActions).toHaveBeenCalledTimes(1); - expect(mockScheduleActions.mock.calls[0]).toMatchInlineSnapshot(` + expect(alertInstanceMock.scheduleActions).toHaveBeenCalledTimes(1); + expect(alertInstanceMock.scheduleActions.mock.calls[0]).toMatchInlineSnapshot(` Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object {