Skip to content

Commit 8c8a822

Browse files
committed
wip
1 parent 896bfb5 commit 8c8a822

File tree

7 files changed

+189
-144
lines changed

7 files changed

+189
-144
lines changed

redisinsight/ui/src/pages/vector-search/create-index/hooks/useCreateIndex.spec.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@ import { renderHook, act } from '@testing-library/react-hooks'
22
import { useCreateIndex } from './useCreateIndex'
33
import {
44
CreateSearchIndexParameters,
5+
SampleDataContent,
56
SampleDataType,
67
SearchIndexType,
78
} from '../types'
89

910
const mockLoad = jest.fn()
10-
const mockDispatch = jest.fn()
11+
const mockExecute = jest.fn()
12+
const mockAddCommands = jest.fn()
1113

1214
jest.mock('uiSrc/services/hooks', () => ({
1315
useLoadData: () => ({
1416
load: mockLoad,
1517
}),
16-
useDispatchWbQuery: () => mockDispatch,
18+
useExecuteQuery: () => mockExecute,
19+
}))
20+
21+
jest.mock('uiSrc/services/workbenchStorage', () => ({
22+
addCommands: (...args: any[]) => mockAddCommands(...args),
1723
}))
1824

1925
jest.mock('uiSrc/utils/index/generateFtCreateCommand', () => ({
@@ -26,18 +32,18 @@ describe('useCreateIndex', () => {
2632
})
2733

2834
const defaultParams: CreateSearchIndexParameters = {
29-
dataContent: '',
30-
usePresetVectorIndex: true,
31-
presetVectorIndexName: '',
32-
tags: [],
3335
instanceId: 'test-instance-id',
34-
searchIndexType: SearchIndexType.REDIS_QUERY_ENGINE,
36+
dataContent: SampleDataContent.E_COMMERCE_DISCOVERY,
3537
sampleDataType: SampleDataType.PRESET_DATA,
38+
searchIndexType: SearchIndexType.REDIS_QUERY_ENGINE,
39+
usePresetVectorIndex: true,
40+
indexName: 'bikes',
41+
indexFields: [],
3642
}
3743

3844
it('should complete flow successfully', async () => {
3945
mockLoad.mockResolvedValue(undefined)
40-
mockDispatch.mockImplementation((_data, { afterAll }) => afterAll?.())
46+
mockExecute.mockResolvedValue([{ id: '1', databaseId: 'test-instance-id' }])
4147

4248
const { result } = renderHook(() => useCreateIndex())
4349

@@ -46,7 +52,8 @@ describe('useCreateIndex', () => {
4652
})
4753

4854
expect(mockLoad).toHaveBeenCalledWith('test-instance-id', 'bikes')
49-
expect(mockDispatch).toHaveBeenCalled()
55+
expect(mockExecute).toHaveBeenCalled()
56+
expect(mockAddCommands).toHaveBeenCalled()
5057
expect(result.current.success).toBe(true)
5158
expect(result.current.error).toBeNull()
5259
expect(result.current.loading).toBe(false)
@@ -80,22 +87,20 @@ describe('useCreateIndex', () => {
8087
expect(result.current.loading).toBe(false)
8188
})
8289

83-
it('should handle dispatch failure', async () => {
90+
it('should handle execution failure', async () => {
8491
mockLoad.mockResolvedValue(undefined)
85-
mockDispatch.mockImplementation((_data, { onFail }) =>
86-
onFail?.(new Error('Dispatch failed')),
87-
)
92+
mockExecute.mockRejectedValue(new Error('Execution failed'))
8893

8994
const { result } = renderHook(() => useCreateIndex())
9095

9196
await act(async () => {
9297
await result.current.run(defaultParams)
9398
})
9499

95-
expect(mockDispatch).toHaveBeenCalled()
100+
expect(mockExecute).toHaveBeenCalled()
96101
expect(result.current.success).toBe(false)
97102
expect(result.current.error).toBeInstanceOf(Error)
98-
expect(result.current.error?.message).toBe('Dispatch failed')
103+
expect(result.current.error?.message).toBe('Execution failed')
99104
expect(result.current.loading).toBe(false)
100105
})
101106
})

redisinsight/ui/src/pages/vector-search/create-index/hooks/useCreateIndex.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useCallback, useState } from 'react'
2-
import { useLoadData, useDispatchWbQuery } from 'uiSrc/services/hooks'
2+
import { reverse } from 'lodash'
3+
import { useLoadData, useExecuteQuery } from 'uiSrc/services/hooks'
4+
import { addCommands } from 'uiSrc/services/workbenchStorage'
35
import { generateFtCreateCommand } from 'uiSrc/utils/index/generateFtCreateCommand'
46
import { CreateSearchIndexParameters, PresetDataType } from '../types'
57

@@ -20,7 +22,7 @@ export const useCreateIndex = (): UseCreateIndexResult => {
2022
const [error, setError] = useState<Error | null>(null)
2123

2224
const { load } = useLoadData()
23-
const dispatchCreateIndex = useDispatchWbQuery()
25+
const executeQuery = useExecuteQuery()
2426

2527
const run = useCallback(
2628
async ({ instanceId }: CreateSearchIndexParameters) => {
@@ -40,22 +42,22 @@ export const useCreateIndex = (): UseCreateIndexResult => {
4042
await load(instanceId, collectionName)
4143

4244
// Step 2: Create the search index
43-
await new Promise<void>((resolve, reject) => {
44-
dispatchCreateIndex(generateFtCreateCommand(), {
45-
afterAll: () => {
46-
setSuccess(true)
47-
resolve()
48-
},
49-
onFail: reject,
50-
})
51-
})
45+
const cmd = generateFtCreateCommand()
46+
const data = await executeQuery(instanceId, cmd)
47+
48+
// Step 3: Persist results locally so Vector Search history (CommandsView) shows it
49+
if (Array.isArray(data) && data.length) {
50+
await addCommands(reverse(data))
51+
}
52+
53+
setSuccess(true)
5254
} catch (e) {
5355
setError(e instanceof Error ? e : new Error(String(e)))
5456
} finally {
5557
setLoading(false)
5658
}
5759
},
58-
[load, dispatchCreateIndex],
60+
[load, executeQuery],
5961
)
6062

6163
return {

redisinsight/ui/src/services/hooks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ export * from './hooks'
22
export * from './useWebworkers'
33
export * from './useCabability'
44
export * from './useStateWithContext'
5-
export * from './useDispatchWbQuery'
5+
export * from './useExecuteQuery'
66
export * from './useLoadData'

redisinsight/ui/src/services/hooks/useDispatchWbQuery.spec.ts

Lines changed: 0 additions & 83 deletions
This file was deleted.

redisinsight/ui/src/services/hooks/useDispatchWbQuery.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { renderHook, act } from '@testing-library/react-hooks'
2+
import { rest } from 'msw'
3+
import { ApiEndpoints } from 'uiSrc/constants'
4+
import { mswServer } from 'uiSrc/mocks/server'
5+
import { getMswURL } from 'uiSrc/utils/test-utils'
6+
import { getUrl } from 'uiSrc/utils'
7+
import { RunQueryMode, ResultsMode } from 'uiSrc/slices/interfaces'
8+
9+
import { useExecuteQuery } from './useExecuteQuery'
10+
11+
describe('useExecuteQuery', () => {
12+
const instanceId = 'test-instance-id'
13+
const command = 'FT.CREATE idx:bikes_vss ...'
14+
15+
beforeEach(() => {
16+
mswServer.resetHandlers()
17+
jest.clearAllMocks()
18+
})
19+
20+
it('should return empty array and not call API when data is null', async () => {
21+
const { result: hookResult } = renderHook(() => useExecuteQuery())
22+
23+
let result: any
24+
await act(async () => {
25+
result = await hookResult.current(instanceId, null)
26+
})
27+
28+
expect(result).toEqual([])
29+
})
30+
31+
it('should return empty array and not call API when data is undefined', async () => {
32+
const { result: hookResult } = renderHook(() => useExecuteQuery())
33+
34+
let result: any
35+
await act(async () => {
36+
result = await hookResult.current(instanceId, undefined)
37+
})
38+
39+
expect(result).toEqual([])
40+
})
41+
42+
it('should call API with correct parameters and return result', async () => {
43+
const mockResponse = [{ id: '1', databaseId: instanceId }]
44+
45+
mswServer.use(
46+
rest.post(
47+
getMswURL(
48+
getUrl(instanceId, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
49+
),
50+
async (req, res, ctx) => {
51+
const body = await req.json()
52+
expect(body).toEqual({
53+
commands: [command],
54+
mode: RunQueryMode.ASCII,
55+
resultsMode: ResultsMode.Default,
56+
type: 'SEARCH',
57+
})
58+
return res(ctx.status(200), ctx.json(mockResponse))
59+
},
60+
),
61+
)
62+
63+
const { result } = renderHook(() => useExecuteQuery())
64+
65+
let returned
66+
await act(async () => {
67+
returned = await result.current(instanceId, command)
68+
})
69+
70+
expect(returned).toEqual(mockResponse)
71+
})
72+
73+
it('should invoke afterAll callback on success', async () => {
74+
const mockResponse = [{ id: '1', databaseId: instanceId }]
75+
76+
mswServer.use(
77+
rest.post(
78+
getMswURL(
79+
getUrl(instanceId, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
80+
),
81+
async (_req, res, ctx) => res(ctx.status(200), ctx.json(mockResponse)),
82+
),
83+
)
84+
85+
const afterAll = jest.fn()
86+
87+
const { result } = renderHook(() => useExecuteQuery())
88+
89+
await act(async () => {
90+
await result.current(instanceId, command, { afterAll })
91+
})
92+
93+
expect(afterAll).toHaveBeenCalled()
94+
})
95+
96+
it('should invoke onFail and throw on error', async () => {
97+
mswServer.use(
98+
rest.post(
99+
getMswURL(
100+
getUrl(instanceId, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
101+
),
102+
async (_req, res, ctx) => res(ctx.status(500)),
103+
),
104+
)
105+
106+
const onFail = jest.fn()
107+
108+
const { result } = renderHook(() => useExecuteQuery())
109+
110+
await act(async () => {
111+
await expect(
112+
result.current(instanceId, command, { onFail }),
113+
).rejects.toThrow()
114+
})
115+
116+
expect(onFail).toHaveBeenCalled()
117+
})
118+
})

0 commit comments

Comments
 (0)