import { test } from 'node:test' import assert from 'node:assert/strict' import { canDownloadItem, FILE_PROGRESS_KEYS, hasMeaningfulProgress, mergeHistoryItemPreservingLiveProgress, } from '../src/shared/merge-live-progress.ts' interface HistoryItem { taskId: number resultId?: number fileReady?: boolean fileStatus?: string fileProgressPercent?: number fileProgressCurrent?: number fileProgressTotal?: number fileProgressMessage?: string downloadUrl?: string sourceFilename?: string } const historyMissingProgress: HistoryItem = { taskId: 1, resultId: 100, fileStatus: 'RUNNING', sourceFilename: 'source.xlsx', } const liveWithProgress: HistoryItem = { taskId: 1, resultId: 100, fileStatus: 'RUNNING', fileProgressPercent: 45, fileProgressCurrent: 9, fileProgressTotal: 20, fileProgressMessage: 'LLM 处理中', } test('merge keeps live progress when history missing file progress', () => { const merged = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, liveWithProgress) assert.equal(merged.fileProgressPercent, 45, 'history 缺失进度时保留 live 的百分比') assert.equal(merged.fileProgressCurrent, 9) assert.equal(merged.fileProgressTotal, 20) assert.equal(merged.fileProgressMessage, 'LLM 处理中') assert.equal(merged.sourceFilename, 'source.xlsx', '非进度字段保留 history') }) test('merge incoming wins when live has progress', () => { const history = { ...historyMissingProgress, fileProgressPercent: 10, fileProgressMessage: '旧消息' } const merged = mergeHistoryItemPreservingLiveProgress(history, liveWithProgress) assert.equal(merged.fileProgressPercent, 45, 'live 有有效进度时以 live 为准') assert.equal(merged.fileProgressMessage, 'LLM 处理中') }) test('merge does not keep live when history is downloadable', () => { const history: HistoryItem = { taskId: 1, resultId: 100, fileReady: true, fileStatus: 'SUCCESS', fileProgressPercent: 100, downloadUrl: 'https://dl.example/a.xlsx', } const merged = mergeHistoryItemPreservingLiveProgress(history, liveWithProgress) assert.equal(merged.fileProgressPercent, 100, '可下载时保留 history 的终态进度') assert.equal(merged.downloadUrl, 'https://dl.example/a.xlsx') assert.equal(merged.fileReady, true) }) test('merge without live returns history as-is', () => { const merged = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, undefined) assert.equal(merged, historyMissingProgress, '无 live 时原样返回 history') assert.equal(merged.fileProgressPercent, undefined) }) test('merge partial fields keeps only present live values', () => { const live: HistoryItem = { taskId: 1, resultId: 100, fileStatus: 'RUNNING', fileProgressPercent: 30 } const merged = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, live) assert.equal(merged.fileProgressPercent, 30) assert.equal(merged.fileProgressCurrent, undefined, 'live 缺失的字段不写入') assert.equal(merged.fileProgressTotal, undefined) assert.equal(merged.fileStatus, 'RUNNING') }) test('merge ignores meaningless progress values', () => { assert.equal(hasMeaningfulProgress('fileProgressPercent', 0), false, '0% 不算有效进度') assert.equal(hasMeaningfulProgress('fileProgressPercent', -1), false) assert.equal(hasMeaningfulProgress('fileProgressPercent', 5), true) assert.equal(hasMeaningfulProgress('fileProgressCurrent', 0), true, '0 是合法计数') assert.equal(hasMeaningfulProgress('fileProgressMessage', null), false) assert.equal(hasMeaningfulProgress('fileProgressMessage', undefined), false) const live: HistoryItem = { taskId: 1, resultId: 100, fileProgressPercent: 0 } const merged = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, live) assert.equal(merged.fileProgressPercent, undefined, '0% 不覆盖 history') }) test('merge preserves fileReady flag from live', () => { const live: HistoryItem = { taskId: 1, resultId: 100, fileReady: true, fileProgressPercent: 100 } const merged = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, live) assert.equal(merged.fileReady, true) assert.equal(merged.fileProgressPercent, 100) }) test('merge is idempotent', () => { const once = mergeHistoryItemPreservingLiveProgress(historyMissingProgress, liveWithProgress) const twice = mergeHistoryItemPreservingLiveProgress(once, liveWithProgress) assert.deepEqual(twice, once, '重复合并结果一致') }) test('file progress keys cover the live progress contract', () => { assert.deepEqual( [...FILE_PROGRESS_KEYS].sort(), [ 'fileProgressCurrent', 'fileProgressMessage', 'fileProgressPercent', 'fileProgressTotal', 'fileReady', 'fileStatus', ].sort(), ) }) test('canDownloadItem requires result and ready state', () => { assert.equal(canDownloadItem({ resultId: 1, fileReady: true }), true) assert.equal(canDownloadItem({ resultId: 1, downloadUrl: 'https://x' }), true) assert.equal(canDownloadItem({ resultId: 1 }), false) assert.equal(canDownloadItem({ fileReady: true }), false, '无 resultId 不可下载') assert.equal(canDownloadItem(null), false) assert.equal(canDownloadItem(undefined), false) })