Skip to content

Commit a178d52

Browse files
authored
[Synthetics] Increase unit test coverage (#193201)
## Summary I frequently find when I go to modify code in Synthetics there is a lack of good module-level coverage, which makes it challenging to confidently change things directly, or tangential to untested code. This commonly results in unintended regressions, or very high levels of manual investigation during code review. One way to get past this is to first introduce tests before modifying the code in question, but that requires a lot of burden on the developer trying to make the change. We should instead find time to add additional module-level testing where possible, and that is the spirit of this PR.
1 parent 5bb42e5 commit a178d52

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)