Skip to content

Commit dcd1cb6

Browse files
[8.x] [Synthetics] Increase unit test coverage (#193201) (#193214)
# Backport This will backport the following commits from `main` to `8.x`: - [[Synthetics] Increase unit test coverage (#193201)](#193201) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Justin Kambic","email":"jk@elastic.co"},"sourceCommit":{"committedDate":"2024-09-17T18:21:05Z","message":"[Synthetics] Increase unit test coverage (#193201)\n\n## Summary\r\n\r\nI frequently find when I go to modify code in Synthetics there is a lack\r\nof good module-level coverage, which makes it challenging to confidently\r\nchange things directly, or tangential to untested code. This commonly\r\nresults in unintended regressions, or very high levels of manual\r\ninvestigation during code review.\r\n\r\nOne way to get past this is to first introduce tests before modifying\r\nthe code in question, but that requires a lot of burden on the developer\r\ntrying to make the change.\r\n\r\nWe should instead find time to add additional module-level testing where\r\npossible, and that is the spirit of this PR.","sha":"a178d52afe99d2693bfd4c10eda8b5a9cb3ac30c","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","ci:project-deploy-observability","Team:obs-ux-management","v8.16.0"],"title":"[Synthetics] Increase unit test coverage","number":193201,"url":"https://github.com/elastic/kibana/pull/193201","mergeCommit":{"message":"[Synthetics] Increase unit test coverage (#193201)\n\n## Summary\r\n\r\nI frequently find when I go to modify code in Synthetics there is a lack\r\nof good module-level coverage, which makes it challenging to confidently\r\nchange things directly, or tangential to untested code. This commonly\r\nresults in unintended regressions, or very high levels of manual\r\ninvestigation during code review.\r\n\r\nOne way to get past this is to first introduce tests before modifying\r\nthe code in question, but that requires a lot of burden on the developer\r\ntrying to make the change.\r\n\r\nWe should instead find time to add additional module-level testing where\r\npossible, and that is the spirit of this PR.","sha":"a178d52afe99d2693bfd4c10eda8b5a9cb3ac30c"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193201","number":193201,"mergeCommit":{"message":"[Synthetics] Increase unit test coverage (#193201)\n\n## Summary\r\n\r\nI frequently find when I go to modify code in Synthetics there is a lack\r\nof good module-level coverage, which makes it challenging to confidently\r\nchange things directly, or tangential to untested code. This commonly\r\nresults in unintended regressions, or very high levels of manual\r\ninvestigation during code review.\r\n\r\nOne way to get past this is to first introduce tests before modifying\r\nthe code in question, but that requires a lot of burden on the developer\r\ntrying to make the change.\r\n\r\nWe should instead find time to add additional module-level testing where\r\npossible, and that is the spirit of this PR.","sha":"a178d52afe99d2693bfd4c10eda8b5a9cb3ac30c"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Justin Kambic <jk@elastic.co>
1 parent 4376d9b commit dcd1cb6

2 files changed

Lines changed: 261 additions & 0 deletions

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { queryPings } from './query_pings';
9+
import { SyntheticsEsClient } from '../../lib';
10+
11+
jest.mock('../../lib'); // Mock the ES client module
12+
13+
const mockEsClient: Partial<SyntheticsEsClient> = {
14+
search: jest.fn(),
15+
};
16+
17+
describe('queryPings', () => {
18+
beforeEach(() => {
19+
jest.clearAllMocks(); // Reset mocks before each test
20+
});
21+
22+
it.each([10, undefined])(
23+
'should return the correct result when fields are provided',
24+
async (sizeParam) => {
25+
const params = {
26+
syntheticsEsClient: mockEsClient as SyntheticsEsClient,
27+
dateRange: { from: '2023-01-01', to: '2023-01-02' },
28+
index: 0,
29+
monitorId: 'test-monitor',
30+
status: 'up',
31+
sort: 'desc',
32+
size: sizeParam,
33+
pageIndex: 0,
34+
fields: [{ field: 'monitor.id' }],
35+
fieldsExtractorFn: (doc: any) => ({ fieldData: doc._source }),
36+
};
37+
38+
const mockResponse = {
39+
body: {
40+
hits: {
41+
hits: [{ _source: { 'monitor.id': 'test-monitor' } }],
42+
total: { value: 1 },
43+
},
44+
},
45+
};
46+
47+
(mockEsClient.search as jest.Mock).mockResolvedValueOnce(mockResponse);
48+
49+
const result = await queryPings(params);
50+
expect(result).toEqual({
51+
total: 1,
52+
pings: [{ fieldData: { 'monitor.id': 'test-monitor' } }],
53+
});
54+
55+
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
56+
const searchParams = (mockEsClient.search as jest.Mock).mock.calls[0][0];
57+
expect(searchParams.body.size).toEqual(sizeParam ?? 25);
58+
}
59+
);
60+
61+
it('should return the correct result when no fields are provided', async () => {
62+
const params = {
63+
syntheticsEsClient: mockEsClient as SyntheticsEsClient,
64+
dateRange: { from: '2023-01-01', to: '2023-01-02' },
65+
index: 0,
66+
monitorId: 'test-monitor',
67+
status: 'up',
68+
sort: 'desc',
69+
size: 10,
70+
pageIndex: 0,
71+
};
72+
73+
const mockResponse = {
74+
body: {
75+
hits: {
76+
hits: [{ _source: { '@timestamp': '2023-01-01T00:00:00Z' }, _id: 'doc1' }],
77+
total: { value: 1 },
78+
},
79+
},
80+
};
81+
82+
(mockEsClient.search as jest.Mock).mockResolvedValueOnce(mockResponse);
83+
84+
const result = await queryPings(params);
85+
expect(result).toEqual({
86+
total: 1,
87+
pings: [
88+
{ '@timestamp': '2023-01-01T00:00:00Z', docId: 'doc1', timestamp: '2023-01-01T00:00:00Z' },
89+
],
90+
});
91+
92+
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
93+
});
94+
95+
it('should handle excluded locations in the query', async () => {
96+
const params = {
97+
syntheticsEsClient: mockEsClient as SyntheticsEsClient,
98+
dateRange: { from: '2023-01-01', to: '2023-01-02' },
99+
excludedLocations: JSON.stringify(['excluded-location']),
100+
size: 10,
101+
pageIndex: 0,
102+
};
103+
104+
const mockResponse = {
105+
body: {
106+
hits: {
107+
hits: [],
108+
total: { value: 0 },
109+
},
110+
},
111+
};
112+
113+
(mockEsClient.search as jest.Mock).mockResolvedValueOnce(mockResponse);
114+
115+
const result = await queryPings(params);
116+
117+
expect(result).toEqual({
118+
total: 0,
119+
pings: [],
120+
});
121+
122+
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
123+
});
124+
125+
it('should return an empty result when Elasticsearch returns no hits', async () => {
126+
const params = {
127+
syntheticsEsClient: mockEsClient as SyntheticsEsClient,
128+
dateRange: { from: '2023-01-01', to: '2023-01-02' },
129+
size: 10,
130+
pageIndex: 0,
131+
};
132+
133+
const mockResponse = {
134+
body: {
135+
hits: {
136+
hits: [],
137+
total: { value: 0 },
138+
},
139+
},
140+
};
141+
142+
(mockEsClient.search as jest.Mock).mockResolvedValueOnce(mockResponse);
143+
144+
const result = await queryPings(params);
145+
expect(result).toEqual({
146+
total: 0,
147+
pings: [],
148+
});
149+
150+
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
151+
});
152+
153+
it('should throw an error if query fails to execute', async () => {
154+
const params = {
155+
syntheticsEsClient: mockEsClient as SyntheticsEsClient,
156+
dateRange: { from: '2023-01-01', to: '2023-01-02' },
157+
size: 10,
158+
pageIndex: 0,
159+
};
160+
161+
(mockEsClient.search as jest.Mock).mockRejectedValueOnce(new Error('Query failed'));
162+
163+
await expect(queryPings(params)).rejects.toThrow('Query failed');
164+
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
165+
});
166+
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import * as getAllMonitors from '../../saved_objects/synthetics_monitor/get_all_monitors';
9+
import * as getCerts from '../../queries/get_certs';
10+
import { getSyntheticsCertsRoute } from './get_certificates';
11+
12+
describe('getSyntheticsCertsRoute', () => {
13+
let getMonitorsSpy: jest.SpyInstance;
14+
15+
beforeEach(() => {
16+
getMonitorsSpy = jest.spyOn(getAllMonitors, 'getAllMonitors').mockReturnValue([] as any);
17+
});
18+
19+
afterEach(() => jest.clearAllMocks());
20+
21+
it('returns empty set when no monitors are found', async () => {
22+
const route = getSyntheticsCertsRoute();
23+
expect(
24+
await route.handler({
25+
// @ts-expect-error partial implementation for testing
26+
request: { query: {} },
27+
// @ts-expect-error partial implementation for testing
28+
syntheticsEsClient: jest.fn(),
29+
savedObjectClient: jest.fn(),
30+
})
31+
).toEqual({
32+
data: {
33+
certs: [],
34+
total: 0,
35+
},
36+
});
37+
expect(getMonitorsSpy).toHaveBeenCalledTimes(1);
38+
});
39+
40+
it('returns cert data when monitors are found', async () => {
41+
const getMonitorsResult = [
42+
{
43+
id: 'test-id',
44+
monitor: {
45+
type: 'browser',
46+
name: 'test-monitor',
47+
enabled: true,
48+
schedule: {
49+
interval: 1,
50+
timezone: 'UTC',
51+
},
52+
},
53+
},
54+
] as any;
55+
const processMonitorsSpy = jest.spyOn(getAllMonitors, 'processMonitors').mockReturnValue({
56+
// @ts-expect-error partial implementation for testing
57+
enableMonitorQueryIds: ['test-id'],
58+
});
59+
const getCertsResult = {
60+
total: 1,
61+
certs: [
62+
{
63+
monitors: [
64+
{
65+
name: 'test-monitor',
66+
id: 'test-id',
67+
configId: 'test-id',
68+
url: 'https://elastic.co',
69+
},
70+
],
71+
sha256: 'some-hash',
72+
configId: 'test-id',
73+
},
74+
],
75+
};
76+
const getSyntheticsCertsSpy = jest
77+
.spyOn(getCerts, 'getSyntheticsCerts')
78+
// @ts-expect-error partial implementation for testing
79+
.mockReturnValue(getCertsResult);
80+
const route = getSyntheticsCertsRoute();
81+
getMonitorsSpy.mockReturnValue(getMonitorsResult);
82+
const result = await route.handler({
83+
// @ts-expect-error partial implementation for testing
84+
request: { query: {} },
85+
// @ts-expect-error partial implementation for testing
86+
syntheticsEsClient: jest.fn(),
87+
savedObjectClient: jest.fn(),
88+
});
89+
expect(getMonitorsSpy).toHaveBeenCalledTimes(1);
90+
expect(processMonitorsSpy).toHaveBeenCalledTimes(1);
91+
expect(processMonitorsSpy).toHaveBeenCalledWith(getMonitorsResult);
92+
expect(getSyntheticsCertsSpy).toHaveBeenCalledTimes(1);
93+
expect(result).toEqual({ data: { ...getCertsResult } });
94+
});
95+
});

0 commit comments

Comments
 (0)