import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import crypto from 'node:crypto';
import { renderReportHtml } from '../skills/ksf-workshop-runtime/scripts/render-report-html.mjs';
import { prepareHtmlReport } from '../skills/ksf-workshop-runtime/scripts/save-report.mjs';
const task = () => ({ title: '隔离测试:面体与酱体的取舍', taskKey: 'test:proposal', version: 1, stageCode: 'S5', artifactType: 'concept',
rawText: '仅用于测试,不是真实调研。', sourceIds: [], pendingQuestions: ['哪项实测会改变建议?'],
report: { executive: ['这是样本内的待验证判断,不代表总体需求。'], delivery: { schema: 'ksf.workshop-delivery.v1', moduleId: 'D2-08', kind: 'proposal', contentStatus: 'ready', outputs: ['proposal'] },
chapters: [{ id: 'product-proposal', title: '本轮比较问题', sections: [{ kind: 'findings', data: [{ conclusion: '改动与代价须一起验证', attitude: '解释不能超出依据。', evidence: 'e1' }] }] }],
evidence: { e1: { claim: '单条原话仅作线索', sourceId: 'test-source', quotes: [{ text: '测试原话\n第二段内容。', author: '测试作者', ref: '测试平台|观察日2026-11-11; https://example.org/review/1' }] } } } });
test('HTML保留真实输入的摘要、章节、解释、逐字证言、来源及待核问题,不编造统计或确认', () => {
const input = task(), original = structuredClone(input), html = renderReportHtml(input);
for (const text of [input.title, input.report.executive[0], input.report.chapters[0].title, '解释不能超出依据。', '测试原话\n第二段内容。', input.pendingQuestions[0], '测试平台|观察日2026-11-11']) assert.ok(html.includes(text), text);
assert.match(html, /href="https:\/\/example.org\/review\/1"/);
assert.match(html, /课堂过程成果 · 待员工核对/);
assert.doesNotMatch(html, /';
input.report.evidence.e1.quotes[0].text = '';
input.report.evidence.e1.quotes[0].ref = 'javascript:alert(1)';
const html = renderReportHtml(input);
assert.ok(html.includes('<img src=x onerror=alert(1)>'));
assert.doesNotMatch(html, /