import { describe, expect, it } from 'vitest'; import { buildSessionProgressView } from '@/lib/session-progress'; import type { ProcessArtifact, ProcessEvent, ProcessRun } from '@/types'; describe('session progress view builder', () => { it('selects the latest active root run for the current session and builds its run tree', () => { const processRuns: ProcessRun[] = [ { run_id: 'old-root', parent_run_id: null, session_id: 'web:current', actor_type: 'agent', actor_id: 'main', actor_name: 'Main Agent', title: '旧任务', status: 'done', started_at: '2026-05-22T08:00:00.000Z', finished_at: '2026-05-22T08:05:00.000Z', }, { run_id: 'latest-root', parent_run_id: null, session_id: 'web:current', actor_type: 'agent', actor_id: 'main', actor_name: 'Main Agent', title: '销售数据分析报告生成', status: 'running', started_at: '2026-05-22T09:00:00.000Z', metadata: { step_index: 3, step_total: 5, }, }, { run_id: 'collect-data', parent_run_id: 'latest-root', session_id: 'web:current', actor_type: 'agent', actor_id: 'collector', actor_name: 'Data Agent', title: '收集销售数据', status: 'done', started_at: '2026-05-22T09:01:00.000Z', finished_at: '2026-05-22T09:03:00.000Z', summary: '已获取 Q1 销售数据', }, { run_id: 'clean-data', parent_run_id: 'latest-root', session_id: 'web:current', actor_type: 'agent', actor_id: 'cleaner', actor_name: 'Cleaning Agent', title: '数据清洗与预处理', status: 'running', started_at: '2026-05-22T09:04:00.000Z', }, { run_id: 'other-root', parent_run_id: null, session_id: 'web:other', actor_type: 'agent', actor_id: 'main', actor_name: 'Main Agent', title: '其他会话任务', status: 'running', started_at: '2026-05-22T10:00:00.000Z', }, ]; const processEvents: ProcessEvent[] = [ { event_id: 'evt-clean', run_id: 'clean-data', parent_run_id: 'latest-root', kind: 'run_progress', actor_type: 'agent', actor_id: 'cleaner', actor_name: 'Cleaning Agent', text: '清洗缺失值、异常值,统一格式', created_at: '2026-05-22T09:05:00.000Z', }, ]; const processArtifacts: ProcessArtifact[] = [ { artifact_id: 'artifact-json', run_id: 'collect-data', actor_type: 'agent', actor_id: 'collector', actor_name: 'Data Agent', title: '销售数据', artifact_type: 'json', data: { rows: 120 }, created_at: '2026-05-22T09:03:30.000Z', }, { artifact_id: 'artifact-markdown', run_id: 'clean-data', actor_type: 'agent', actor_id: 'cleaner', actor_name: 'Cleaning Agent', title: '清洗说明', artifact_type: 'markdown', content: '已完成数据标准化。', created_at: '2026-05-22T09:05:30.000Z', }, { artifact_id: 'artifact-other-session', run_id: 'other-root', actor_type: 'agent', actor_id: 'main', title: '其他会话产物', artifact_type: 'text', content: '不应出现', created_at: '2026-05-22T10:01:00.000Z', }, ]; const view = buildSessionProgressView({ sessionId: 'web:current', processRuns, processEvents, processArtifacts, locale: 'zh-CN', }); expect(view).not.toBeNull(); expect(view?.rootRunId).toBe('latest-root'); expect(view?.title).toBe('销售数据分析报告生成'); expect(view?.progress).toMatchObject({ value: 3, max: 5, percent: 60, label: '运行中:3 / 5 步', }); expect(view?.steps.map((step) => step.runId)).toEqual(['collect-data', 'clean-data', 'latest-root']); expect(view?.steps.find((step) => step.runId === 'clean-data')?.description).toBe('清洗缺失值、异常值,统一格式'); expect(view?.artifactTypeSummaries).toEqual([ { type: 'json', count: 1, label: 'JSON' }, { type: 'markdown', count: 1, label: 'Markdown' }, ]); expect(view?.artifacts.map((artifact) => artifact.artifactId)).toEqual(['artifact-markdown', 'artifact-json']); }); it('falls back to completed child run counts when no explicit progress metadata exists', () => { const processRuns: ProcessRun[] = [ { run_id: 'root', parent_run_id: null, session_id: 'web:current', actor_type: 'agent', actor_id: 'main', actor_name: 'Main Agent', title: '生成总结', status: 'running', started_at: '2026-05-22T09:00:00.000Z', }, { run_id: 'done-child', parent_run_id: 'root', session_id: 'web:current', actor_type: 'agent', actor_id: 'writer', actor_name: 'Writer', title: '整理结果', status: 'done', started_at: '2026-05-22T09:01:00.000Z', finished_at: '2026-05-22T09:02:00.000Z', }, { run_id: 'running-child', parent_run_id: 'root', session_id: 'web:current', actor_type: 'agent', actor_id: 'reviewer', actor_name: 'Reviewer', title: '复核结果', status: 'running', started_at: '2026-05-22T09:03:00.000Z', }, ]; const view = buildSessionProgressView({ sessionId: 'web:current', processRuns, processEvents: [], processArtifacts: [], locale: 'zh-CN', }); expect(view?.progress).toMatchObject({ value: 1, max: 2, percent: 50, label: '已完成 1 / 2 步', }); }); });