2026-07-10 23:56:06 +08:00
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { parse as parseSfc } from '@vue/compiler-sfc'
2026-07-27 10:03:52 +08:00
import { dataProcessDisplayStatus } from '../src/utils/dataProcessStatus.js'
2026-07-10 23:56:06 +08:00
const scriptDir = path . dirname ( fileURLToPath ( import . meta . url ) )
2026-07-23 15:10:13 +08:00
const sourceRoot = path . resolve ( scriptDir , '../src' )
const viewPath = path . join ( sourceRoot , 'views/data-process/DataProcessListView.vue' )
const [ source , apiSource ] = await Promise . all ( [
readFile ( viewPath , 'utf8' ) ,
readFile ( path . join ( sourceRoot , 'api/modules/dataProcess.ts' ) , 'utf8' ) ,
] )
2026-07-10 23:56:06 +08:00
const { descriptor , errors } = parseSfc ( source , { filename : viewPath } )
assert . equal ( errors . length , 0 , ` 数据处理任务列表模板无法解析: ${ errors [ 0 ] } ` )
assert . ok ( descriptor . template ? . content . trim ( ) , '数据处理任务列表缺少可渲染模板' )
assert . match ( source , /:data="dataList"/ , '任务表格必须直接展示完整任务数据' )
assert . doesNotMatch ( source , /activeTab|filteredDataList/ , '不应保留状态切换筛选逻辑' )
assert . doesNotMatch ( source , /全部任务|处理中|已完成/ , '不应保留状态切换按钮文案' )
assert . doesNotMatch ( source , /capsule-tabs|capsule-tab-item/ , '不应保留状态切换专用样式' )
2026-07-23 15:10:13 +08:00
assert . ok ( source . includes ( "getDataProcessTasks({ page: 1, page_size: 200" ) , '列表没有通过真实 API 分页加载任务' )
2026-07-28 10:56:05 +08:00
assert . match ( source , /usePolling\([\s\S]*?\(\) => loadData\(true\)[\s\S]*?3000/ , '列表没有静默轮询运行中任务' )
assert . match (
source ,
/item\.status === 'running'[\s\S]*?item\.preview_status === 'running'[\s\S]*?item\.preview_status === 'queued'[\s\S]*?startPolling\(\)[\s\S]*?stopPolling\(\)/ ,
'列表轮询没有同时覆盖后台生成与后台切分' ,
)
2026-07-23 15:10:13 +08:00
assert . doesNotMatch ( source , /toolbar-extra|refreshData|refreshing|fa-refresh/ , '列表不应显示手动刷新按钮' )
assert . ok ( source . includes ( 'ElMessageBox.confirm(' ) , '删除任务前缺少二次确认' )
assert . ok ( source . includes ( 'await deleteDataProcessTask(row.id)' ) , '删除操作没有调用真实 API' )
assert . ok ( ! source . includes ( 'TODO: 接入真实接口' ) , '列表仍包含本地 Mock 任务' )
assert . ok ( source . includes ( 'const dataList = ref<DataProcessTask[]>([])' ) , '任务列表必须以空数组初始化并等待真实 API 数据' )
2026-07-27 09:50:26 +08:00
assert . match ( source , /label="文档数量"[\s\S]*?documentCountLabel\(row as DataProcessTask\)/ , '任务列表没有展示真实文档数量' )
assert . match ( source , /function documentCountLabel\(task: DataProcessTask\)[\s\S]*?`\$\{safeCount\(task\.source_file_count\)\} 个`/ , '文档数量没有使用 source_file_count 或缺少单位' )
assert . match ( source , /label="生成个数"[\s\S]*?generatedCountLabel\(row as DataProcessTask\)/ , '任务列表没有展示真实生成数量' )
assert . match ( source , /function generatedCountLabel\(task: DataProcessTask\)[\s\S]*?`\$\{safeCount\(task\.output_count\)\} 条`/ , '生成数量没有使用 output_count 或缺少单位' )
assert . doesNotMatch ( source , /label="源数据集"|label="输出数据集"/ , '任务列表仍使用旧的数据集列标题' )
2026-07-27 10:03:52 +08:00
assert . match ( source , /import \{ dataProcessDisplayStatus \} from '@\/utils\/dataProcessStatus\.js'/ , '任务列表没有复用数据处理六态映射' )
assert . match ( source , /dataProcessDisplayStatus\(row as DataProcessTask\)\.type[\s\S]*?dataProcessDisplayStatus\(row as DataProcessTask\)\.label/ , '任务列表状态标签没有使用公共映射' )
const statusCases = [
[ { status : 'pending' } , { label : '待生成' , type : 'info' } ] ,
2026-07-28 10:56:05 +08:00
[ { status : 'pending' , preview _status : 'queued' } , { label : '切分中' , type : 'primary' } ] ,
[ { status : 'pending' , preview _status : 'running' } , { label : '切分中' , type : 'primary' } ] ,
2026-07-27 10:03:52 +08:00
[ { status : 'running' } , { label : '生成中' , type : 'primary' } ] ,
2026-07-28 10:56:05 +08:00
[ { status : 'completed' , results _confirmed : false } , { label : '待确认' , type : 'warning' } ] ,
2026-07-27 10:03:52 +08:00
[ { status : 'completed' } , { label : '已生成' , type : 'warning' } ] ,
[ { status : 'completed' , output _datasets : [ { id : 'old-train' } ] } , { label : '已生成' , type : 'warning' } ] ,
[ { status : 'completed' , output _dataset _id : 'current-train' } , { label : '已发布' , type : 'success' } ] ,
[ { status : 'failed' } , { label : '生成失败' , type : 'danger' } ] ,
[ { status : 'stopped' } , { label : '已停止' , type : 'info' } ] ,
]
for ( const [ task , expected ] of statusCases ) {
assert . deepEqual ( dataProcessDisplayStatus ( task ) , expected , ` 任务六态映射错误: ${ task . status } ` )
2026-07-27 09:50:26 +08:00
}
2026-07-10 23:56:06 +08:00
2026-07-28 10:56:05 +08:00
assert . match ( source , /task\.status === 'completed' && task\.results_confirmed !== false[\s\S]*?'data-process-detail'[\s\S]*?'data-process-workflow'/ , '详情入口没有区分已确认详情与未完成工作流' )
assert . doesNotMatch ( source , /stopDataProcess|\/stop\b|停止生成|停止任务/ , '列表不应暴露删除以外的任务终止入口' )
2026-07-23 15:10:13 +08:00
assert . match ( apiSource , /export (?:async )?function getDataProcessTasks/ , 'API 模块缺少任务列表方法' )
assert . match ( apiSource , /export const deleteDataProcessTask/ , 'API 模块缺少任务删除方法' )
assert . ok ( apiSource . includes ( "get<DataProcessPage<DataProcessTask>>('/data-process', params)" ) , '任务列表 API 路径或分页契约不正确' )
assert . ok ( apiSource . includes ( '`/data-process/${encodeURIComponent(taskId)}`' ) , '任务详情资源路径没有安全编码任务 ID' )
console . log ( '数据处理任务列表真实 API 回归检查通过' )