Files
PlantAI-rag-new/tests/frontend/unit/b2/sessions.store.test.js
T
jerryandClaude Fable 5 1e94de7464 PlantAI-rag-new 首次提交:文献知识工作台
- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化
- Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab
- 四档响应式(桌面/笔记本/平板/手机)、深浅主题
- 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测
- 真实凭据已脱敏,运行时配置在 backend/.env(不入 git)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 20:47:52 +08:00

155 lines
7.5 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
// sessions.store.test.js —— sessions store 单测
// 覆盖:分组(今天/昨天/更早)、搜索过滤、CRUD、标题自动生成(FR-SESSION-001/005/008)。
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
const updateCalls = [];
vi.mock('../../../../frontend/src/api/sessions.js', () => ({
listSessions: vi.fn(async () => []),
createSession: vi.fn(async () => ({ id: 'new-1', title: '新会话', status: 'idle', created_at: '2026-08-25T00:00:00Z', updated_at: '2026-08-25T00:00:00Z', message_count: 0, meta: {} })),
getSession: vi.fn(async () => ({})),
updateSession: vi.fn(async (id, body) => { updateCalls.push({ id, body }); return { id, ...body }; }),
deleteSession: vi.fn(async () => {}),
createMessage: vi.fn(),
updateMessage: vi.fn(),
getSessionSources: vi.fn(),
}));
import { useSessionsStore, groupKeyOf, deriveTitle } from '../../../../frontend/src/stores/sessions.js';
const iso = (s) => new Date(s).toISOString();
describe('groupKeyOf', () => {
const now = new Date('2026-08-25T15:00:00');
it('今天/昨天/更早分界(本地日界)', () => {
expect(groupKeyOf(iso('2026-08-25T00:01:00'), now)).toBe('today');
expect(groupKeyOf(iso('2026-08-24T23:59:00'), now)).toBe('yesterday');
expect(groupKeyOf(iso('2026-08-24T00:00:00'), now)).toBe('yesterday');
expect(groupKeyOf(iso('2026-08-23T23:59:00'), now)).toBe('older');
expect(groupKeyOf(iso('2026-08-20T09:00:00'), now)).toBe('older');
});
it('无效/缺失时间归更早', () => {
expect(groupKeyOf(null, now)).toBe('older');
expect(groupKeyOf('不是时间', now)).toBe('older');
});
it('未来时间归今天', () => {
expect(groupKeyOf(iso('2026-08-26T00:00:00'), now)).toBe('today');
});
});
describe('deriveTitle(FR-SESSION-008)', () => {
it('前 30 字符 + 省略号', () => {
const q = '水稻SPL基因家族的功能研究有哪些关键进展?这是超过三十个字符的问题内容继续延长';
expect(deriveTitle(q).length).toBeLessThanOrEqual(30);
expect(deriveTitle(q)).toBe(q.slice(0, 30));
});
it('空白回退「新建会话」(labels.T.newTask)', () => {
expect(deriveTitle(' ')).toBe('新建会话');
});
});
describe('sessions store', () => {
beforeEach(() => {
setActivePinia(createPinia());
updateCalls.length = 0;
vi.clearAllMocks();
});
it('分组:按 updated_at 倒序 + 三组', async () => {
const { listSessions } = await import('../../../../frontend/src/api/sessions.js');
listSessions.mockResolvedValueOnce([
{ id: 'a', title: '今天会话', status: 'done', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 2, meta: {} },
{ id: 'b', title: '昨天会话', status: 'done', created_at: 'x', updated_at: iso('2026-08-24T10:00:00'), message_count: 4, meta: {} },
{ id: 'c', title: '更早会话', status: 'error', created_at: 'x', updated_at: iso('2026-08-20T10:00:00'), message_count: 1, meta: {} },
]);
const store = useSessionsStore();
await store.load();
expect(store.groups.map((g) => g.label)).toEqual(['今天', '昨天', '更早']);
expect(store.groups[0].items.map((s) => s.id)).toEqual(['a']);
expect(store.groups[1].items.map((s) => s.id)).toEqual(['b']);
expect(store.groups[2].items.map((s) => s.id)).toEqual(['c']);
});
it('搜索过滤:title 子串不区分大小写 + 无匹配(FR-SESSION-005)', async () => {
const { listSessions } = await import('../../../../frontend/src/api/sessions.js');
listSessions.mockResolvedValueOnce([
{ id: 'a', title: '水稻SPL基因家族', status: 'done', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 2, meta: {} },
{ id: 'b', title: 'NAC 转录因子研究综述', status: 'done', created_at: 'x', updated_at: iso('2026-08-24T10:00:00'), message_count: 2, meta: {} },
]);
const store = useSessionsStore();
await store.load();
store.searchQuery = 'spl';
expect(store.matchedCount).toBe(1);
expect(store.filtered[0].id).toBe('a');
store.searchQuery = '不存在的词';
expect(store.noMatch).toBe(true);
expect(store.matchedCount).toBe(0);
store.searchQuery = '';
expect(store.matchedCount).toBe(2);
expect(store.noMatch).toBe(false);
});
it('新建会话:切为活动会话 + 连点两次产生两个独立会话(FR-SESSION-003)', async () => {
const { createSession } = await import('../../../../frontend/src/api/sessions.js');
let n = 0;
createSession.mockImplementation(async () => ({ id: `new-${++n}`, title: '新会话', status: 'idle', created_at: 'x', updated_at: 'x', message_count: 0, meta: {} }));
const store = useSessionsStore();
const s1 = await store.create();
const s2 = await store.create();
expect(s1.id).not.toBe(s2.id);
expect(store.activeId).toBe(s2.id);
expect(store.items).toHaveLength(2);
});
it('删除:删除当前会话 → 切最新一个;列表空 → activeId=null(FR-SESSION-007)', async () => {
const { listSessions, deleteSession } = await import('../../../../frontend/src/api/sessions.js');
listSessions.mockResolvedValueOnce([
{ id: 'a', title: 'A', status: 'done', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 2, meta: {} },
{ id: 'b', title: 'B', status: 'done', created_at: 'x', updated_at: iso('2026-08-24T10:00:00'), message_count: 2, meta: {} },
]);
const store = useSessionsStore();
await store.load();
store.activeId = 'a';
await store.remove('a');
expect(store.activeId).toBe('b');
expect(store.items.map((s) => s.id)).toEqual(['b']);
await store.remove('b');
expect(store.activeId).toBeNull();
expect(store.empty).toBe(true);
expect(deleteSession).toHaveBeenCalledTimes(2);
});
it('标题自动生成:首问 PATCH 前 30 字符;title_manual 不再覆盖(FR-SESSION-008)', async () => {
const { listSessions } = await import('../../../../frontend/src/api/sessions.js');
const q = '水稻SPL基因家族的功能研究有哪些关键进展?';
listSessions.mockResolvedValueOnce([
{ id: 'a', title: '新会话', status: 'running', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 1, meta: { title_manual: false } },
{ id: 'b', title: '手动改名', status: 'running', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 1, meta: { title_manual: true } },
]);
const store = useSessionsStore();
await store.load();
await store.ensureTitle('a', q);
expect(store.items.find((s) => s.id === 'a').title).toBe(q.slice(0, 30));
expect(updateCalls.some((c) => c.id === 'a' && c.body.title === q.slice(0, 30))).toBe(true);
await store.ensureTitle('b', q);
expect(store.items.find((s) => s.id === 'b').title).toBe('手动改名');
expect(updateCalls.some((c) => c.id === 'b')).toBe(false);
});
it('重命名:PATCH 后本地同步(FR-SESSION-006)', async () => {
const { listSessions } = await import('../../../../frontend/src/api/sessions.js');
listSessions.mockResolvedValueOnce([
{ id: 'a', title: '旧标题', status: 'done', created_at: 'x', updated_at: iso('2026-08-25T10:00:00'), message_count: 2, meta: { title_manual: false } },
]);
const store = useSessionsStore();
await store.load();
await store.rename('a', '新标题');
expect(store.items[0].title).toBe('新标题');
expect(store.items[0].meta.title_manual).toBe(true);
});
});