import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { COURSE_ASSESSMENT_COLUMNS, courseAssessmentRows } from '../skills/ksf-workshop-runtime/scripts/course-assessment.mjs'; import { renderReportHtml } from '../skills/ksf-workshop-runtime/scripts/render-report-html.mjs'; import { saveAndVerifyReport } from '../skills/ksf-workshop-runtime/scripts/save-report.mjs'; import { DELIVERY_SCHEMA } from '../skills/ksf-workshop-runtime/scripts/report-contract.mjs'; const section = rows => ({ id: 'course-priority', kind: 'table', data: { cols: [...COURSE_ASSESSMENT_COLUMNS], rows } }); const valid = () => section([ ['测试候选A', 3, 2, 2, '证据为隔离样例;工艺条件仅测试;商业价值需本组复核', '第1'], ['测试候选B', 2, 1, 2, '测试可做性1分,保持不进入前三', '不进入前三'], ['测试候选C', 2, null, 2, '工艺资料缺失,列未知项;不是低分', '先补证'], ]); const task = table => ({ artifactType: 'assessment_report', stageCode: 'S4', version: 1, title: '隔离样例:课程排序', report: { delivery: { schema: DELIVERY_SCHEMA, moduleId: 'D2-05', kind: 'validation', contentStatus: 'ready', outputs: ['validation'] }, chapters: [{ id: 'opportunity-validation', title: '三维评分', sections: [table] }] } }); test('课程三维分别为1–3,未知保持未知,资格不是自动前三或总分', () => { const rows = courseAssessmentRows(valid()); assert.deepEqual(rows.map(row => row.status), ['可参与排序', '不进入前三', '未知项待补']); assert.deepEqual(rows[2].scores, [2, null, 2]); assert.equal(Object.hasOwn(rows[0], 'total'), false); const displayTable = valid(); displayTable.data.cols[0] = '机\u2009会'; assert.equal(courseAssessmentRows(displayTable).length, 3); }); test('一维1分或未知时不能按样卡进入前三,越界分值和缺依据不通过', () => { for (const scores of [[2, 1, 2], [2, null, 2], [0, 2, 2], [4, 2, 2], [2.5, 2, 2]]) { assert.throws(() => courseAssessmentRows(section([['测试', ...scores, '测试依据', '第3']])), { code: 'COURSE_ASSESSMENT_INVALID' }); } assert.throws(() => courseAssessmentRows(section([['测试', 2, 2, 2, '', '第1']])), { code: 'COURSE_ASSESSMENT_INVALID' }); assert.equal(courseAssessmentRows({ kind: 'table', data: { rows: [['旧表']] } }), null); }); test('独立HTML从同一评分表展示三维、未知及说明,不生成百分制总分', () => { const html = renderReportHtml(task(valid())); assert.match(html, /data-course-assessment/);assert.match(html, /待确认/);assert.match(html, /不进入前三/); assert.match(html, /商业价值需本组复核/);assert.doesNotMatch(html, /总分<\/th>/); const escaped = valid(); escaped.data.rows[0][0] = ''; assert.doesNotMatch(renderReportHtml(task(escaped)), / { let calls = 0; const bad = task(section([['测试', 2, 1, 2, '可做性为1', '第3']])); await assert.rejects(saveAndVerifyReport(bad, { requestId: 'invalid-course', invoke: () => { calls++; } }), { code: 'COURSE_ASSESSMENT_INVALID' }); assert.equal(calls, 0); }); test('final操作手册采用v3全文,13段提示词原样,页内锚点不被远程base改写', () => { const source = fs.readFileSync(new URL('../docs/workshop-v3-alignment-20261112/source-manual-v3.html', import.meta.url), 'utf8'); const final = fs.readFileSync(new URL('../docs/workshop-quality-r13-20261110/final-康师傅工作坊操作手册.html', import.meta.url), 'utf8'); assert.equal(final.replace(/