import { describe, expect, it } from 'vitest'; import { directThreadHistoryItemsToMessages, directThreadHistoryPage, isDirectTurnInProgress, prependDirectHistoryMessages, } from '../src/features/project-workspace/directThreadEvents'; describe('Direct 回合状态与历史时间', () => { it('游标来自原始切片,不从没有聊天消息的工具页倒推', () => { const page = directThreadHistoryPage( { items: [{ id: 'tool-older', type: 'function_call_output' }], hasMore: true, oldestItemId: 'tool-older', }, 'message-newer', ); expect(page.messages).toEqual([]); expect(page.cursor).toBe('tool-older'); }); it('空历史结束,无 ID 或不前进的非终页明确失败而不循环', () => { expect(directThreadHistoryPage({ items: [], hasMore: false })).toEqual({ messages: [], cursor: null, hasMore: false, }); expect(() => directThreadHistoryPage({ items: [], hasMore: true })).toThrow( '游标未前进', ); expect(() => directThreadHistoryPage( { items: [{ id: 'same' }], hasMore: true, oldestItemId: 'same', }, 'same', ), ).toThrow('游标未前进'); }); it('重叠页按原始 ID 去重,保留当前正文,旧无 ID 消息不删除', () => { const current = [ { role: 'assistant' as const, text: '完整正文', messageId: 'a' }, ]; const old = { role: 'user' as const, text: '用户输入', messageId: 'u' }; expect( prependDirectHistoryMessages(current, [ old, old, { role: 'assistant', text: '旧快照', messageId: 'a' }, { role: 'assistant', text: '无身份旧消息' }, ]), ).toEqual([old, { role: 'assistant', text: '无身份旧消息' }, ...current]); }); it('终态和空状态不恢复为活动回合', () => { for (const status of [ 'completed', 'failed', 'interrupted', null, undefined, ]) { expect(isDirectTurnInProgress(status)).toBe(false); } for (const status of ['accepted', 'running', 'streaming', 'finalizing']) { expect(isDirectTurnInProgress(status)).toBe(true); } }); it('按消息 id 读取信封时间,旧记录不使用当前时间补造', () => { const items = [ { type: 'message', role: 'user', id: 'direct-codex:turn:user', content: [{ type: 'input_text', text: '帮我修改游戏' }], }, ]; const timestamps = { 'direct-codex:turn:user': 1_800_000_000_001 }; expect( directThreadHistoryItemsToMessages(items, timestamps)[0]?.updatedAt, ).toBe(1_800_000_000_001); expect(directThreadHistoryItemsToMessages(items)[0]?.updatedAt).toBe(0); expect(items[0]).not.toHaveProperty('recordedAt'); }); });