From b343245c5933b3046b4375eb409e04138ce9da74 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Fri, 22 Dec 2023 22:53:08 +0000 Subject: [PATCH] add more unit tests Signed-off-by: Joshua Li --- .../query_assist/__tests__/hooks.test.ts | 79 ++++++++++++- .../query_assist/__tests__/input.test.tsx | 107 ++++++++++++++++++ .../explorer/query_assist/input.tsx | 2 + 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx diff --git a/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts b/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts index aa04d960d..45e910d02 100644 --- a/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts +++ b/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts @@ -3,7 +3,84 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { genericReducer } from '../hooks'; +import { renderHook } from '@testing-library/react-hooks'; +import { SavedObjectsFindResponsePublic } from '../../../../../../../../src/core/public'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import * as coreServices from '../../../../../../common/utils/core_services'; +import { coreRefs } from '../../../../../framework/core_refs'; +import { genericReducer, useCatIndices, useGetIndexPatterns } from '../hooks'; + +const coreStartMock = coreMock.createStart(); + +describe('useCatIndices', () => { + const httpMock = coreStartMock.http; + + beforeEach(() => { + jest.spyOn(coreServices, 'getOSDHttp').mockReturnValue(httpMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return indices', async () => { + httpMock.get.mockResolvedValueOnce([{ index: 'test1' }, { index: 'test2' }]); + + const { result, waitForNextUpdate } = renderHook(() => useCatIndices()); + expect(result.current.loading).toBe(true); + await waitForNextUpdate(); + expect(result.current.loading).toBe(false); + expect(result.current.data).toEqual([{ label: 'test1' }, { label: 'test2' }]); + }); + + it('should handle errors', async () => { + httpMock.get.mockRejectedValueOnce('API failed'); + + const { result, waitForNextUpdate } = renderHook(() => useCatIndices()); + expect(result.current.loading).toBe(true); + await waitForNextUpdate(); + expect(result.current.loading).toBe(false); + expect(result.current.data).toBe(undefined); + expect(result.current.error).toEqual('API failed'); + }); +}); + +describe('useGetIndexPatterns', () => { + const savedObjectsClientMock = coreStartMock.savedObjects.client as jest.Mocked< + typeof coreStartMock.savedObjects.client + >; + + beforeAll(() => { + coreRefs.savedObjectsClient = savedObjectsClientMock; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return index patterns', async () => { + savedObjectsClientMock.find.mockResolvedValueOnce({ + savedObjects: [{ attributes: { title: 'test1' } }, { attributes: { title: 'test2' } }], + } as SavedObjectsFindResponsePublic); + + const { result, waitForNextUpdate } = renderHook(() => useGetIndexPatterns()); + expect(result.current.loading).toBe(true); + await waitForNextUpdate(); + expect(result.current.loading).toBe(false); + expect(result.current.data).toEqual([{ label: 'test1' }, { label: 'test2' }]); + }); + + it('should handle errors', async () => { + savedObjectsClientMock.find.mockRejectedValueOnce('API failed'); + + const { result, waitForNextUpdate } = renderHook(() => useGetIndexPatterns()); + expect(result.current.loading).toBe(true); + await waitForNextUpdate(); + expect(result.current.loading).toBe(false); + expect(result.current.data).toBe(undefined); + expect(result.current.error).toEqual('API failed'); + }); +}); describe('genericReducer', () => { it('should return original state', () => { diff --git a/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx b/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx new file mode 100644 index 000000000..f6a9dd02d --- /dev/null +++ b/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx @@ -0,0 +1,107 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configureStore } from '@reduxjs/toolkit'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import React, { ComponentProps } from 'react'; +import { Provider } from 'react-redux'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { QUERY_ASSIST_API } from '../../../../../../common/constants/query_assist'; +import * as coreServices from '../../../../../../common/utils/core_services'; +import { coreRefs } from '../../../../../framework/core_refs'; +import { rootReducer } from '../../../../../framework/redux/reducers'; +import { initialTabId } from '../../../../../framework/redux/store/shared_state'; +import { QueryAssistInput } from '../input'; + +const renderQueryAssistInput = ( + overrideProps: Partial> = {} +) => { + const preloadedState = {}; + const store = configureStore({ reducer: rootReducer, preloadedState }); + const props: ComponentProps = Object.assign( + { + handleQueryChange: jest.fn(), + handleTimeRangePickerRefresh: jest.fn(), + tabId: initialTabId, + setNeedsUpdate: jest.fn(), + selectedIndex: [{ label: 'selected-test-index' }], + nlqInput: 'test-input', + setNlqInput: jest.fn(), + }, + overrideProps + ); + const component = render( + + + + ); + return { component, props, store }; +}; + +describe(' spec', () => { + const coreStartMock = coreMock.createStart(); + coreRefs.toasts = coreStartMock.notifications.toasts; + const httpMock = coreStartMock.http; + + beforeEach(() => { + jest.spyOn(coreServices, 'getOSDHttp').mockReturnValue(httpMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call generate ppl based on nlq input value', async () => { + httpMock.post.mockResolvedValueOnce('source = index'); + + const { component, props } = renderQueryAssistInput(); + + await waitFor(() => { + fireEvent.click(component.getByTestId('query-assist-generate-and-run-button')); + }); + + expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.GENERATE_PPL, { + body: '{"question":"test-input","index":"selected-test-index"}', + }); + expect(props.handleQueryChange).toBeCalledWith('source = index'); + }); + + it('should display toast for generate errors', async () => { + httpMock.post.mockRejectedValueOnce({ body: { statusCode: 429 } }); + + const { component } = renderQueryAssistInput(); + await waitFor(() => { + fireEvent.click(component.getByTestId('query-assist-generate-button')); + }); + + expect(coreRefs.toasts!.addError).toBeCalledWith( + { + message: 'Request is throttled. Try again later or contact your administrator', + statusCode: 429, + }, + { title: 'Failed to generate results' } + ); + }); + + it('should call summarize for generate and run errors', async () => { + httpMock.post.mockRejectedValueOnce({ body: { statusCode: 429 } }).mockResolvedValueOnce({ + summary: 'too many requests', + suggestedQuestions: ['1', '2'], + }); + + const { component } = renderQueryAssistInput(); + await waitFor(() => { + fireEvent.click(component.getByTestId('query-assist-generate-and-run-button')); + }); + + expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.GENERATE_PPL, { + body: '{"question":"test-input","index":"selected-test-index"}', + }); + expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.SUMMARIZE, { + body: + '{"question":"test-input","index":"selected-test-index","isError":true,"query":"","response":"{\\"statusCode\\":429}"}', + }); + }); +}); diff --git a/public/components/event_analytics/explorer/query_assist/input.tsx b/public/components/event_analytics/explorer/query_assist/input.tsx index 0eab5893c..58008819d 100644 --- a/public/components/event_analytics/explorer/query_assist/input.tsx +++ b/public/components/event_analytics/explorer/query_assist/input.tsx @@ -336,6 +336,7 @@ export const QueryAssistInput: React.FC = (props) => { isDisabled={generating || generatingOrRunning} iconSide="right" fill={false} + data-test-subj="query-assist-generate-button" style={{ width: 160 }} > Generate query @@ -350,6 +351,7 @@ export const QueryAssistInput: React.FC = (props) => { iconSide="right" type="submit" fill={barSelected} + data-test-subj="query-assist-generate-and-run-button" style={{ width: 175 }} > Generate and run