Files
Agent-System/web/src/hooks/useChatWindow.test.ts
T

168 lines
5.4 KiB
TypeScript
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.
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); // 提问本身即为用户消息
});
test('mapRecordToMessage: restores task phase snapshot into card fields', () => {
const raw = {
id: 2048,
session_id: 'sess_task',
user_id: 1,
role: 'assistant' as const,
content: '已完成用户初始需求收集表',
intent: 'TRIGGER_TASK',
metadata: {
run_id: 'run_snap_1',
task_status: 'completed',
artifact: { title: '用户初始需求收集表', quality_score: 91 },
task_phase_snapshot: {
title: '用户初始需求收集表',
duration_ms: 42000,
label: '质量门禁通过',
status: 'completed',
steps: [
{ step: 'squad_assembly', icon: '👥', label: '组建小队', status: 'completed' },
{ step: 'quality_gate', icon: '🛡️', label: '质量门禁(95%)', status: 'completed', detail: '门禁' },
],
},
},
day_key: '2026-09-21',
created_at: '2026-09-21T10:00:00+08:00',
};
const msg = mapRecordToMessage(raw);
assert.equal(msg.taskRunId, 'run_snap_1');
assert.equal(msg.status, 'completed');
assert.equal(msg.taskDurationMs, 42000);
assert.equal(msg.livePhaseLabel, '质量门禁通过');
assert.equal(msg.phaseEvents?.length, 2);
assert.equal(msg.phaseSnapshot?.title, '用户初始需求收集表');
assert.equal(msg.phaseEvents?.[1].detail, '门禁');
});