import type { KnowledgeBaseNodeType } from './types' import type { ModelItem } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { CommonNodeType } from '@/app/components/workflow/types' import { render, screen } from '@testing-library/react' import { ConfigurationMethodEnum, ModelStatusEnum, ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' import { BlockEnum } from '@/app/components/workflow/types' import Node from './node' import { ChunkStructureEnum, IndexMethodEnum, RetrievalSearchMethodEnum, } from './types' const mockUseModelList = vi.hoisted(() => vi.fn()) const mockUseSettingsDisplay = vi.hoisted(() => vi.fn()) const mockUseEmbeddingModelStatus = vi.hoisted(() => vi.fn()) vi.mock('@tanstack/react-query', async (importOriginal) => { const actual = await importOriginal() return { ...actual, useQuery: () => ({ data: undefined }), } }) vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', async (importOriginal) => { const actual = await importOriginal() return { ...actual, useLanguage: () => 'en_US', useModelList: mockUseModelList, } }) vi.mock('./hooks/use-settings-display', () => ({ useSettingsDisplay: mockUseSettingsDisplay, })) vi.mock('./hooks/use-embedding-model-status', () => ({ useEmbeddingModelStatus: mockUseEmbeddingModelStatus, })) const createModelItem = (overrides: Partial = {}): ModelItem => ({ model: 'text-embedding-3-large', label: { en_US: 'Text Embedding 3 Large', zh_Hans: 'Text Embedding 3 Large' }, model_type: ModelTypeEnum.textEmbedding, fetch_from: ConfigurationMethodEnum.predefinedModel, status: ModelStatusEnum.active, model_properties: {}, load_balancing_enabled: false, ...overrides, }) const createNodeData = (overrides: Partial> = {}): CommonNodeType => ({ title: 'Knowledge Base', desc: '', type: BlockEnum.KnowledgeBase, index_chunk_variable_selector: ['result'], chunk_structure: ChunkStructureEnum.general, indexing_technique: IndexMethodEnum.QUALIFIED, embedding_model: 'text-embedding-3-large', embedding_model_provider: 'openai', keyword_number: 10, retrieval_model: { top_k: 3, score_threshold_enabled: false, score_threshold: 0.5, search_method: RetrievalSearchMethodEnum.semantic, }, ...overrides, }) describe('KnowledgeBaseNode', () => { beforeEach(() => { vi.clearAllMocks() mockUseModelList.mockReturnValue({ data: [] }) mockUseSettingsDisplay.mockReturnValue({ [IndexMethodEnum.QUALIFIED]: 'High Quality', [RetrievalSearchMethodEnum.semantic]: 'Vector Search', }) mockUseEmbeddingModelStatus.mockReturnValue({ providerMeta: undefined, modelProvider: undefined, currentModel: createModelItem(), status: 'active', }) }) // Embedding model row should mirror the selector status labels. describe('Embedding Model Status', () => { it('should render active embedding model label when the model is available', () => { render() expect(screen.getByText('Text Embedding 3 Large')).toBeInTheDocument() }) it('should render configure required when embedding model status requires configuration', () => { mockUseEmbeddingModelStatus.mockReturnValue({ providerMeta: undefined, modelProvider: undefined, currentModel: createModelItem({ status: ModelStatusEnum.noConfigure }), status: 'configure-required', }) render() expect(screen.getByText('common.modelProvider.selector.configureRequired')).toBeInTheDocument() }) it('should render disabled when embedding model status is disabled', () => { mockUseEmbeddingModelStatus.mockReturnValue({ providerMeta: undefined, modelProvider: undefined, currentModel: createModelItem({ status: ModelStatusEnum.disabled }), status: 'disabled', }) render() expect(screen.getByText('common.modelProvider.selector.disabled')).toBeInTheDocument() }) it('should render incompatible when embedding model status is incompatible', () => { mockUseEmbeddingModelStatus.mockReturnValue({ providerMeta: undefined, modelProvider: undefined, currentModel: undefined, status: 'incompatible', }) render() expect(screen.getByText('common.modelProvider.selector.incompatible')).toBeInTheDocument() }) it('should render configure model prompt when no embedding model is selected', () => { mockUseEmbeddingModelStatus.mockReturnValue({ providerMeta: undefined, modelProvider: undefined, currentModel: undefined, status: 'empty', }) render( , ) expect(screen.getByText('plugin.detailPanel.configureModel')).toBeInTheDocument() }) }) describe('Validation warnings', () => { it('should render a warning banner when chunk structure is missing', () => { render( , ) expect(screen.getByText(/chunkIsRequired/i)).toBeInTheDocument() }) it('should render a warning value for the chunks input row when no chunk variable is selected', () => { render( , ) expect(screen.getByText(/chunksVariableIsRequired/i)).toBeInTheDocument() }) it('should render a warning value for retrieval settings when reranking is incomplete', () => { mockUseModelList.mockImplementation((modelType: ModelTypeEnum) => { if (modelType === ModelTypeEnum.textEmbedding) { return { data: [{ provider: 'openai', models: [createModelItem()], }], } } return { data: [] } }) render( , ) expect(screen.getByText(/rerankingModelIsRequired/i)).toBeInTheDocument() }) it('should hide the embedding model row when the index method is not qualified', () => { render( , ) expect(screen.queryByText('Text Embedding 3 Large')).not.toBeInTheDocument() }) }) })