import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import { CloudKnowledgePanel } from '@/pages/CloudAgents/CloudKnowledge'; const api = vi.hoisted(() => ({ call: vi.fn() })); vi.mock('@/lib/cloud-agents-api', () => ({ cloudAgentsApi: api })); const slug = 'ml-' + 'a'.repeat(32); const empty = { databases: [], models: [] }; const available = { ...empty, models: [{ id: 'relay:fixture-embedding', name: '资料向量模型' }] }; beforeEach(() => vi.resetAllMocks()); afterEach(cleanup); function openCreation() { render( undefined} />); fireEvent.click(screen.getByText('管理我的知识文档')); fireEvent.click(screen.getByRole('button', { name: '新建知识库' })); } it('explains who configures an empty embedding catalog and refreshes without losing the name', async () => { api.call.mockResolvedValueOnce(empty).mockResolvedValueOnce(available); openCreation(); expect(await screen.findByText('暂时没有可用的向量模型。请联系平台管理员完成云端配置,然后刷新。')).toBeVisible(); expect(screen.getByRole('combobox', { name: '向量模型' })).toBeDisabled(); fireEvent.click(screen.getByText('管理员配置说明')); expect(screen.getByText(/Yuxi 后台/)).toHaveTextContent('智能体管理 → 模型供应商'); expect(screen.getByText(/Works Square 运营后台/)).toHaveTextContent('模型管理'); fireEvent.change(screen.getByRole('textbox', { name: '知识库名称' }), { target: { value: '创作资料' } }); fireEvent.click(screen.getByRole('button', { name: '刷新知识库与模型' })); expect(await screen.findByRole('option', { name: '资料向量模型' })).toBeInTheDocument(); expect(screen.getByRole('textbox', { name: '知识库名称' })).toHaveValue('创作资料'); expect(screen.queryByText('管理员配置说明')).not.toBeInTheDocument(); fireEvent.change(screen.getByRole('combobox', { name: '向量模型' }), { target: { value: 'relay:fixture-embedding' } }); expect(screen.getByRole('button', { name: '创建并选用' })).toBeEnabled(); }); it('shows loading instead of suggesting configuration is missing before the catalog arrives', () => { api.call.mockReturnValue(new Promise(() => undefined)); openCreation(); expect(screen.getByRole('combobox', { name: '向量模型' })).toBeDisabled(); expect(screen.getByRole('option', { name: '正在加载向量模型…' })).toBeInTheDocument(); expect(screen.queryByText('管理员配置说明')).not.toBeInTheDocument(); }); it('lets a failed catalog load retry in place and clears its error after success', async () => { api.call.mockRejectedValueOnce(new Error('目录连接失败')).mockResolvedValueOnce(available); openCreation(); expect(await screen.findByRole('alert')).toHaveTextContent('目录连接失败'); expect(screen.getByRole('combobox', { name: '向量模型' })).toBeDisabled(); fireEvent.click(screen.getByRole('button', { name: '刷新知识库与模型' })); expect(await screen.findByRole('option', { name: '资料向量模型' })).toBeInTheDocument(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); }); it('clears a removed selection after refresh so a new create cannot use an unavailable model', async () => { api.call.mockResolvedValueOnce(available).mockResolvedValueOnce(empty); openCreation(); await screen.findByRole('option', { name: '资料向量模型' }); fireEvent.change(screen.getByRole('textbox', { name: '知识库名称' }), { target: { value: '创作资料' } }); fireEvent.change(screen.getByRole('combobox', { name: '向量模型' }), { target: { value: 'relay:fixture-embedding' } }); fireEvent.click(screen.getByRole('button', { name: '刷新知识库与模型' })); await waitFor(() => expect(screen.getByRole('combobox', { name: '向量模型' })).toHaveValue('')); expect(screen.getByRole('button', { name: '创建并选用' })).toBeDisabled(); expect(api.call.mock.calls.every(([operation]) => operation === 'knowledge')).toBe(true); }); it('preserves an uncertain creation intent when its model disappears on refresh', async () => { let loads = 0; api.call.mockImplementation(async operation => { if (operation === 'knowledge') return ++loads === 1 ? available : empty; throw new Error('创建结果未确认'); }); openCreation(); await screen.findByRole('option', { name: '资料向量模型' }); fireEvent.change(screen.getByRole('textbox', { name: '知识库名称' }), { target: { value: '创作资料' } }); fireEvent.change(screen.getByRole('combobox', { name: '向量模型' }), { target: { value: 'relay:fixture-embedding' } }); fireEvent.click(screen.getByRole('button', { name: '创建并选用' })); await screen.findByRole('alert'); fireEvent.click(screen.getByRole('button', { name: '刷新知识库与模型' })); await screen.findByText('管理员配置说明'); fireEvent.click(screen.getByRole('button', { name: '重试创建' })); await waitFor(() => expect(api.call.mock.calls.filter(([operation]) => operation === 'createKnowledge')).toHaveLength(2)); const attempts = api.call.mock.calls.filter(([operation]) => operation === 'createKnowledge'); expect(attempts[0][1]).toEqual(attempts[1][1]); expect(attempts[1][1]).toMatchObject({ slug, name: '创作资料', embedding_model: 'relay:fixture-embedding' }); });