113 lines
3.8 KiB
JavaScript
113 lines
3.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
mergeConversationSnapshot,
|
|
mergeSessionMessagesAfterFinish,
|
|
} from './chat-finish-sync.mjs';
|
|
|
|
const msg = (id, role, text) => ({
|
|
id,
|
|
role,
|
|
metadata: { userVisible: true },
|
|
content: [{ type: 'text', text }],
|
|
});
|
|
|
|
test('mergeConversationSnapshot keeps local-only messages when server snapshot is shorter', () => {
|
|
const local = [
|
|
msg('u1', 'user', '生成一个页面'),
|
|
msg('a1', 'assistant', '正在写入 public/demo.html…'),
|
|
msg('a2', 'assistant', '页面已生成'),
|
|
];
|
|
const server = [msg('u1', 'user', '生成一个页面')];
|
|
const merged = mergeConversationSnapshot(local, server);
|
|
assert.equal(merged.length, 3);
|
|
assert.deepEqual(
|
|
merged.map((message) => message.id),
|
|
['u1', 'a1', 'a2'],
|
|
);
|
|
});
|
|
|
|
test('mergeConversationSnapshot prefers server content for shared message ids', () => {
|
|
const local = [msg('u1', 'user', '旧文案')];
|
|
const server = [msg('u1', 'user', '新文案')];
|
|
const merged = mergeConversationSnapshot(local, server);
|
|
assert.equal(merged.length, 1);
|
|
assert.equal(merged[0].content[0].text, '新文案');
|
|
});
|
|
|
|
test('mergeConversationSnapshot returns current when incoming is empty', () => {
|
|
const local = [msg('u1', 'user', 'hello')];
|
|
assert.deepEqual(mergeConversationSnapshot(local, []), local);
|
|
});
|
|
|
|
test('mergeSessionMessagesAfterFinish matches Finish sync merge semantics', () => {
|
|
const local = [
|
|
msg('u1', 'user', '根据图片做主题页'),
|
|
msg('a1', 'assistant', '已完成'),
|
|
];
|
|
const server = [msg('u1', 'user', '根据图片做主题页')];
|
|
assert.deepEqual(
|
|
mergeSessionMessagesAfterFinish(local, server).map((message) => message.id),
|
|
['u1', 'a1'],
|
|
);
|
|
});
|
|
|
|
test('mergeConversationSnapshot keeps an omitted middle message between its anchors', () => {
|
|
const local = [
|
|
msg('u1', 'user', '第一轮'),
|
|
msg('a1', 'assistant', '第一轮回复'),
|
|
msg('u2', 'user', '第二轮'),
|
|
msg('a2', 'assistant', '第二轮回复'),
|
|
];
|
|
const server = [local[0], local[1], local[3]];
|
|
assert.deepEqual(
|
|
mergeConversationSnapshot(local, server).map((message) => message.id),
|
|
['u1', 'a1', 'u2', 'a2'],
|
|
);
|
|
});
|
|
|
|
test('mergeConversationSnapshot keeps local messages before the first and after the last server anchor', () => {
|
|
const local = [
|
|
msg('u0', 'user', '本地前置'),
|
|
msg('u1', 'user', '服务端消息'),
|
|
msg('a1', 'assistant', '服务端回复'),
|
|
msg('a2', 'assistant', '本地尾部'),
|
|
];
|
|
const server = [local[1], local[2]];
|
|
assert.deepEqual(
|
|
mergeConversationSnapshot(local, server).map((message) => message.id),
|
|
['u0', 'u1', 'a1', 'a2'],
|
|
);
|
|
});
|
|
|
|
test('mergeConversationSnapshot keeps local-only messages at their streamed position', () => {
|
|
const local = [
|
|
msg('u1', 'user', '生成页面'),
|
|
msg('a1', 'assistant', '开始分析'),
|
|
msg('a2', 'assistant', '调用工具'),
|
|
msg('a3', 'assistant', '页面完成'),
|
|
];
|
|
const server = [
|
|
msg('u1', 'user', '生成页面'),
|
|
msg('a3', 'assistant', '页面完成(服务端)'),
|
|
];
|
|
const merged = mergeConversationSnapshot(local, server);
|
|
assert.deepEqual(merged.map((message) => message.id), ['u1', 'a1', 'a2', 'a3']);
|
|
assert.equal(merged[3].content[0].text, '页面完成(服务端)');
|
|
});
|
|
|
|
test('mergeConversationSnapshot preserves multiple local messages before the next server anchor', () => {
|
|
const local = [
|
|
msg('u1', 'user', '任务'),
|
|
msg('a1', 'assistant', '步骤一'),
|
|
msg('a2', 'assistant', '步骤二'),
|
|
msg('a3', 'assistant', '完成'),
|
|
];
|
|
const server = [msg('u1', 'user', '任务'), msg('a3', 'assistant', '完成')];
|
|
assert.deepEqual(
|
|
mergeConversationSnapshot(local, server).map((message) => message.id),
|
|
['u1', 'a1', 'a2', 'a3'],
|
|
);
|
|
});
|