Files
Agent-System/web/src/engine/phaseEventMapper.test.ts
T
WeiCHEN 1f37f90b4b feat(chat): show MinerU parse progress while waiting in-turn
When summarizing the latest upload, poll parse state up to MinerU timeout,
emit document_parsing phase events so the bubble stays live and input stays
locked, then summarize or return a clear slow/failed reply.
2026-09-23 09:55:59 +08:00

143 lines
6.5 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { applyPhaseEvent, PHASE_META } from './phaseEventMapper';
test('applyPhaseEvent: appends first phase as running node', () => {
const strip = applyPhaseEvent([], { phase_id: 'squad_assembly', message: '组建小队' });
assert.equal(strip.length, 1);
assert.equal(strip[0].step, 'squad_assembly');
assert.equal(strip[0].icon, '👥');
assert.equal(strip[0].status, 'running');
});
test('applyPhaseEvent: completes predecessors when a new phase arrives', () => {
let strip = applyPhaseEvent([], { phase_id: 'squad_assembly', message: 'a' });
strip = applyPhaseEvent(strip, { phase_id: 'knowledge_retrieval', message: 'b' });
assert.equal(strip.length, 2);
assert.equal(strip[0].status, 'completed');
assert.equal(strip[1].status, 'running');
});
test('applyPhaseEvent: same phase refreshes label in place (no duplicate node)', () => {
let strip = applyPhaseEvent([], { phase_id: 'quality_gate', message: 'x', progress: 85 });
strip = applyPhaseEvent(strip, { phase_id: 'quality_gate', message: 'y', progress: 95 });
assert.equal(strip.length, 1);
assert.match(strip[0].label, /95%/);
});
test('applyPhaseEvent: unknown phase falls back to hourglass meta', () => {
const strip = applyPhaseEvent([], { phase_id: 'mystery_phase' });
assert.equal(strip[0].icon, '⏳');
assert.equal(strip[0].label, '任务推进');
});
test('applyPhaseEvent: prefers backend message over short label', () => {
const strip = applyPhaseEvent([], { phase_id: 'squad_assembly', message: '正在组建真实小队...' });
assert.equal(strip[0].icon, '👥');
assert.equal(strip[0].label, '正在组建真实小队...');
});
test('applyPhaseEvent: step_completed closes running entry without appending', () => {
let strip = applyPhaseEvent([], { phase_id: 'squad_assembly', message: 'a' });
strip = applyPhaseEvent(strip, { phase_id: 'step_executing', message: '撰写中' });
strip = applyPhaseEvent(strip, { phase_id: 'step_completed', message: '完成' });
assert.equal(strip.length, 2);
assert.equal(strip[0].status, 'completed');
assert.equal(strip[1].status, 'completed');
// 收口后 label 归一化为静态措辞,不再冻结"撰写中"进度中文案;原始叙述保留在 detail。
assert.equal(strip[1].label, '专员执行已完成');
assert.equal(strip[0].label, '组建小队已完成');
assert.equal(strip[1].detail, '撰写中');
});
test('applyPhaseEvent: stage_completed closes entries', () => {
let strip = applyPhaseEvent([], { phase_id: 'stage_started', message: 's' });
strip = applyPhaseEvent(strip, { phase_id: 'stage_completed', message: 'done' });
assert.equal(strip.length, 1);
assert.equal(strip[0].status, 'completed');
assert.equal(strip[0].label, '阶段启动已完成');
});
test('applyPhaseEvent: task_failed appends warning node', () => {
let strip = applyPhaseEvent([], { phase_id: 'step_executing', message: 'e' });
strip = applyPhaseEvent(strip, { phase_id: 'task_failed', message: 'boom' });
assert.equal(strip.length, 2);
assert.equal(strip[0].status, 'completed');
assert.equal(strip[1].step, 'task_failed');
assert.equal(strip[1].icon, '⚠️');
assert.equal(strip[1].status, 'running');
});
test('applyPhaseEvent: decorates label with step index / total steps', () => {
const strip = applyPhaseEvent([], {
phase_id: 'step_executing',
message: 'm',
step_index: 2,
total_steps: 3,
});
assert.match(strip[0].label, /第 2\/3 步/);
});
test('applyPhaseEvent: repeated step_executing with step_index gets separate nodes', () => {
let strip = applyPhaseEvent([], { phase_id: 'step_executing', message: '一', step_index: 1, total_steps: 3 });
strip = applyPhaseEvent(strip, { phase_id: 'step_executing', message: '二', step_index: 2, total_steps: 3 });
assert.equal(strip.length, 2);
assert.equal(strip[0].status, 'completed');
assert.match(strip[0].label, /第 1\/3 步/);
assert.equal(strip[1].status, 'running');
assert.match(strip[1].label, /第 2\/3 步/);
});
test('applyPhaseEvent: ignores event without phase payload', () => {
const strip = applyPhaseEvent([], undefined);
assert.equal(strip.length, 0);
});
test('applyPhaseEvent: step_failed marks running entry as failed (keeps step label)', () => {
let strip = applyPhaseEvent([], { phase_id: 'step_executing', message: '撰写专员撰写初稿中...' });
strip = applyPhaseEvent(strip, { phase_id: 'step_failed', message: 'pi remote runner call failed: connection refused' });
assert.equal(strip.length, 1);
assert.equal(strip[0].status, 'failed');
// 失败节点保留原步骤文案,错误细节由错误气泡承载
assert.match(strip[0].label, /撰写专员撰写初稿中/);
});
test('applyPhaseEvent: step_failed appends failed node when no running entry exists', () => {
const strip = applyPhaseEvent(
[{ step: 'squad_assembly', icon: '👥', label: '组建小队', status: 'completed' }],
{ phase_id: 'step_failed', message: '执行未成功' }
);
assert.equal(strip.length, 2);
assert.equal(strip[1].step, 'step_failed');
assert.equal(strip[1].icon, '⚠️');
assert.equal(strip[1].status, 'failed');
assert.equal(strip[1].label, '执行未成功');
});
test('applyPhaseEvent: retry of a failed step resets it to running', () => {
let strip = applyPhaseEvent([], { phase_id: 'step_executing', message: '撰写初稿中' });
strip = applyPhaseEvent(strip, { phase_id: 'step_failed', message: '执行未成功' });
strip = applyPhaseEvent(strip, { phase_id: 'step_executing', message: '重新撰写初稿中' });
assert.equal(strip.length, 1);
assert.equal(strip[0].status, 'running');
assert.equal(strip[0].label, '重新撰写初稿中');
});
test('applyPhaseEvent: stores detail from phase.message', () => {
const strip = applyPhaseEvent([], { phase_id: 'squad_assembly', message: '正在组建真实小队...' });
assert.equal(strip[0].detail, '正在组建真实小队...');
});
test('PHASE_META: document_parsing and document_summarizing are registered', () => {
assert.equal(PHASE_META.document_parsing.icon, '📄');
assert.equal(PHASE_META.document_parsing.label, '文档解析');
assert.equal(PHASE_META.document_summarizing.icon, '📝');
assert.equal(PHASE_META.document_summarizing.label, '正文总结');
const strip = applyPhaseEvent([], {
phase_id: 'document_parsing',
message: '《说明书》正在解析中…已等待 4s',
progress: 20,
});
assert.equal(strip[0].icon, '📄');
assert.match(strip[0].label, /解析中/);
});