Files
PlantAI-rag-new/tests/frontend/unit/b2/chatpanel.health.test.js
T
jerryandClaude Fable 5 0875bb036c P0/M1: 四部分重构——frontend→ui、backend→sdk 改名 + 引用修复 + 骨架
- git mv 保留历史:ui/(Vue3+Vite)、sdk/(BFF 中间层)
- 路径语义修正:db_path/figures_cache 改相对 sdk/(BFF cwd),
  历史库 sdk/backend/data 迁移至 sdk/data/(会话/图片缓存无损)
- 引用修复 ~15 处:config.py、deploy/*.sh、vitest/playwright 配置、
  单测导入路径、docs、README、gitignore、.env.example
- 骨架:agent/{qa,methodology,legacy-rag,vendor}、knowledge/manifest.yaml、
  tooling/{compile,kb,crypto,assemble,audit,tools,scripts} + README
- 门禁验证:sdk pytest 125 通过、ui vitest 158 通过、
  生产 BFF 脚本重启四上游全绿 + 静态托管 + 会话库无损

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:56:47 +08:00

85 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment jsdom
// chatpanel.health.test.js —— BUG-R1-04 回归:QA 降级时作曲器禁用 + 降级提示(FR-HEALTH-002)
// 契约:health store(B1)暴露 qaOk getter;ChatPanel 读 health.data != null && !qaOk → 禁用。
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { setActivePinia, createPinia } from 'pinia';
// mock health store(B1 归属,不实改其文件):qaOk 由 data.upstreams.qa.ok 派生
vi.mock('../../../../ui/src/stores/health.js', () => ({
useHealthStore: vi.fn(() => healthState),
}));
// mock bench store(B3):ChatPanel 对 bench 的引用须容错(本测试不关心工作台)
vi.mock('../../../../ui/src/stores/bench.js', () => ({
useBenchStore: vi.fn(() => ({ setActiveTab: vi.fn(), setSourceScope: vi.fn() })),
}));
// sessions/chat store 用真实现(chat.store 内部 mock api 层,见下)
vi.mock('../../../../ui/src/api/sessions.js', () => ({
listSessions: vi.fn(async () => []),
createSession: vi.fn(async () => ({ id: 's1', title: '新会话', status: 'idle', created_at: 'x', updated_at: 'x', message_count: 0, meta: {} })),
getSession: vi.fn(async () => ({ id: 's1', title: 't', status: 'done', meta: {}, messages: [] })),
updateSession: vi.fn(async (id, b) => ({ id, ...b })),
deleteSession: vi.fn(async () => {}),
createMessage: vi.fn(),
updateMessage: vi.fn(),
getSessionSources: vi.fn(),
}));
vi.mock('../../../../ui/src/api/qa.js', () => ({
chatStream: vi.fn(),
chat: vi.fn(),
search: vi.fn(),
info: vi.fn(),
}));
import ChatPanel from '../../../../ui/src/components/chat/ChatPanel.vue';
/** health 状态(响应式替换:mock 工厂每次返回同一对象) */
let healthState;
function makeHealthState(data) {
return {
data,
qaOk: !!(data && data.upstreams && data.upstreams.qa && data.upstreams.qa.ok === true),
};
}
async function mountPanel() {
const w = mount(ChatPanel, { global: { stubs: { teleport: true } } });
await flushPromises();
return w;
}
describe('ChatPanel × health(BUG-R1-04)', () => {
beforeEach(() => {
setActivePinia(createPinia());
healthState = makeHealthState(null); // 未知态:首轮探测未返回
});
it('未知态(data=null):作曲器不禁用(避免首屏闪烁)', async () => {
const w = await mountPanel();
const ta = w.find('.composer textarea');
expect(ta.attributes('disabled')).toBeUndefined();
expect(w.find('.hint').text()).toContain('Enter');
});
it('qaOk=false:作曲器禁用 + 降级提示(labels.qaDown 文案)', async () => {
healthState = makeHealthState({
status: 'degraded',
upstreams: { qa: { ok: false }, rag: { ok: true }, ragAlt: { ok: true }, neo4j: { ok: true } },
});
const w = await mountPanel();
const ta = w.find('.composer textarea');
expect(ta.attributes('disabled')).toBeDefined();
expect(w.find('.hint').text()).toBe('知识问答服务当前不可用,请稍后重试');
});
it('qaOk=true:作曲器可用,提示为快捷键说明', async () => {
healthState = makeHealthState({ status: 'ok', upstreams: { qa: { ok: true } } });
const w = await mountPanel();
expect(w.find('.composer textarea').attributes('disabled')).toBeUndefined();
expect(w.find('.hint').text()).toContain('Enter');
});
});