Distinguish authorized-empty and forbidden phases; skip caching day buckets with zero messages; bind window cache keys to user id. Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mapRecordToMessage, mapRawBucket } from '../api/chatWindow';
|
|
import { resolveTurnAnchorId } from '../engine/anchorNavigator';
|
|
|
|
test('mapRecordToMessage: 正确映射服务端消息记录为前端 ChatMessage', () => {
|
|
const raw = {
|
|
id: 1024,
|
|
session_id: 'sess_test_001',
|
|
user_id: 101,
|
|
role: 'user' as const,
|
|
content: '立项便携超声设备',
|
|
intent: 'SUPPLEMENT_INFO',
|
|
branch: 'EXPECTATION_HIT',
|
|
metadata: {
|
|
author_name: '张工',
|
|
attachments: [{ name: 'spec.pdf', size: '2MB', type: 'pdf' }],
|
|
},
|
|
day_key: '2026-09-10',
|
|
category: 'consult',
|
|
created_at: '2026-09-10T09:12:00+08:00',
|
|
};
|
|
|
|
const msg = mapRecordToMessage(raw);
|
|
|
|
assert.equal(msg.id, 'msg-1024');
|
|
assert.equal(msg.serverId, 1024);
|
|
assert.equal(msg.sessionId, 'sess_test_001');
|
|
assert.equal(msg.role, 'user');
|
|
assert.equal(msg.authorName, '张工');
|
|
assert.equal(msg.content, '立项便携超声设备');
|
|
assert.equal(msg.intentTag, 'SUPPLEMENT_INFO');
|
|
assert.equal(msg.branchTag, 'EXPECTATION_HIT');
|
|
assert.equal(msg.dayKey, '2026-09-10');
|
|
assert.equal(msg.category, 'consult');
|
|
assert.equal(msg.attachments?.length, 1);
|
|
});
|
|
|
|
test('refresh window: 空 messages 日桶不算有效窗口', () => {
|
|
const emptyBucket = {
|
|
day_key: '2026-09-10',
|
|
count: 4,
|
|
loaded_count: 0,
|
|
truncated: false,
|
|
first_message_id: 1,
|
|
last_message_id: 4,
|
|
messages: [],
|
|
};
|
|
const hasMessages = emptyBucket.messages.length > 0;
|
|
assert.equal(hasMessages, false, 'must not treat stats-only day bucket as populated window');
|
|
});
|
|
|
|
test('mapRawBucket: 正确映射服务端按日分片桶', () => {
|
|
const rawBucket = {
|
|
day_key: '2026-09-10',
|
|
count: 38,
|
|
loaded_count: 38,
|
|
truncated: false,
|
|
first_message_id: 1024,
|
|
last_message_id: 1061,
|
|
messages: [
|
|
{
|
|
id: 1024,
|
|
session_id: 'sess_test_001',
|
|
user_id: 101,
|
|
role: 'user' as const,
|
|
content: '立项需求',
|
|
day_key: '2026-09-10',
|
|
created_at: '2026-09-10T09:12:00+08:00',
|
|
},
|
|
],
|
|
};
|
|
|
|
const bucket = mapRawBucket(rawBucket);
|
|
|
|
assert.equal(bucket.dayKey, '2026-09-10');
|
|
assert.equal(bucket.count, 38);
|
|
assert.equal(bucket.loadedCount, 38);
|
|
assert.equal(bucket.truncated, false);
|
|
assert.equal(bucket.firstMessageId, 1024);
|
|
assert.equal(bucket.lastMessageId, 1061);
|
|
assert.equal(bucket.messages.length, 1);
|
|
assert.equal(bucket.messages[0].serverId, 1024);
|
|
});
|
|
|
|
test('Category navigation: chip 选中后准确匹配目标分类最新轮次并触发归一化定位', () => {
|
|
const allMessages: any[] = [
|
|
{
|
|
id: 'm-1',
|
|
serverId: 101,
|
|
sessionId: 'sess_1',
|
|
role: 'user',
|
|
category: 'task',
|
|
content: '请开始声学仿真任务',
|
|
timestamp: '10:00',
|
|
},
|
|
{
|
|
id: 'm-2',
|
|
serverId: 102,
|
|
sessionId: 'sess_1',
|
|
role: 'assistant',
|
|
category: 'task',
|
|
content: '仿真任务启动,正在分析网格...',
|
|
timestamp: '10:01',
|
|
},
|
|
{
|
|
id: 'm-3',
|
|
serverId: 103,
|
|
sessionId: 'sess_1',
|
|
role: 'user',
|
|
category: 'consult',
|
|
content: 'CE 认证周期大概多久?',
|
|
timestamp: '10:02',
|
|
},
|
|
];
|
|
|
|
// 1. 查找 task 分类的最新一条消息
|
|
const taskMsgs = allMessages.filter((m) => m.category === 'task');
|
|
const latestTask = taskMsgs[taskMsgs.length - 1];
|
|
assert.equal(latestTask.id, 'm-2');
|
|
|
|
// 2. 方案 A 轮次归一化:由于最新一条是 assistant 回复,必须归一化至该轮用户的提问 (m-1)
|
|
const normalizedId = resolveTurnAnchorId(latestTask.serverId || latestTask.id, allMessages);
|
|
assert.equal(normalizedId, 101); // 用户提问 m-1 的 serverId
|
|
|
|
// 3. 验证 consult 分类
|
|
const consultMsgs = allMessages.filter((m) => m.category === 'consult');
|
|
const latestConsult = consultMsgs[consultMsgs.length - 1];
|
|
const consultNormalizedId = resolveTurnAnchorId(latestConsult.serverId || latestConsult.id, allMessages);
|
|
assert.equal(consultNormalizedId, 103); // 提问本身即为用户消息
|
|
});
|