mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 17:38:04 +08:00
test: add comprehensive tests for retrieval-param-config component
- Add 42 tests covering all component functionality - Test semantic search mode: rerank toggle, model selection, top_k, score threshold - Test full text search mode: conditional score threshold visibility - Test keyword search mode (economical): simplified UI without rerank - Test hybrid search mode: reranking mode selection, weighted score, model selector - Test user interactions: toggle callbacks, model selection, parameter changes - Test multimodal tip display conditions - Test default weights initialization - Achieve 100% statement, 96% branch, 100% function, 100% line coverage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,915 @@
|
||||
import type { RetrievalConfig } from '@/types/app'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { RerankingModeEnum, WeightedScoreEnum } from '@/models/datasets'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import RetrievalParamConfig from './index'
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
const mockNotify = vi.fn()
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
default: {
|
||||
notify: (params: { type: string, message: string }) => mockNotify(params),
|
||||
},
|
||||
}))
|
||||
|
||||
let mockCurrentModel: { model: string, provider: string } | null = {
|
||||
model: 'rerank-model',
|
||||
provider: 'rerank-provider',
|
||||
}
|
||||
|
||||
vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
|
||||
useModelListAndDefaultModel: () => ({
|
||||
modelList: [
|
||||
{
|
||||
provider: 'rerank-provider',
|
||||
models: [{ model: 'rerank-model', label: { en_US: 'Rerank Model' } }],
|
||||
},
|
||||
],
|
||||
defaultModel: { provider: 'rerank-provider', model: 'rerank-model' },
|
||||
}),
|
||||
useCurrentProviderAndModel: () => ({
|
||||
currentModel: mockCurrentModel,
|
||||
currentProvider: mockCurrentModel ? { provider: 'rerank-provider' } : null,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/header/account-setting/model-provider-page/model-selector', () => ({
|
||||
default: ({ onSelect, defaultModel }: { onSelect: (v: { provider: string, model: string }) => void, defaultModel?: { provider: string, model: string } }) => (
|
||||
<div data-testid="model-selector" data-default-model={defaultModel ? JSON.stringify(defaultModel) : ''}>
|
||||
<button
|
||||
data-testid="select-model-btn"
|
||||
onClick={() => onSelect({ provider: 'new-provider', model: 'new-model' })}
|
||||
>
|
||||
Select Model
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/app/configuration/dataset-config/params-config/weighted-score', () => ({
|
||||
default: ({ value, onChange }: { value: { value: number[] }, onChange: (v: { value: number[] }) => void }) => (
|
||||
<div data-testid="weighted-score" data-value={JSON.stringify(value)}>
|
||||
<button
|
||||
data-testid="change-weights-btn"
|
||||
onClick={() => onChange({ value: [0.6, 0.4] })}
|
||||
>
|
||||
Change Weights
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/param-item/top-k-item', () => ({
|
||||
default: ({ value, onChange }: { value: number, onChange: (key: string, v: number) => void }) => (
|
||||
<div data-testid="top-k-item" data-value={value}>
|
||||
<button
|
||||
data-testid="change-top-k-btn"
|
||||
onClick={() => onChange('top_k', 10)}
|
||||
>
|
||||
Change TopK
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/param-item/score-threshold-item', () => ({
|
||||
default: ({ value, onChange, enable, hasSwitch, onSwitchChange }: {
|
||||
value: number
|
||||
onChange: (key: string, v: number) => void
|
||||
enable: boolean
|
||||
hasSwitch: boolean
|
||||
onSwitchChange?: (key: string, v: boolean) => void
|
||||
}) => (
|
||||
<div
|
||||
data-testid="score-threshold-item"
|
||||
data-value={value}
|
||||
data-enabled={enable}
|
||||
data-has-switch={hasSwitch}
|
||||
>
|
||||
<button
|
||||
data-testid="change-score-btn"
|
||||
onClick={() => onChange('score_threshold', 0.8)}
|
||||
>
|
||||
Change Score
|
||||
</button>
|
||||
{hasSwitch && onSwitchChange && (
|
||||
<button
|
||||
data-testid="toggle-score-switch-btn"
|
||||
onClick={() => onSwitchChange('score_threshold_enabled', !enable)}
|
||||
>
|
||||
Toggle Score Switch
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/radio-card', () => ({
|
||||
default: ({ isChosen, onChosen, title, description }: {
|
||||
isChosen: boolean
|
||||
onChosen: () => void
|
||||
title: string
|
||||
description: string
|
||||
}) => (
|
||||
<div
|
||||
data-testid="radio-card"
|
||||
data-chosen={isChosen}
|
||||
data-title={title}
|
||||
onClick={onChosen}
|
||||
>
|
||||
{title}
|
||||
<span data-testid="radio-description">{description}</span>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/switch', () => ({
|
||||
default: ({ defaultValue, onChange }: { defaultValue: boolean, onChange: (v: boolean) => void }) => (
|
||||
<button
|
||||
data-testid="rerank-switch"
|
||||
data-checked={defaultValue}
|
||||
onClick={() => onChange(!defaultValue)}
|
||||
>
|
||||
Switch
|
||||
</button>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/tooltip', () => ({
|
||||
default: ({ popupContent }: { popupContent: React.ReactNode }) => (
|
||||
<div data-testid="tooltip">{popupContent}</div>
|
||||
),
|
||||
}))
|
||||
|
||||
describe('RetrievalParamConfig', () => {
|
||||
const createDefaultConfig = (overrides?: Partial<RetrievalConfig>): RetrievalConfig => ({
|
||||
search_method: RETRIEVE_METHOD.semantic,
|
||||
reranking_enable: true,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'rerank-provider',
|
||||
reranking_model_name: 'rerank-model',
|
||||
},
|
||||
top_k: 5,
|
||||
score_threshold_enabled: true,
|
||||
score_threshold: 0.5,
|
||||
reranking_mode: RerankingModeEnum.RerankingModel,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
const mockOnChange = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockCurrentModel = { model: 'rerank-model', provider: 'rerank-provider' }
|
||||
})
|
||||
|
||||
describe('Semantic Search Mode', () => {
|
||||
it('should render rerank switch for semantic search', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('rerank-switch')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render model selector when reranking is enabled', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('model-selector')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render model selector when reranking is disabled', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: false })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('model-selector')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render TopK item', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('top-k-item')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('top-k-item')).toHaveAttribute('data-value', '5')
|
||||
})
|
||||
|
||||
it('should render score threshold item when reranking is enabled', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('score-threshold-item')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should toggle reranking enable', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('rerank-switch'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...config,
|
||||
reranking_enable: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should show error toast when enabling rerank without model', () => {
|
||||
mockCurrentModel = null
|
||||
const config = createDefaultConfig({ reranking_enable: false })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('rerank-switch'))
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'errorMsg.rerankModelRequired',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update reranking model on selection', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('select-model-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...config,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'new-provider',
|
||||
reranking_model_name: 'new-model',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should update top_k value', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-top-k-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...config,
|
||||
top_k: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it('should update score threshold value', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-score-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...config,
|
||||
score_threshold: 0.8,
|
||||
})
|
||||
})
|
||||
|
||||
it('should toggle score threshold enabled', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true, score_threshold_enabled: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('toggle-score-switch-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...config,
|
||||
score_threshold_enabled: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should show multimodal tip when showMultiModalTip is true and reranking enabled', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
showMultiModalTip={true}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('form.retrievalSetting.multiModalTip')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not show multimodal tip when showMultiModalTip is false', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
showMultiModalTip={false}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText('form.retrievalSetting.multiModalTip')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Full Text Search Mode', () => {
|
||||
it('should render rerank switch for full text search', () => {
|
||||
const config = createDefaultConfig({ search_method: RETRIEVE_METHOD.fullText })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.fullText}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('rerank-switch')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide score threshold when reranking is disabled for full text search', () => {
|
||||
const config = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.fullText,
|
||||
reranking_enable: false,
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.fullText}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('score-threshold-item')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show score threshold when reranking is enabled for full text search', () => {
|
||||
const config = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.fullText,
|
||||
reranking_enable: true,
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.fullText}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('score-threshold-item')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Keyword Search Mode (Economical)', () => {
|
||||
it('should not render rerank switch for keyword search', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.keywordSearch}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('rerank-switch')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render model selector for keyword search', () => {
|
||||
const config = createDefaultConfig({ reranking_enable: true })
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.keywordSearch}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('model-selector')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render TopK item for keyword search', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.keywordSearch}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('top-k-item')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render score threshold for keyword search', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.keywordSearch}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('score-threshold-item')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Hybrid Search Mode', () => {
|
||||
const hybridConfig = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.RerankingModel,
|
||||
})
|
||||
|
||||
it('should render radio cards for reranking mode selection', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
expect(radioCards).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('should have WeightedScore option', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('weightedScore.title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should have RerankingModel option', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('modelProvider.rerankModel.key')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show model selector when RerankingModel mode is selected', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('model-selector')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show WeightedScore component when WeightedScore mode is selected', () => {
|
||||
const weightedConfig = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.WeightedScore,
|
||||
weights: {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.7,
|
||||
embedding_provider_name: '',
|
||||
embedding_model_name: '',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.3,
|
||||
},
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={weightedConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('weighted-score')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('model-selector')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should change reranking mode to WeightedScore', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
const weightedScoreCard = radioCards.find(card => card.getAttribute('data-title') === 'weightedScore.title')
|
||||
fireEvent.click(weightedScoreCard!)
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalled()
|
||||
const calledWith = mockOnChange.mock.calls[0][0]
|
||||
expect(calledWith.reranking_mode).toBe(RerankingModeEnum.WeightedScore)
|
||||
expect(calledWith.weights).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not call onChange when clicking already selected mode', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
const rerankModelCard = radioCards.find(card => card.getAttribute('data-title') === 'modelProvider.rerankModel.key')
|
||||
fireEvent.click(rerankModelCard!)
|
||||
|
||||
expect(mockOnChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show error toast when switching to RerankingModel without model', () => {
|
||||
mockCurrentModel = null
|
||||
const weightedConfig = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.WeightedScore,
|
||||
weights: {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.7,
|
||||
embedding_provider_name: '',
|
||||
embedding_model_name: '',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.3,
|
||||
},
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={weightedConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
const rerankModelCard = radioCards.find(card => card.getAttribute('data-title') === 'modelProvider.rerankModel.key')
|
||||
fireEvent.click(rerankModelCard!)
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'errorMsg.rerankModelRequired',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update weights when WeightedScore changes', () => {
|
||||
const weightedConfig = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.WeightedScore,
|
||||
weights: {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.7,
|
||||
embedding_provider_name: '',
|
||||
embedding_model_name: '',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.3,
|
||||
},
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={weightedConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-weights-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalled()
|
||||
const calledWith = mockOnChange.mock.calls[0][0]
|
||||
expect(calledWith.weights.vector_setting.vector_weight).toBe(0.6)
|
||||
expect(calledWith.weights.keyword_setting.keyword_weight).toBe(0.4)
|
||||
})
|
||||
|
||||
it('should render TopK and score threshold for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('top-k-item')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('score-threshold-item')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should update top_k for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-top-k-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...hybridConfig,
|
||||
top_k: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it('should update score threshold for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-score-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...hybridConfig,
|
||||
score_threshold: 0.8,
|
||||
})
|
||||
})
|
||||
|
||||
it('should toggle score threshold enabled for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('toggle-score-switch-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...hybridConfig,
|
||||
score_threshold_enabled: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should show multimodal tip for hybrid search with RerankingModel', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
showMultiModalTip={true}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('form.retrievalSetting.multiModalTip')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not show multimodal tip for hybrid search with WeightedScore', () => {
|
||||
const weightedConfig = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.WeightedScore,
|
||||
weights: {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.7,
|
||||
embedding_provider_name: '',
|
||||
embedding_model_name: '',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.3,
|
||||
},
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={weightedConfig}
|
||||
showMultiModalTip={true}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByText('form.retrievalSetting.multiModalTip')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render rerank switch for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('rerank-switch')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should update model selection for hybrid search', () => {
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={hybridConfig}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('select-model-btn'))
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
...hybridConfig,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'new-provider',
|
||||
reranking_model_name: 'new-model',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Tooltip', () => {
|
||||
it('should render tooltip with rerank model tip', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('tooltip')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Rerank Model Label', () => {
|
||||
it('should display rerank model label', () => {
|
||||
const config = createDefaultConfig()
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('modelProvider.rerankModel.key')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Default weights initialization', () => {
|
||||
it('should initialize default weights when switching to WeightedScore without existing weights', () => {
|
||||
const configWithoutWeights = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.RerankingModel,
|
||||
weights: undefined,
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={configWithoutWeights}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
const weightedScoreCard = radioCards.find(card => card.getAttribute('data-title') === 'weightedScore.title')
|
||||
fireEvent.click(weightedScoreCard!)
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalled()
|
||||
const calledWith = mockOnChange.mock.calls[0][0]
|
||||
expect(calledWith.weights).toBeDefined()
|
||||
expect(calledWith.weights.weight_type).toBe(WeightedScoreEnum.Customized)
|
||||
})
|
||||
|
||||
it('should preserve existing weights when switching to WeightedScore', () => {
|
||||
const configWithWeights = createDefaultConfig({
|
||||
search_method: RETRIEVE_METHOD.hybrid,
|
||||
reranking_mode: RerankingModeEnum.RerankingModel,
|
||||
weights: {
|
||||
weight_type: WeightedScoreEnum.Customized,
|
||||
vector_setting: {
|
||||
vector_weight: 0.8,
|
||||
embedding_provider_name: 'test-provider',
|
||||
embedding_model_name: 'test-model',
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: 0.2,
|
||||
},
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.hybrid}
|
||||
value={configWithWeights}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const radioCards = screen.getAllByTestId('radio-card')
|
||||
const weightedScoreCard = radioCards.find(card => card.getAttribute('data-title') === 'weightedScore.title')
|
||||
fireEvent.click(weightedScoreCard!)
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalled()
|
||||
const calledWith = mockOnChange.mock.calls[0][0]
|
||||
expect(calledWith.weights.vector_setting.vector_weight).toBe(0.8)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Model Selector Default Model', () => {
|
||||
it('should pass correct default model to ModelSelector', () => {
|
||||
const config = createDefaultConfig({
|
||||
reranking_enable: true,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'custom-provider',
|
||||
reranking_model_name: 'custom-model',
|
||||
},
|
||||
})
|
||||
render(
|
||||
<RetrievalParamConfig
|
||||
type={RETRIEVE_METHOD.semantic}
|
||||
value={config}
|
||||
onChange={mockOnChange}
|
||||
/>,
|
||||
)
|
||||
|
||||
const modelSelector = screen.getByTestId('model-selector')
|
||||
const defaultModel = JSON.parse(modelSelector.getAttribute('data-default-model') || '{}')
|
||||
expect(defaultModel.provider).toBe('custom-provider')
|
||||
expect(defaultModel.model).toBe('custom-model')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user