From aae330627daef9b110dc5daeeaf1d5ef320a245e Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Thu, 18 Dec 2025 10:06:33 +0800
Subject: [PATCH] test: add unit tests for DatasetConfig component with
comprehensive coverage of rendering, dataset management, context variables,
and metadata filtering (#29779)
---
.../dataset-config/index.spec.tsx | 1048 +++++++++++++++++
1 file changed, 1048 insertions(+)
create mode 100644 web/app/components/app/configuration/dataset-config/index.spec.tsx
diff --git a/web/app/components/app/configuration/dataset-config/index.spec.tsx b/web/app/components/app/configuration/dataset-config/index.spec.tsx
new file mode 100644
index 0000000000..3c48eca206
--- /dev/null
+++ b/web/app/components/app/configuration/dataset-config/index.spec.tsx
@@ -0,0 +1,1048 @@
+import { render, screen, within } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import DatasetConfig from './index'
+import type { DataSet } from '@/models/datasets'
+import { DataSourceType, DatasetPermission } from '@/models/datasets'
+import { AppModeEnum } from '@/types/app'
+import { ModelModeType } from '@/types/app'
+import { RETRIEVE_TYPE } from '@/types/app'
+import { ComparisonOperator, LogicalOperator } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
+import type { DatasetConfigs } from '@/models/debug'
+
+// Mock external dependencies
+jest.mock('@/app/components/workflow/nodes/knowledge-retrieval/utils', () => ({
+ getMultipleRetrievalConfig: jest.fn(() => ({
+ top_k: 4,
+ score_threshold: 0.7,
+ reranking_enable: false,
+ reranking_model: undefined,
+ reranking_mode: 'reranking_model',
+ weights: { weight1: 1.0 },
+ })),
+ getSelectedDatasetsMode: jest.fn(() => ({
+ allInternal: true,
+ allExternal: false,
+ mixtureInternalAndExternal: false,
+ mixtureHighQualityAndEconomic: false,
+ inconsistentEmbeddingModel: false,
+ })),
+}))
+
+jest.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
+ useModelListAndDefaultModelAndCurrentProviderAndModel: jest.fn(() => ({
+ currentModel: { model: 'rerank-model' },
+ currentProvider: { provider: 'openai' },
+ })),
+}))
+
+jest.mock('@/context/app-context', () => ({
+ useSelector: jest.fn((fn: any) => fn({
+ userProfile: {
+ id: 'user-123',
+ },
+ })),
+}))
+
+jest.mock('@/utils/permission', () => ({
+ hasEditPermissionForDataset: jest.fn(() => true),
+}))
+
+jest.mock('../debug/hooks', () => ({
+ useFormattingChangedDispatcher: jest.fn(() => jest.fn()),
+}))
+
+jest.mock('lodash-es', () => ({
+ intersectionBy: jest.fn((...arrays) => {
+ // Mock realistic intersection behavior based on metadata name
+ const validArrays = arrays.filter(Array.isArray)
+ if (validArrays.length === 0) return []
+
+ // Start with first array and filter down
+ return validArrays[0].filter((item: any) => {
+ if (!item || !item.name) return false
+
+ // Only return items that exist in all arrays
+ return validArrays.every(array =>
+ array.some((otherItem: any) =>
+ otherItem && otherItem.name === item.name,
+ ),
+ )
+ })
+ }),
+}))
+
+jest.mock('uuid', () => ({
+ v4: jest.fn(() => 'mock-uuid'),
+}))
+
+// Mock child components
+jest.mock('./card-item', () => ({
+ __esModule: true,
+ default: ({ config, onRemove, onSave, editable }: any) => (
+
+ {config.name}
+ {editable && }
+
+
+ ),
+}))
+
+jest.mock('./params-config', () => ({
+ __esModule: true,
+ default: ({ disabled, selectedDatasets }: any) => (
+
+ ),
+}))
+
+jest.mock('./context-var', () => ({
+ __esModule: true,
+ default: ({ value, options, onChange }: any) => (
+
+ ),
+}))
+
+jest.mock('@/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter', () => ({
+ __esModule: true,
+ default: ({
+ metadataList,
+ metadataFilterMode,
+ handleMetadataFilterModeChange,
+ handleAddCondition,
+ handleRemoveCondition,
+ handleUpdateCondition,
+ handleToggleConditionLogicalOperator,
+ }: any) => (
+
+ {metadataList.length}
+
+
+
+
+
+
+ ),
+}))
+
+// Mock context
+const mockConfigContext: any = {
+ mode: AppModeEnum.CHAT,
+ modelModeType: ModelModeType.chat,
+ isAgent: false,
+ dataSets: [],
+ setDataSets: jest.fn(),
+ modelConfig: {
+ configs: {
+ prompt_variables: [],
+ },
+ },
+ setModelConfig: jest.fn(),
+ showSelectDataSet: jest.fn(),
+ datasetConfigs: {
+ retrieval_model: RETRIEVE_TYPE.multiWay,
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ top_k: 4,
+ score_threshold_enabled: false,
+ score_threshold: 0.7,
+ metadata_filtering_mode: 'disabled' as any,
+ metadata_filtering_conditions: undefined,
+ datasets: {
+ datasets: [],
+ },
+ } as DatasetConfigs,
+ datasetConfigsRef: {
+ current: {
+ retrieval_model: RETRIEVE_TYPE.multiWay,
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ top_k: 4,
+ score_threshold_enabled: false,
+ score_threshold: 0.7,
+ metadata_filtering_mode: 'disabled' as any,
+ metadata_filtering_conditions: undefined,
+ datasets: {
+ datasets: [],
+ },
+ } as DatasetConfigs,
+ },
+ setDatasetConfigs: jest.fn(),
+ setRerankSettingModalOpen: jest.fn(),
+}
+
+jest.mock('@/context/debug-configuration', () => ({
+ __esModule: true,
+ default: ({ children }: any) => (
+
+ {children}
+
+ ),
+}))
+
+jest.mock('use-context-selector', () => ({
+ useContext: jest.fn(() => mockConfigContext),
+}))
+
+const createMockDataset = (overrides: Partial = {}): DataSet => {
+ const defaultDataset: DataSet = {
+ id: 'dataset-1',
+ name: 'Test Dataset',
+ indexing_status: 'completed' as any,
+ icon_info: {
+ icon: '📘',
+ icon_type: 'emoji',
+ icon_background: '#FFEAD5',
+ icon_url: '',
+ },
+ description: 'Test dataset description',
+ permission: DatasetPermission.onlyMe,
+ data_source_type: DataSourceType.FILE,
+ indexing_technique: 'high_quality' as any,
+ author_name: 'Test Author',
+ created_by: 'user-123',
+ updated_by: 'user-123',
+ updated_at: Date.now(),
+ app_count: 0,
+ doc_form: 'text' as any,
+ document_count: 10,
+ total_document_count: 10,
+ total_available_documents: 10,
+ word_count: 1000,
+ provider: 'dify',
+ embedding_model: 'text-embedding-ada-002',
+ embedding_model_provider: 'openai',
+ embedding_available: true,
+ retrieval_model_dict: {
+ search_method: 'semantic_search' as any,
+ reranking_enable: false,
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ top_k: 4,
+ score_threshold_enabled: false,
+ score_threshold: 0.7,
+ },
+ retrieval_model: {
+ search_method: 'semantic_search' as any,
+ reranking_enable: false,
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ top_k: 4,
+ score_threshold_enabled: false,
+ score_threshold: 0.7,
+ },
+ tags: [],
+ external_knowledge_info: {
+ external_knowledge_id: '',
+ external_knowledge_api_id: '',
+ external_knowledge_api_name: '',
+ external_knowledge_api_endpoint: '',
+ },
+ external_retrieval_model: {
+ top_k: 2,
+ score_threshold: 0.5,
+ score_threshold_enabled: true,
+ },
+ built_in_field_enabled: true,
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ keyword_number: 3,
+ pipeline_id: 'pipeline-123',
+ is_published: true,
+ runtime_mode: 'general',
+ enable_api: true,
+ is_multimodal: false,
+ ...overrides,
+ }
+ return defaultDataset
+}
+
+const renderDatasetConfig = (contextOverrides: Partial = {}) => {
+ const useContextSelector = require('use-context-selector').useContext
+ const mergedContext = { ...mockConfigContext, ...contextOverrides }
+ useContextSelector.mockReturnValue(mergedContext)
+
+ return render()
+}
+
+describe('DatasetConfig', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ mockConfigContext.dataSets = []
+ mockConfigContext.setDataSets = jest.fn()
+ mockConfigContext.setModelConfig = jest.fn()
+ mockConfigContext.setDatasetConfigs = jest.fn()
+ mockConfigContext.setRerankSettingModalOpen = jest.fn()
+ })
+
+ describe('Rendering', () => {
+ it('should render dataset configuration panel when component mounts', () => {
+ renderDatasetConfig()
+
+ expect(screen.getByText('appDebug.feature.dataSet.title')).toBeInTheDocument()
+ })
+
+ it('should display empty state message when no datasets are configured', () => {
+ renderDatasetConfig()
+
+ expect(screen.getByText(/no.*data/i)).toBeInTheDocument()
+ expect(screen.getByTestId('params-config')).toBeDisabled()
+ })
+
+ it('should render dataset cards and enable parameters when datasets exist', () => {
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ expect(screen.getByText(dataset.name)).toBeInTheDocument()
+ expect(screen.getByTestId('params-config')).not.toBeDisabled()
+ })
+
+ it('should show configuration title and add dataset button in header', () => {
+ renderDatasetConfig()
+
+ expect(screen.getByText('appDebug.feature.dataSet.title')).toBeInTheDocument()
+ expect(screen.getByText('common.operation.add')).toBeInTheDocument()
+ })
+
+ it('should hide parameters configuration when in agent mode', () => {
+ renderDatasetConfig({
+ isAgent: true,
+ })
+
+ expect(screen.queryByTestId('params-config')).not.toBeInTheDocument()
+ })
+ })
+
+ describe('Dataset Management', () => {
+ it('should open dataset selection modal when add button is clicked', async () => {
+ const user = userEvent.setup()
+ renderDatasetConfig()
+
+ const addButton = screen.getByText('common.operation.add')
+ await user.click(addButton)
+
+ expect(mockConfigContext.showSelectDataSet).toHaveBeenCalledTimes(1)
+ })
+
+ it('should remove dataset and update configuration when remove button is clicked', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ const removeButton = screen.getByText('Remove')
+ await user.click(removeButton)
+
+ expect(mockConfigContext.setDataSets).toHaveBeenCalledWith([])
+ // Note: setDatasetConfigs is also called but its exact parameters depend on
+ // the retrieval config calculation which involves complex mocked utilities
+ })
+
+ it('should trigger rerank setting modal when removing dataset requires rerank configuration', async () => {
+ const user = userEvent.setup()
+ const { getSelectedDatasetsMode } = require('@/app/components/workflow/nodes/knowledge-retrieval/utils')
+
+ // Mock scenario that triggers rerank modal
+ getSelectedDatasetsMode.mockReturnValue({
+ allInternal: false,
+ allExternal: true,
+ mixtureInternalAndExternal: false,
+ mixtureHighQualityAndEconomic: false,
+ inconsistentEmbeddingModel: false,
+ })
+
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ const removeButton = screen.getByText('Remove')
+ await user.click(removeButton)
+
+ expect(mockConfigContext.setRerankSettingModalOpen).toHaveBeenCalledWith(true)
+ })
+
+ it('should handle dataset save', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ // Mock the onSave in card-item component - it will pass the original dataset
+ const editButton = screen.getByText('Edit')
+ await user.click(editButton)
+
+ expect(mockConfigContext.setDataSets).toHaveBeenCalledWith(
+ expect.arrayContaining([
+ expect.objectContaining({
+ id: dataset.id,
+ name: dataset.name,
+ editable: true,
+ }),
+ ]),
+ )
+ })
+
+ it('should format datasets with edit permission', () => {
+ const dataset = createMockDataset({
+ created_by: 'user-123',
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ })
+ })
+
+ describe('Context Variables', () => {
+ it('should show context variable selector in completion mode with datasets', () => {
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ mode: AppModeEnum.COMPLETION,
+ dataSets: [dataset],
+ modelConfig: {
+ configs: {
+ prompt_variables: [
+ { key: 'query', name: 'Query', type: 'string', is_context_var: false },
+ { key: 'context', name: 'Context', type: 'string', is_context_var: true },
+ ],
+ },
+ },
+ })
+
+ expect(screen.getByTestId('context-var')).toBeInTheDocument()
+ // Should find the selected context variable in the options
+ expect(screen.getByText('Select context variable')).toBeInTheDocument()
+ })
+
+ it('should not show context variable selector in chat mode', () => {
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ mode: AppModeEnum.CHAT,
+ dataSets: [dataset],
+ modelConfig: {
+ configs: {
+ prompt_variables: [
+ { key: 'query', name: 'Query', type: 'string', is_context_var: false },
+ ],
+ },
+ },
+ })
+
+ expect(screen.queryByTestId('context-var')).not.toBeInTheDocument()
+ })
+
+ it('should handle context variable selection', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+ renderDatasetConfig({
+ mode: AppModeEnum.COMPLETION,
+ dataSets: [dataset],
+ modelConfig: {
+ configs: {
+ prompt_variables: [
+ { key: 'query', name: 'Query', type: 'string', is_context_var: false },
+ { key: 'context', name: 'Context', type: 'string', is_context_var: true },
+ ],
+ },
+ },
+ })
+
+ const select = screen.getByTestId('context-var')
+ await user.selectOptions(select, 'query')
+
+ expect(mockConfigContext.setModelConfig).toHaveBeenCalled()
+ })
+ })
+
+ describe('Metadata Filtering', () => {
+ it('should render metadata filter component', () => {
+ const dataset = createMockDataset({
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('2') // both 'category' and 'priority'
+ })
+
+ it('should handle metadata filter mode change', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+ const updatedDatasetConfigs = {
+ ...mockConfigContext.datasetConfigs,
+ metadata_filtering_mode: 'disabled' as any,
+ }
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: updatedDatasetConfigs,
+ })
+
+ // Update the ref to match
+ mockConfigContext.datasetConfigsRef.current = updatedDatasetConfigs
+
+ const select = within(screen.getByTestId('metadata-filter')).getByDisplayValue('Disabled')
+ await user.selectOptions(select, 'automatic')
+
+ expect(mockConfigContext.setDatasetConfigs).toHaveBeenCalledWith(
+ expect.objectContaining({
+ metadata_filtering_mode: 'automatic',
+ }),
+ )
+ })
+
+ it('should handle adding metadata conditions', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+ const baseDatasetConfigs = {
+ ...mockConfigContext.datasetConfigs,
+ }
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: baseDatasetConfigs,
+ })
+
+ // Update the ref to match
+ mockConfigContext.datasetConfigsRef.current = baseDatasetConfigs
+
+ const addButton = within(screen.getByTestId('metadata-filter')).getByText('Add Condition')
+ await user.click(addButton)
+
+ expect(mockConfigContext.setDatasetConfigs).toHaveBeenCalledWith(
+ expect.objectContaining({
+ metadata_filtering_conditions: expect.objectContaining({
+ logical_operator: LogicalOperator.and,
+ conditions: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'mock-uuid',
+ name: 'test',
+ comparison_operator: ComparisonOperator.is,
+ }),
+ ]),
+ }),
+ }),
+ )
+ })
+
+ it('should handle removing metadata conditions', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+
+ const datasetConfigsWithConditions = {
+ ...mockConfigContext.datasetConfigs,
+ metadata_filtering_conditions: {
+ logical_operator: LogicalOperator.and,
+ conditions: [
+ { id: 'condition-id', name: 'test', comparison_operator: ComparisonOperator.is },
+ ],
+ },
+ }
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: datasetConfigsWithConditions,
+ })
+
+ // Update ref to match datasetConfigs
+ mockConfigContext.datasetConfigsRef.current = datasetConfigsWithConditions
+
+ const removeButton = within(screen.getByTestId('metadata-filter')).getByText('Remove Condition')
+ await user.click(removeButton)
+
+ expect(mockConfigContext.setDatasetConfigs).toHaveBeenCalledWith(
+ expect.objectContaining({
+ metadata_filtering_conditions: expect.objectContaining({
+ conditions: [],
+ }),
+ }),
+ )
+ })
+
+ it('should handle updating metadata conditions', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+
+ const datasetConfigsWithConditions = {
+ ...mockConfigContext.datasetConfigs,
+ metadata_filtering_conditions: {
+ logical_operator: LogicalOperator.and,
+ conditions: [
+ { id: 'condition-id', name: 'test', comparison_operator: ComparisonOperator.is },
+ ],
+ },
+ }
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: datasetConfigsWithConditions,
+ })
+
+ mockConfigContext.datasetConfigsRef.current = datasetConfigsWithConditions
+
+ const updateButton = within(screen.getByTestId('metadata-filter')).getByText('Update Condition')
+ await user.click(updateButton)
+
+ expect(mockConfigContext.setDatasetConfigs).toHaveBeenCalledWith(
+ expect.objectContaining({
+ metadata_filtering_conditions: expect.objectContaining({
+ conditions: expect.arrayContaining([
+ expect.objectContaining({
+ name: 'updated',
+ }),
+ ]),
+ }),
+ }),
+ )
+ })
+
+ it('should handle toggling logical operator', async () => {
+ const user = userEvent.setup()
+ const dataset = createMockDataset()
+
+ const datasetConfigsWithConditions = {
+ ...mockConfigContext.datasetConfigs,
+ metadata_filtering_conditions: {
+ logical_operator: LogicalOperator.and,
+ conditions: [
+ { id: 'condition-id', name: 'test', comparison_operator: ComparisonOperator.is },
+ ],
+ },
+ }
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: datasetConfigsWithConditions,
+ })
+
+ mockConfigContext.datasetConfigsRef.current = datasetConfigsWithConditions
+
+ const toggleButton = within(screen.getByTestId('metadata-filter')).getByText('Toggle Operator')
+ await user.click(toggleButton)
+
+ expect(mockConfigContext.setDatasetConfigs).toHaveBeenCalledWith(
+ expect.objectContaining({
+ metadata_filtering_conditions: expect.objectContaining({
+ logical_operator: LogicalOperator.or,
+ }),
+ }),
+ )
+ })
+ })
+
+ describe('Edge Cases', () => {
+ it('should handle null doc_metadata gracefully', () => {
+ const dataset = createMockDataset({
+ doc_metadata: undefined,
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('0')
+ })
+
+ it('should handle empty doc_metadata array', () => {
+ const dataset = createMockDataset({
+ doc_metadata: [],
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('0')
+ })
+
+ it('should handle missing userProfile', () => {
+ const useSelector = require('@/context/app-context').useSelector
+ useSelector.mockImplementation((fn: any) => fn({ userProfile: null }))
+
+ const dataset = createMockDataset()
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ })
+
+ it('should handle missing datasetConfigsRef gracefully', () => {
+ const dataset = createMockDataset()
+
+ // Test with undefined datasetConfigsRef - component renders without immediate error
+ // The component will fail on interaction due to non-null assertions in handlers
+ expect(() => {
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigsRef: undefined as any,
+ })
+ }).not.toThrow()
+
+ // The component currently expects datasetConfigsRef to exist for interactions
+ // This test documents the current behavior and requirements
+ })
+
+ it('should handle missing prompt_variables', () => {
+ // Context var is only shown when datasets exist AND there are prompt_variables
+ // Test with no datasets to ensure context var is not shown
+ renderDatasetConfig({
+ mode: AppModeEnum.COMPLETION,
+ dataSets: [],
+ modelConfig: {
+ configs: {
+ prompt_variables: [],
+ },
+ },
+ })
+
+ expect(screen.queryByTestId('context-var')).not.toBeInTheDocument()
+ })
+ })
+
+ describe('Component Integration', () => {
+ it('should integrate with card item component', () => {
+ const datasets = [
+ createMockDataset({ id: 'ds1', name: 'Dataset 1' }),
+ createMockDataset({ id: 'ds2', name: 'Dataset 2' }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ expect(screen.getByTestId('card-item-ds1')).toBeInTheDocument()
+ expect(screen.getByTestId('card-item-ds2')).toBeInTheDocument()
+ expect(screen.getByText('Dataset 1')).toBeInTheDocument()
+ expect(screen.getByText('Dataset 2')).toBeInTheDocument()
+ })
+
+ it('should integrate with params config component', () => {
+ const datasets = [
+ createMockDataset(),
+ createMockDataset({ id: 'ds2' }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ const paramsConfig = screen.getByTestId('params-config')
+ expect(paramsConfig).toBeInTheDocument()
+ expect(paramsConfig).toHaveTextContent('Params (2)')
+ expect(paramsConfig).not.toBeDisabled()
+ })
+
+ it('should integrate with metadata filter component', () => {
+ const datasets = [
+ createMockDataset({
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'tags', type: 'string' } as any,
+ ],
+ }),
+ createMockDataset({
+ id: 'ds2',
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ const metadataFilter = screen.getByTestId('metadata-filter')
+ expect(metadataFilter).toBeInTheDocument()
+ // Should show intersection (only 'category')
+ expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('1')
+ })
+ })
+
+ describe('Model Configuration', () => {
+ it('should handle metadata model change', () => {
+ const dataset = createMockDataset()
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: {
+ ...mockConfigContext.datasetConfigs,
+ metadata_model_config: {
+ provider: 'openai',
+ name: 'gpt-3.5-turbo',
+ mode: AppModeEnum.CHAT,
+ completion_params: { temperature: 0.7 },
+ },
+ },
+ })
+
+ // The component would need to expose this functionality through the metadata filter
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ })
+
+ it('should handle metadata completion params change', () => {
+ const dataset = createMockDataset()
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ datasetConfigs: {
+ ...mockConfigContext.datasetConfigs,
+ metadata_model_config: {
+ provider: 'openai',
+ name: 'gpt-3.5-turbo',
+ mode: AppModeEnum.CHAT,
+ completion_params: { temperature: 0.5, max_tokens: 100 },
+ },
+ },
+ })
+
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ })
+ })
+
+ describe('Permission Handling', () => {
+ it('should hide edit options when user lacks permission', () => {
+ const { hasEditPermissionForDataset } = require('@/utils/permission')
+ hasEditPermissionForDataset.mockReturnValue(false)
+
+ const dataset = createMockDataset({
+ created_by: 'other-user',
+ permission: DatasetPermission.onlyMe,
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ // The editable property should be false when no permission
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ })
+
+ it('should show readonly state for non-editable datasets', () => {
+ const { hasEditPermissionForDataset } = require('@/utils/permission')
+ hasEditPermissionForDataset.mockReturnValue(false)
+
+ const dataset = createMockDataset({
+ created_by: 'admin',
+ permission: DatasetPermission.allTeamMembers,
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ })
+
+ it('should allow editing when user has partial member permission', () => {
+ const { hasEditPermissionForDataset } = require('@/utils/permission')
+ hasEditPermissionForDataset.mockReturnValue(true)
+
+ const dataset = createMockDataset({
+ created_by: 'admin',
+ permission: DatasetPermission.partialMembers,
+ partial_member_list: ['user-123'],
+ })
+
+ renderDatasetConfig({
+ dataSets: [dataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
+ })
+ })
+
+ describe('Dataset Reordering and Management', () => {
+ it('should maintain dataset order after updates', () => {
+ const datasets = [
+ createMockDataset({ id: 'ds1', name: 'Dataset 1' }),
+ createMockDataset({ id: 'ds2', name: 'Dataset 2' }),
+ createMockDataset({ id: 'ds3', name: 'Dataset 3' }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ // Verify order is maintained
+ expect(screen.getByText('Dataset 1')).toBeInTheDocument()
+ expect(screen.getByText('Dataset 2')).toBeInTheDocument()
+ expect(screen.getByText('Dataset 3')).toBeInTheDocument()
+ })
+
+ it('should handle multiple dataset operations correctly', async () => {
+ const user = userEvent.setup()
+ const datasets = [
+ createMockDataset({ id: 'ds1', name: 'Dataset 1' }),
+ createMockDataset({ id: 'ds2', name: 'Dataset 2' }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ // Remove first dataset
+ const removeButton1 = screen.getAllByText('Remove')[0]
+ await user.click(removeButton1)
+
+ expect(mockConfigContext.setDataSets).toHaveBeenCalledWith([datasets[1]])
+ })
+ })
+
+ describe('Complex Configuration Scenarios', () => {
+ it('should handle multiple retrieval methods in configuration', () => {
+ const datasets = [
+ createMockDataset({
+ id: 'ds1',
+ retrieval_model: {
+ search_method: 'semantic_search' as any,
+ reranking_enable: true,
+ reranking_model: {
+ reranking_provider_name: 'cohere',
+ reranking_model_name: 'rerank-v3.5',
+ },
+ top_k: 5,
+ score_threshold_enabled: true,
+ score_threshold: 0.8,
+ },
+ }),
+ createMockDataset({
+ id: 'ds2',
+ retrieval_model: {
+ search_method: 'full_text_search' as any,
+ reranking_enable: false,
+ reranking_model: {
+ reranking_provider_name: '',
+ reranking_model_name: '',
+ },
+ top_k: 3,
+ score_threshold_enabled: false,
+ score_threshold: 0.5,
+ },
+ }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ expect(screen.getByTestId('params-config')).toHaveTextContent('Params (2)')
+ })
+
+ it('should handle external knowledge base integration', () => {
+ const externalDataset = createMockDataset({
+ provider: 'notion',
+ external_knowledge_info: {
+ external_knowledge_id: 'notion-123',
+ external_knowledge_api_id: 'api-456',
+ external_knowledge_api_name: 'Notion Integration',
+ external_knowledge_api_endpoint: 'https://api.notion.com',
+ },
+ })
+
+ renderDatasetConfig({
+ dataSets: [externalDataset],
+ })
+
+ expect(screen.getByTestId(`card-item-${externalDataset.id}`)).toBeInTheDocument()
+ expect(screen.getByText(externalDataset.name)).toBeInTheDocument()
+ })
+ })
+
+ describe('Performance and Error Handling', () => {
+ it('should handle large dataset lists efficiently', () => {
+ // Create many datasets to test performance
+ const manyDatasets = Array.from({ length: 50 }, (_, i) =>
+ createMockDataset({
+ id: `ds-${i}`,
+ name: `Dataset ${i}`,
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ }),
+ )
+
+ renderDatasetConfig({
+ dataSets: manyDatasets,
+ })
+
+ expect(screen.getByTestId('params-config')).toHaveTextContent('Params (50)')
+ })
+
+ it('should handle metadata intersection calculation efficiently', () => {
+ const datasets = [
+ createMockDataset({
+ id: 'ds1',
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'tags', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ }),
+ createMockDataset({
+ id: 'ds2',
+ doc_metadata: [
+ { name: 'category', type: 'string' } as any,
+ { name: 'status', type: 'string' } as any,
+ { name: 'priority', type: 'number' } as any,
+ ],
+ }),
+ ]
+
+ renderDatasetConfig({
+ dataSets: datasets,
+ })
+
+ // Should calculate intersection correctly
+ expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
+ })
+ })
+})