forked from wangziqi/gongxue-base
- mergeArtifactIntoMessage 不再派发 legacy 列表,只维护 uiArtifacts - 渲染层从 uiArtifacts 派生 forms/reviews/charts,为空时回退 legacy (历史消息兼容,老 metadata 仅 a2uiForm/a2uiReview/a2uiChart) - sseReducer 删除 ui.form/ui.review/ui.chart 旧事件分支,只消费 ui.artifact(后端过渡期双发,旧事件将被忽略) - types.ts legacy 字段标注 deprecated;契约文档同步现状 - 测试更新:旧事件忽略 + uiArtifacts 合并/恢复断言 aislop scan: 5 引擎 0 issues
196 lines
5.5 KiB
TypeScript
196 lines
5.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { mapHistoryMessage } from './message-mappers';
|
|
|
|
describe('AI chat history mapper', () => {
|
|
it('restores reasoning, tool summaries and completed status', () => {
|
|
const mapped = mapHistoryMessage({
|
|
id: 3,
|
|
role: 'assistant',
|
|
content: '回答',
|
|
reasoningContent: '思考',
|
|
status: 'completed',
|
|
errorCode: null,
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
attachments: [
|
|
{
|
|
id: 8,
|
|
name: '考勤.pdf',
|
|
mimeType: 'application/pdf',
|
|
size: 100,
|
|
status: 'ready',
|
|
url: '/api/ai/chat/attachments/8',
|
|
createdAt: '2026-07-24T00:00:00.000Z',
|
|
},
|
|
],
|
|
toolRuns: [
|
|
{
|
|
toolCallId: 'tool-1',
|
|
toolName: 'search_rooms',
|
|
status: 'success',
|
|
resultSummary: '共 4 间',
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(mapped.status).toBe('success');
|
|
expect(mapped.message.reasoningContent).toBe('思考');
|
|
expect(mapped.message.toolRuns[0].summary).toBe('共 4 间');
|
|
expect(mapped.message.attachments).toHaveLength(1);
|
|
});
|
|
|
|
it('maps failed and cancelled history to X SDK statuses', () => {
|
|
const base = {
|
|
id: 4,
|
|
role: 'assistant' as const,
|
|
content: '',
|
|
reasoningContent: null,
|
|
errorCode: 'UPSTREAM_ERROR',
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
};
|
|
expect(mapHistoryMessage({ ...base, status: 'failed' }).status).toBe('error');
|
|
expect(mapHistoryMessage({ ...base, status: 'cancelled' }).status).toBe('abort');
|
|
});
|
|
|
|
it('restores a persisted A2UI form from message metadata', () => {
|
|
const mapped = mapHistoryMessage({
|
|
id: 5,
|
|
role: 'assistant',
|
|
content: '请填写表单',
|
|
reasoningContent: null,
|
|
status: 'completed',
|
|
errorCode: null,
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
metadata: {
|
|
a2uiForm: {
|
|
id: 'form-9',
|
|
title: '新增学生',
|
|
submitLabel: '提交创建',
|
|
status: 'pending',
|
|
fields: [
|
|
{ name: 'name', label: '姓名', type: 'input', required: true },
|
|
{ name: 'gender', label: '性别', type: 'select', options: [{ label: '男', value: 'male' }] },
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(mapped.message.forms).toHaveLength(1);
|
|
expect(mapped.message.forms?.[0]).toMatchObject({ id: 'form-9', title: '新增学生' });
|
|
});
|
|
|
|
it('restores uiArtifacts from message metadata (统一协议)', () => {
|
|
const mapped = mapHistoryMessage({
|
|
id: 8,
|
|
role: 'assistant',
|
|
content: '请处理',
|
|
reasoningContent: null,
|
|
status: 'completed',
|
|
errorCode: null,
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
metadata: {
|
|
uiArtifacts: [
|
|
{
|
|
id: 'form-10',
|
|
type: 'form',
|
|
status: 'submitted',
|
|
messageId: 8,
|
|
conversationId: 3,
|
|
payload: {
|
|
id: 'form-10',
|
|
title: '新增学生',
|
|
status: 'submitted',
|
|
fields: [],
|
|
},
|
|
},
|
|
{
|
|
id: 'review-10',
|
|
type: 'review',
|
|
status: 'expired',
|
|
messageId: 8,
|
|
conversationId: 3,
|
|
payload: {
|
|
id: 'review-10',
|
|
title: '旧预览',
|
|
status: 'expired',
|
|
sections: [],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(mapped.message.uiArtifacts).toHaveLength(2);
|
|
expect(mapped.message.uiArtifacts?.[0].payload).toMatchObject({
|
|
id: 'form-10',
|
|
status: 'submitted',
|
|
});
|
|
expect(mapped.message.uiArtifacts?.[1].payload).toMatchObject({
|
|
id: 'review-10',
|
|
status: 'expired',
|
|
});
|
|
});
|
|
|
|
it('restores a persisted A2UI review from message metadata', () => {
|
|
const mapped = mapHistoryMessage({
|
|
id: 6,
|
|
role: 'assistant',
|
|
content: '请审阅导入预览',
|
|
reasoningContent: null,
|
|
status: 'completed',
|
|
errorCode: null,
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
metadata: {
|
|
a2uiReview: {
|
|
id: 'review-9',
|
|
title: '批量导入',
|
|
status: 'pending',
|
|
sections: [
|
|
{
|
|
key: 'transfers',
|
|
type: 'transfers',
|
|
title: '换宿',
|
|
kind: 'table',
|
|
columns: [{ key: 'newRoom', title: '目标宿舍' }],
|
|
rows: [{ newRoom: '3-301' }],
|
|
issues: [],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(mapped.message.reviews).toHaveLength(1);
|
|
expect(mapped.message.reviews?.[0]).toMatchObject({ id: 'review-9', title: '批量导入' });
|
|
});
|
|
|
|
it('restores persisted A2UI charts from message metadata', () => {
|
|
const mapped = mapHistoryMessage({
|
|
id: 7,
|
|
role: 'assistant',
|
|
content: '这是图表',
|
|
reasoningContent: null,
|
|
status: 'completed',
|
|
errorCode: null,
|
|
createdAt: '2026-07-23T00:00:00.000Z',
|
|
metadata: {
|
|
a2uiChart: [
|
|
{
|
|
id: 'chart-9',
|
|
title: '各班级人数',
|
|
chartType: 'bar',
|
|
columns: [
|
|
{ key: 'className', title: '班级' },
|
|
{ key: 'count', title: '人数' },
|
|
],
|
|
rows: [{ className: '一班', count: 20 }],
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(mapped.message.charts).toHaveLength(1);
|
|
expect(mapped.message.charts?.[0]).toMatchObject({ id: 'chart-9', chartType: 'bar' });
|
|
});
|
|
|
|
});
|