mirror of https://github.com/langgenius/dify.git
232 lines
6.3 KiB
TypeScript
232 lines
6.3 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import type { AppConversationData, ConversationItem } from '@/models/share'
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
import { act, renderHook, waitFor } from '@testing-library/react'
|
|
import {
|
|
fetchChatList,
|
|
fetchConversations,
|
|
generationConversationName,
|
|
} from './share'
|
|
import {
|
|
shareQueryKeys,
|
|
useInvalidateShareConversations,
|
|
useShareChatList,
|
|
useShareConversationName,
|
|
useShareConversations,
|
|
} from './use-share'
|
|
|
|
vi.mock('./share', () => ({
|
|
fetchChatList: vi.fn(),
|
|
fetchConversations: vi.fn(),
|
|
generationConversationName: vi.fn(),
|
|
fetchAppInfo: vi.fn(),
|
|
fetchAppMeta: vi.fn(),
|
|
fetchAppParams: vi.fn(),
|
|
getAppAccessModeByAppCode: vi.fn(),
|
|
}))
|
|
|
|
const mockFetchConversations = vi.mocked(fetchConversations)
|
|
const mockFetchChatList = vi.mocked(fetchChatList)
|
|
const mockGenerationConversationName = vi.mocked(generationConversationName)
|
|
|
|
const createQueryClient = () => new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: false,
|
|
},
|
|
},
|
|
})
|
|
|
|
const createWrapper = (queryClient: QueryClient) => {
|
|
return ({ children }: { children: ReactNode }) => (
|
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
)
|
|
}
|
|
|
|
const renderShareHook = <T,>(hook: () => T) => {
|
|
const queryClient = createQueryClient()
|
|
const wrapper = createWrapper(queryClient)
|
|
return {
|
|
queryClient,
|
|
...renderHook(hook, { wrapper }),
|
|
}
|
|
}
|
|
|
|
const createConversationItem = (overrides: Partial<ConversationItem> = {}): ConversationItem => ({
|
|
id: 'conversation-1',
|
|
name: 'Conversation 1',
|
|
inputs: null,
|
|
introduction: 'Intro',
|
|
...overrides,
|
|
})
|
|
|
|
const createConversationData = (overrides: Partial<AppConversationData> = {}): AppConversationData => ({
|
|
data: [createConversationItem()],
|
|
has_more: false,
|
|
limit: 20,
|
|
...overrides,
|
|
})
|
|
|
|
// Scenario: share conversation list queries behave consistently with params and enablement.
|
|
describe('useShareConversations', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should fetch conversations when enabled for non-installed apps', async () => {
|
|
// Arrange
|
|
const params = {
|
|
isInstalledApp: false,
|
|
appId: undefined,
|
|
pinned: true,
|
|
limit: 50,
|
|
}
|
|
const response = createConversationData()
|
|
mockFetchConversations.mockResolvedValueOnce(response)
|
|
|
|
// Act
|
|
const { result, queryClient } = renderShareHook(() => useShareConversations(params))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(mockFetchConversations).toHaveBeenCalledWith(false, undefined, undefined, true, 50)
|
|
})
|
|
await waitFor(() => {
|
|
expect(result.current.data).toEqual(response)
|
|
})
|
|
expect(queryClient.getQueryCache().find({ queryKey: shareQueryKeys.conversationList(params) })).toBeDefined()
|
|
})
|
|
|
|
it('should not fetch conversations when installed app lacks appId', async () => {
|
|
// Arrange
|
|
const params = {
|
|
isInstalledApp: true,
|
|
appId: undefined,
|
|
}
|
|
|
|
// Act
|
|
const { result } = renderShareHook(() => useShareConversations(params))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(result.current.fetchStatus).toBe('idle')
|
|
})
|
|
expect(mockFetchConversations).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
// Scenario: chat list queries respect conversation ID and app installation rules.
|
|
describe('useShareChatList', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should fetch chat list when conversationId is provided', async () => {
|
|
// Arrange
|
|
const params = {
|
|
conversationId: 'conversation-1',
|
|
isInstalledApp: true,
|
|
appId: 'app-1',
|
|
}
|
|
const response = { data: [] }
|
|
mockFetchChatList.mockResolvedValueOnce(response)
|
|
|
|
// Act
|
|
const { result } = renderShareHook(() => useShareChatList(params))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(mockFetchChatList).toHaveBeenCalledWith('conversation-1', true, 'app-1')
|
|
})
|
|
await waitFor(() => {
|
|
expect(result.current.data).toEqual(response)
|
|
})
|
|
})
|
|
|
|
it('should not fetch chat list when conversationId is empty', async () => {
|
|
// Arrange
|
|
const params = {
|
|
conversationId: '',
|
|
isInstalledApp: false,
|
|
appId: undefined,
|
|
}
|
|
|
|
// Act
|
|
const { result } = renderShareHook(() => useShareChatList(params))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(result.current.fetchStatus).toBe('idle')
|
|
})
|
|
expect(mockFetchChatList).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
// Scenario: conversation name queries follow enabled flags and installation constraints.
|
|
describe('useShareConversationName', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should fetch conversation name when enabled and conversationId exists', async () => {
|
|
// Arrange
|
|
const params = {
|
|
conversationId: 'conversation-2',
|
|
isInstalledApp: false,
|
|
appId: undefined,
|
|
}
|
|
const response = createConversationItem({ id: 'conversation-2', name: 'Generated' })
|
|
mockGenerationConversationName.mockResolvedValueOnce(response)
|
|
|
|
// Act
|
|
const { result } = renderShareHook(() => useShareConversationName(params))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(mockGenerationConversationName).toHaveBeenCalledWith(false, undefined, 'conversation-2')
|
|
})
|
|
await waitFor(() => {
|
|
expect(result.current.data).toEqual(response)
|
|
})
|
|
})
|
|
|
|
it('should not fetch conversation name when disabled via options', async () => {
|
|
// Arrange
|
|
const params = {
|
|
conversationId: 'conversation-3',
|
|
isInstalledApp: false,
|
|
appId: undefined,
|
|
}
|
|
|
|
// Act
|
|
const { result } = renderShareHook(() => useShareConversationName(params, { enabled: false }))
|
|
|
|
// Assert
|
|
await waitFor(() => {
|
|
expect(result.current.fetchStatus).toBe('idle')
|
|
})
|
|
expect(mockGenerationConversationName).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
// Scenario: invalidation helper clears share conversation caches.
|
|
describe('useInvalidateShareConversations', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should invalidate share conversations query key when invoked', () => {
|
|
// Arrange
|
|
const { result, queryClient } = renderShareHook(() => useInvalidateShareConversations())
|
|
const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')
|
|
|
|
// Act
|
|
act(() => {
|
|
result.current()
|
|
})
|
|
|
|
// Assert
|
|
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: shareQueryKeys.conversations })
|
|
})
|
|
})
|