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(/