import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import path from 'node:path' const scriptDir = path.dirname(fileURLToPath(import.meta.url)) const sourceRoot = path.resolve(scriptDir, '../src') const evalViewDir = path.join(sourceRoot, 'views/eval') const [ createSource, taskStepSource, ruleStepSource, basicMetricStepSource, startStepSource, dimensionFieldsSource, listSource, routerSource, apiSource, ] = await Promise.all([ readFile(path.join(evalViewDir, 'EvalCreateView.vue'), 'utf8'), readFile(path.join(evalViewDir, 'create/EvalTaskSetupStep.vue'), 'utf8'), readFile(path.join(evalViewDir, 'create/EvalRuleSetupStep.vue'), 'utf8'), readFile(path.join(evalViewDir, 'create/BasicMetricSetupStep.vue'), 'utf8'), readFile(path.join(evalViewDir, 'create/StartEvalStep.vue'), 'utf8'), readFile(path.join(evalViewDir, 'create/DimensionFormFields.vue'), 'utf8'), readFile(path.join(evalViewDir, 'EvalView.vue'), 'utf8'), readFile(path.join(sourceRoot, 'router/index.ts'), 'utf8'), readFile(path.join(sourceRoot, 'api/modules/eval.ts'), 'utf8'), ]) function extractRouteBlock(source, routePath) { const pathPattern = new RegExp(`path:\\s*['"]${routePath.replaceAll('/', '\\/')}['"]`) const pathIndex = source.search(pathPattern) assert.notEqual(pathIndex, -1, `未找到路由 ${routePath}`) const openBrace = source.lastIndexOf('{', pathIndex) assert.notEqual(openBrace, -1, `路由 ${routePath} 缺少左花括号`) let depth = 0 for (let index = openBrace; index < source.length; index += 1) { if (source[index] === '{') depth += 1 if (source[index] === '}') depth -= 1 if (depth === 0) return source.slice(openBrace + 1, index) } assert.fail(`路由 ${routePath} 缺少右花括号`) } const createDimensionRoute = extractRouteBlock(routerSource, 'model-eval/dimension/create') assert.match( createDimensionRoute, /redirect:\s*['"]\/model-eval\/create['"]/, '独立创建维度入口必须收敛到新建评测向导', ) assert.doesNotMatch( createDimensionRoute, /DimensionCreateView/, '独立创建维度路由不应继续加载旧创建页', ) const editDimensionRoute = extractRouteBlock(routerSource, 'model-eval/dimension/:id/edit') assert.match( editDimensionRoute, /DimensionCreateView\.vue/, '编辑维度路由必须继续复用 DimensionCreateView.vue', ) assert.doesNotMatch(listSource, /评测维度/, '模型评测页不应保留评测维度页签及内容') assert.doesNotMatch(listSource, /activeTab\s*===\s*['"]dimensions['"]/, '模型评测页不应保留评测维度状态分支') assert.doesNotMatch(listSource, /getDimensionList|deleteDimension|dimensionList/, '模型评测页不应继续加载维度列表数据') assert.match( apiSource, /createDimension\s*=\s*\([^)]*\)\s*=>\s*[\s\S]*?post<\{\s*id:\s*string\s*\|\s*number\s*\}>\(['"]\/dimension['"]\s*,\s*data\)/, 'createDimension 必须声明返回包含 string | number 类型 id', ) assert.match(createSource, /EvalTaskSetupStep/, '向导缺少任务配置子组件') assert.match(createSource, /EvalRuleSetupStep/, '向导缺少评测规则子组件') assert.match(createSource, /BasicMetricSetupStep/, '向导缺少基础评测指标子组件') assert.match(createSource, /StartEvalStep/, '向导缺少开始评测确认子组件') assert.match( createSource, /任务配置[\s\S]*?大模型评测指标[\s\S]*?基础评测指标[\s\S]*?开始评测/, '评测创建向导必须按“任务配置、大模型评测指标、基础评测指标、开始评测”定义四个步骤', ) assert.match( ruleStepSource, /:show-description=["']false["']/, '大模型评测指标步骤不应显示重复的规则描述输入框', ) assert.match( ruleStepSource, /:show-status-settings=["']false["']/, '大模型评测指标步骤不应显示维度管理状态字段', ) assert.match( ruleStepSource, /LLM_METRIC_TYPES[^=]*=\s*\[['"]classification['"],\s*['"]metric['"]\][\s\S]*?:allowed-types=["']LLM_METRIC_TYPES["']/, '大模型评测指标步骤只应提供依赖大模型的分类与评分指标', ) assert.match( dimensionFieldsSource, /v-if=["']showDescription["'][^>]*label=["']描述["']/, '共享维度字段必须支持按场景隐藏描述输入框', ) assert.match( dimensionFieldsSource, /score_min[\s\S]*?score_max[\s\S]*?pass_threshold[\s\S]*?Math\.min[\s\S]*?Math\.max/, '指标型评分区间变化时必须把通过阈值约束在合法范围内', ) assert.match( dimensionFieldsSource, /prop=["']score_min["'][\s\S]*?:max=["'][^"']*score_max[^"']*["'][\s\S]*?prop=["']score_max["'][\s\S]*?:min=["'][^"']*score_min[^"']*["']/, '评分最小值和最大值输入必须具备交叉边界约束', ) assert.match(createSource, /currentStep\s*=\s*ref\(0\)/, '评测创建向导缺少当前步骤状态') assert.match(createSource, /class=["']custom-wizard-steps["']/, '评测向导未复用数据处理的自定义步骤条结构') assert.match(createSource, /class=["']step-connector["']/, '评测向导步骤条缺少连接线') assert.match(createSource, /:aria-current=/, '步骤条缺少当前步骤的可访问性绑定') assert.match(createSource, /currentStep === index \? 'step' : undefined/, '步骤条当前步骤语义不正确') assert.doesNotMatch(createSource, /\s*开始评测\s*