Skip to content

Commit b255a11

Browse files
opensearch-trigger-bot[bot]github-actions[bot]Kishore Kumaar Natarajan
authored
Unit test for QueryUtils, application, plugin (#96) (#125)
* Unit test for QueryUtils, application, plugin * Changes * Changes * Updated QueryUtils.test.ts * Updated Application.test.tsx * Updated plugin.test.tsx * used coremock in application.test.tsx and plugin.test.tsx * added edge cases - the response structure is different * added edge cases - the response structure is different --------- (cherry picked from commit b2502f0) Signed-off-by: Kishore Kumaar Natarajan <[email protected]> Signed-off-by: [email protected] <[email protected]> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kishore Kumaar Natarajan <[email protected]>
1 parent 0a7e821 commit b255a11

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

common/utils/QueryUtils.test.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { retrieveQueryById } from './QueryUtils';
7+
import { mockQueries } from '../../test/mocks/mockQueries';
8+
import { testQueryParams } from '../../test/mocks/testConstants';
9+
10+
jest.unmock('../../common/utils/QueryUtils');
11+
12+
describe('retrieveQueryById - Fetch Query Record by ID from API', () => {
13+
const mockCore = {
14+
http: {
15+
get: jest.fn(),
16+
post: jest.fn(),
17+
},
18+
};
19+
20+
beforeEach(() => {
21+
jest.clearAllMocks();
22+
});
23+
24+
const testStart = testQueryParams?.start;
25+
const testEnd = testQueryParams?.end;
26+
const testId = testQueryParams?.id;
27+
const mockQuery = mockQueries[0];
28+
29+
const mockResponse = {
30+
response: {
31+
top_queries: [mockQuery],
32+
},
33+
};
34+
35+
it('should make three GET requests to fetch different query records', async () => {
36+
mockCore.http.get.mockResolvedValue(mockResponse);
37+
38+
await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
39+
40+
expect(mockCore.http.get).toHaveBeenCalledTimes(3);
41+
expect(mockCore.http.get).toHaveBeenCalledWith('/api/top_queries/latency', {
42+
query: { from: testStart, to: testEnd, id: testId, dataSourceId: undefined },
43+
});
44+
expect(mockCore.http.get).toHaveBeenCalledWith('/api/top_queries/cpu', {
45+
query: { from: testStart, to: testEnd, id: testId, dataSourceId: undefined },
46+
});
47+
expect(mockCore.http.get).toHaveBeenCalledWith('/api/top_queries/memory', {
48+
query: { from: testStart, to: testEnd, id: testId, dataSourceId: undefined },
49+
});
50+
});
51+
52+
it('should return the valid query result', async () => {
53+
mockCore.http.get.mockResolvedValue(mockResponse);
54+
55+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
56+
57+
expect(result).toEqual(mockResponse.response.top_queries[0]);
58+
});
59+
60+
it('should return null if no queries are found', async () => {
61+
mockCore.http.get.mockResolvedValue({ response: { top_queries: [] } });
62+
63+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
64+
65+
expect(result).toBeNull();
66+
});
67+
it('should return null if API response is missing the response field', async () => {
68+
mockCore.http.get.mockResolvedValue({});
69+
70+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
71+
72+
expect(result).toBeNull();
73+
});
74+
75+
it('should return null if API response contains an unexpected structure', async () => {
76+
mockCore.http.get.mockResolvedValue({ unexpectedKey: {} });
77+
78+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
79+
80+
expect(result).toBeNull();
81+
});
82+
83+
it('should return null if API request fails', async () => {
84+
mockCore.http.get.mockRejectedValue(new Error('API error'));
85+
86+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
87+
88+
expect(result).toBeNull();
89+
});
90+
91+
it('should handle cases where API returns an empty object instead of expected response structure', async () => {
92+
mockCore.http.get.mockResolvedValue({});
93+
94+
const result = await retrieveQueryById(mockCore, undefined, testStart, testEnd, testId);
95+
96+
expect(result).toBeNull();
97+
});
98+
});

public/application.test.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as ReactDOM from 'react-dom';
7+
import { renderApp } from './application';
8+
import { coreMock } from '../../../src/core/public/mocks';
9+
10+
jest.mock('react-dom', () => {
11+
const actualReactDOM = jest.requireActual('react-dom');
12+
return {
13+
...actualReactDOM,
14+
render: jest.fn(),
15+
unmountComponentAtNode: jest.fn(),
16+
};
17+
});
18+
19+
describe('renderApp', () => {
20+
const coreMockStart = coreMock.createStart();
21+
const depsStartMock = {};
22+
const paramsMock = { element: document.createElement('div') };
23+
const dataSourceManagementMock = {};
24+
25+
afterEach(() => {
26+
jest.clearAllMocks();
27+
});
28+
29+
it('should unmount the component when the returned function is called', () => {
30+
const unmount = renderApp(coreMockStart, depsStartMock, paramsMock, dataSourceManagementMock);
31+
unmount();
32+
33+
expect(ReactDOM.unmountComponentAtNode).toHaveBeenCalledWith(paramsMock.element);
34+
});
35+
});

public/plugin.test.tsx

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { CoreSetup, CoreStart } from '../../../src/core/public';
7+
import { QueryInsightsDashboardsPlugin } from './plugin';
8+
import { PLUGIN_NAME } from '../common';
9+
import { renderApp } from './application';
10+
import { coreMock } from '../../../src/core/public/mocks';
11+
12+
jest.mock('./application', () => ({
13+
renderApp: jest.fn(),
14+
}));
15+
16+
describe('QueryInsightsDashboardsPlugin', () => {
17+
let plugin: QueryInsightsDashboardsPlugin;
18+
let coreSetupMock: jest.Mocked<CoreSetup>;
19+
let coreStartMock: jest.Mocked<CoreStart>;
20+
let registerMock: jest.Mock;
21+
22+
beforeEach(() => {
23+
coreSetupMock = coreMock.createSetup();
24+
coreStartMock = coreMock.createStart();
25+
26+
plugin = new QueryInsightsDashboardsPlugin();
27+
registerMock = coreSetupMock.application.register;
28+
});
29+
30+
it('should register the application in setup', () => {
31+
plugin.setup(coreSetupMock, {} as any);
32+
33+
expect(registerMock).toHaveBeenCalledWith(
34+
expect.objectContaining({
35+
id: PLUGIN_NAME,
36+
title: 'Query Insights',
37+
category: expect.objectContaining({
38+
id: expect.any(String),
39+
label: expect.any(String),
40+
order: expect.any(Number),
41+
}),
42+
mount: expect.any(Function),
43+
order: expect.any(Number),
44+
description: expect.any(String),
45+
})
46+
);
47+
});
48+
49+
it('should mount the application correctly', async () => {
50+
plugin.setup(coreSetupMock, {} as any);
51+
52+
const appRegistration = registerMock.mock.calls[0][0];
53+
expect(appRegistration).toBeDefined();
54+
55+
const paramsMock = { element: document.createElement('div') };
56+
const mountFunction = appRegistration.mount;
57+
58+
await mountFunction(paramsMock);
59+
60+
const depsMock = { dataSourceManagement: undefined };
61+
coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, depsMock]);
62+
63+
await mountFunction(paramsMock);
64+
65+
expect(renderApp).toHaveBeenCalledWith(
66+
coreStartMock,
67+
depsMock,
68+
expect.objectContaining({ element: expect.any(HTMLElement) }),
69+
depsMock.dataSourceManagement
70+
);
71+
});
72+
73+
it('should return empty start and stop methods', () => {
74+
// Ensures `start` and `stop` do not introduce unwanted behavior
75+
expect(plugin.start(coreStartMock)).toEqual({});
76+
expect(plugin.stop()).toBeUndefined();
77+
});
78+
});

test/mocks/testConstants.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export const testQueryParams = {
7+
start: '2025-01-01T00:00:00Z',
8+
end: '2025-01-31T23:59:59Z',
9+
id: '8c1e50c035663459d567fa11d8eb494d',
10+
};

0 commit comments

Comments
 (0)