Skip to content

Commit bb9f884

Browse files
authored
[alerting] Adds an alertServices mock and uses it in siem, monitoring and uptime (#63489)
Work on #61313 has revealed that we don't have amock for AlertServices, which creates coupling between us and any solution depending on us, which makes it harder to make changes in our own code. This PR adds mocks and uses them in SIEM, Monitoring and Uptime, so that we can make future changes without having to change outside solutions.
1 parent 3ade2d3 commit bb9f884

File tree

12 files changed

+305
-361
lines changed

12 files changed

+305
-361
lines changed

x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,27 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { savedObjectsClientMock } from 'src/core/server/mocks';
87
import { loggerMock } from 'src/core/server/logging/logger.mock';
98
import { getResult } from '../routes/__mocks__/request_responses';
109
import { rulesNotificationAlertType } from './rules_notification_alert_type';
1110
import { buildSignalsSearchQuery } from './build_signals_query';
12-
import { AlertInstance } from '../../../../../../../plugins/alerting/server';
11+
import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
1312
import { NotificationExecutorOptions } from './types';
1413
jest.mock('./build_signals_query');
1514

1615
describe('rules_notification_alert_type', () => {
1716
let payload: NotificationExecutorOptions;
1817
let alert: ReturnType<typeof rulesNotificationAlertType>;
19-
let alertInstanceMock: Record<string, jest.Mock>;
20-
let alertInstanceFactoryMock: () => AlertInstance;
21-
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
2218
let logger: ReturnType<typeof loggerMock.create>;
23-
let callClusterMock: jest.Mock;
19+
let alertServices: AlertServicesMock;
2420

2521
beforeEach(() => {
26-
alertInstanceMock = {
27-
scheduleActions: jest.fn(),
28-
replaceState: jest.fn(),
29-
};
30-
alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock);
31-
alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock);
32-
callClusterMock = jest.fn();
33-
savedObjectsClient = savedObjectsClientMock.create();
22+
alertServices = alertsMock.createAlertServices();
3423
logger = loggerMock.create();
3524

3625
payload = {
3726
alertId: '1111',
38-
services: {
39-
savedObjectsClient,
40-
alertInstanceFactory: alertInstanceFactoryMock,
41-
callCluster: callClusterMock,
42-
},
27+
services: alertServices,
4328
params: { ruleAlertId: '2222' },
4429
state: {},
4530
spaceId: '',
@@ -58,7 +43,7 @@ describe('rules_notification_alert_type', () => {
5843

5944
describe('executor', () => {
6045
it('throws an error if rule alert was not found', async () => {
61-
savedObjectsClient.get.mockResolvedValue({
46+
alertServices.savedObjectsClient.get.mockResolvedValue({
6247
id: 'id',
6348
attributes: {},
6449
type: 'type',
@@ -72,13 +57,13 @@ describe('rules_notification_alert_type', () => {
7257

7358
it('should call buildSignalsSearchQuery with proper params', async () => {
7459
const ruleAlert = getResult();
75-
savedObjectsClient.get.mockResolvedValue({
60+
alertServices.savedObjectsClient.get.mockResolvedValue({
7661
id: 'id',
7762
type: 'type',
7863
references: [],
7964
attributes: ruleAlert,
8065
});
81-
callClusterMock.mockResolvedValue({
66+
alertServices.callCluster.mockResolvedValue({
8267
count: 0,
8368
});
8469

@@ -96,36 +81,38 @@ describe('rules_notification_alert_type', () => {
9681

9782
it('should not call alertInstanceFactory if signalsCount was 0', async () => {
9883
const ruleAlert = getResult();
99-
savedObjectsClient.get.mockResolvedValue({
84+
alertServices.savedObjectsClient.get.mockResolvedValue({
10085
id: 'id',
10186
type: 'type',
10287
references: [],
10388
attributes: ruleAlert,
10489
});
105-
callClusterMock.mockResolvedValue({
90+
alertServices.callCluster.mockResolvedValue({
10691
count: 0,
10792
});
10893

10994
await alert.executor(payload);
11095

111-
expect(alertInstanceFactoryMock).not.toHaveBeenCalled();
96+
expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled();
11297
});
11398

11499
it('should call scheduleActions if signalsCount was greater than 0', async () => {
115100
const ruleAlert = getResult();
116-
savedObjectsClient.get.mockResolvedValue({
101+
alertServices.savedObjectsClient.get.mockResolvedValue({
117102
id: 'id',
118103
type: 'type',
119104
references: [],
120105
attributes: ruleAlert,
121106
});
122-
callClusterMock.mockResolvedValue({
107+
alertServices.callCluster.mockResolvedValue({
123108
count: 10,
124109
});
125110

126111
await alert.executor(payload);
127112

128-
expect(alertInstanceFactoryMock).toHaveBeenCalled();
113+
expect(alertServices.alertInstanceFactory).toHaveBeenCalled();
114+
115+
const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results;
129116
expect(alertInstanceMock.replaceState).toHaveBeenCalledWith(
130117
expect.objectContaining({ signals_count: 10 })
131118
);

x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,28 @@
55
*/
66

77
import { getQueryFilter, getFilter } from './get_filter';
8-
import { savedObjectsClientMock } from 'src/core/server/mocks';
98
import { PartialFilter } from '../types';
10-
import { AlertServices } from '../../../../../../../plugins/alerting/server';
9+
import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
1110

1211
describe('get_filter', () => {
13-
let savedObjectsClient = savedObjectsClientMock.create();
14-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
15-
attributes: {
16-
query: { query: 'host.name: linux', language: 'kuery' },
17-
filters: [],
18-
},
19-
}));
20-
let servicesMock: AlertServices = {
21-
savedObjectsClient,
22-
callCluster: jest.fn(),
23-
alertInstanceFactory: jest.fn(),
24-
};
12+
let servicesMock: AlertServicesMock;
2513

2614
beforeAll(() => {
2715
jest.resetAllMocks();
2816
});
2917

3018
beforeEach(() => {
31-
savedObjectsClient = savedObjectsClientMock.create();
32-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
19+
servicesMock = alertsMock.createAlertServices();
20+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
21+
id,
22+
type,
23+
references: [],
3324
attributes: {
3425
query: { query: 'host.name: linux', language: 'kuery' },
3526
language: 'kuery',
3627
filters: [],
3728
},
3829
}));
39-
servicesMock = {
40-
savedObjectsClient,
41-
callCluster: jest.fn(),
42-
alertInstanceFactory: jest.fn(),
43-
};
4430
});
4531

4632
afterEach(() => {

x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,13 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { savedObjectsClientMock } from 'src/core/server/mocks';
87
import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
98
import { getInputIndex } from './get_input_output_index';
109
import { defaultIndexPattern } from '../../../../default_index_pattern';
11-
import { AlertServices } from '../../../../../../../plugins/alerting/server';
10+
import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
1211

1312
describe('get_input_output_index', () => {
14-
let savedObjectsClient = savedObjectsClientMock.create();
15-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
16-
attributes: {},
17-
}));
18-
let servicesMock: AlertServices = {
19-
savedObjectsClient,
20-
callCluster: jest.fn(),
21-
alertInstanceFactory: jest.fn(),
22-
};
13+
let servicesMock: AlertServicesMock;
2314

2415
beforeAll(() => {
2516
jest.resetAllMocks();
@@ -30,28 +21,32 @@ describe('get_input_output_index', () => {
3021
});
3122

3223
beforeEach(() => {
33-
savedObjectsClient = savedObjectsClientMock.create();
34-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
24+
servicesMock = alertsMock.createAlertServices();
25+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
26+
id,
27+
type,
28+
references: [],
3529
attributes: {},
3630
}));
37-
servicesMock = {
38-
savedObjectsClient,
39-
callCluster: jest.fn(),
40-
alertInstanceFactory: jest.fn(),
41-
};
4231
});
4332

4433
describe('getInputOutputIndex', () => {
4534
test('Returns inputIndex if inputIndex is passed in', async () => {
46-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
35+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
36+
id,
37+
type,
38+
references: [],
4739
attributes: {},
4840
}));
4941
const inputIndex = await getInputIndex(servicesMock, '8.0.0', ['test-input-index-1']);
5042
expect(inputIndex).toEqual(['test-input-index-1']);
5143
});
5244

5345
test('Returns a saved object inputIndex if passed in inputIndex is undefined', async () => {
54-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
46+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
47+
id,
48+
type,
49+
references: [],
5550
attributes: {
5651
[DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'],
5752
},
@@ -61,7 +56,10 @@ describe('get_input_output_index', () => {
6156
});
6257

6358
test('Returns a saved object inputIndex if passed in inputIndex is null', async () => {
64-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
59+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
60+
id,
61+
type,
62+
references: [],
6563
attributes: {
6664
[DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'],
6765
},
@@ -71,7 +69,10 @@ describe('get_input_output_index', () => {
7169
});
7270

7371
test('Returns a saved object inputIndex default from constants if inputIndex passed in is null and the key is also null', async () => {
74-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
72+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
73+
id,
74+
type,
75+
references: [],
7576
attributes: {
7677
[DEFAULT_INDEX_KEY]: null,
7778
},
@@ -81,7 +82,10 @@ describe('get_input_output_index', () => {
8182
});
8283

8384
test('Returns a saved object inputIndex default from constants if inputIndex passed in is undefined and the key is also null', async () => {
84-
savedObjectsClient.get = jest.fn().mockImplementation(() => ({
85+
servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
86+
id,
87+
type,
88+
references: [],
8589
attributes: {
8690
[DEFAULT_INDEX_KEY]: null,
8791
},

x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@ import {
1616
} from './__mocks__/es_results';
1717
import { searchAfterAndBulkCreate } from './search_after_bulk_create';
1818
import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants';
19-
import { savedObjectsClientMock } from 'src/core/server/mocks';
19+
import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
2020
import uuid from 'uuid';
2121

22-
export const mockService = {
23-
callCluster: jest.fn(),
24-
alertInstanceFactory: jest.fn(),
25-
savedObjectsClient: savedObjectsClientMock.create(),
26-
};
27-
2822
describe('searchAfterAndBulkCreate', () => {
23+
let mockService: AlertServicesMock;
2924
let inputIndexPattern: string[] = [];
3025
beforeEach(() => {
3126
jest.clearAllMocks();
3227
inputIndexPattern = ['auditbeat-*'];
28+
mockService = alertsMock.createAlertServices();
3329
});
3430

3531
test('if successful with empty search results', async () => {
@@ -65,7 +61,7 @@ describe('searchAfterAndBulkCreate', () => {
6561
const sampleParams = sampleRuleAlertParams(30);
6662
const someGuids = Array.from({ length: 13 }).map(x => uuid.v4());
6763
mockService.callCluster
68-
.mockReturnValueOnce({
64+
.mockResolvedValueOnce({
6965
took: 100,
7066
errors: false,
7167
items: [
@@ -79,8 +75,8 @@ describe('searchAfterAndBulkCreate', () => {
7975
},
8076
],
8177
})
82-
.mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3)))
83-
.mockReturnValueOnce({
78+
.mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3)))
79+
.mockResolvedValueOnce({
8480
took: 100,
8581
errors: false,
8682
items: [
@@ -94,8 +90,8 @@ describe('searchAfterAndBulkCreate', () => {
9490
},
9591
],
9692
})
97-
.mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6)))
98-
.mockReturnValueOnce({
93+
.mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6)))
94+
.mockResolvedValueOnce({
9995
took: 100,
10096
errors: false,
10197
items: [
@@ -139,7 +135,7 @@ describe('searchAfterAndBulkCreate', () => {
139135
test('if unsuccessful first bulk create', async () => {
140136
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
141137
const sampleParams = sampleRuleAlertParams(10);
142-
mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult);
138+
mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult);
143139
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
144140
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
145141
ruleParams: sampleParams,
@@ -169,7 +165,7 @@ describe('searchAfterAndBulkCreate', () => {
169165

170166
test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids', async () => {
171167
const sampleParams = sampleRuleAlertParams();
172-
mockService.callCluster.mockReturnValueOnce({
168+
mockService.callCluster.mockResolvedValueOnce({
173169
took: 100,
174170
errors: false,
175171
items: [
@@ -212,7 +208,7 @@ describe('searchAfterAndBulkCreate', () => {
212208

213209
test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids and 0 total hits', async () => {
214210
const sampleParams = sampleRuleAlertParams();
215-
mockService.callCluster.mockReturnValueOnce({
211+
mockService.callCluster.mockResolvedValueOnce({
216212
took: 100,
217213
errors: false,
218214
items: [
@@ -256,7 +252,7 @@ describe('searchAfterAndBulkCreate', () => {
256252
const sampleParams = sampleRuleAlertParams(10);
257253
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
258254
mockService.callCluster
259-
.mockReturnValueOnce({
255+
.mockResolvedValueOnce({
260256
took: 100,
261257
errors: false,
262258
items: [
@@ -270,7 +266,7 @@ describe('searchAfterAndBulkCreate', () => {
270266
},
271267
],
272268
})
273-
.mockReturnValueOnce(sampleDocSearchResultsNoSortId());
269+
.mockResolvedValueOnce(sampleDocSearchResultsNoSortId());
274270
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
275271
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
276272
ruleParams: sampleParams,
@@ -301,7 +297,7 @@ describe('searchAfterAndBulkCreate', () => {
301297
const sampleParams = sampleRuleAlertParams(10);
302298
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
303299
mockService.callCluster
304-
.mockReturnValueOnce({
300+
.mockResolvedValueOnce({
305301
took: 100,
306302
errors: false,
307303
items: [
@@ -315,7 +311,7 @@ describe('searchAfterAndBulkCreate', () => {
315311
},
316312
],
317313
})
318-
.mockReturnValueOnce(sampleEmptyDocSearchResults());
314+
.mockResolvedValueOnce(sampleEmptyDocSearchResults());
319315
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
320316
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
321317
ruleParams: sampleParams,
@@ -346,7 +342,7 @@ describe('searchAfterAndBulkCreate', () => {
346342
const sampleParams = sampleRuleAlertParams(10);
347343
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
348344
mockService.callCluster
349-
.mockReturnValueOnce({
345+
.mockResolvedValueOnce({
350346
took: 100,
351347
errors: false,
352348
items: [

0 commit comments

Comments
 (0)