| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /**
- * 质量校验 & 改写优化节点 - 单元测试
- * 测试消息构建、响应解析等纯函数逻辑
- */
- import { strict as assert } from 'assert';
- // ====== 模拟数据 ======
- // 模拟 quality-check 响应
- const mockQualityPassResponse = JSON.stringify({
- overallScore: 92,
- overallAssessment: "全书内容质量优秀,章节逻辑清晰,通顺性好",
- passedChapters: [1, 2, 3, 4, 5],
- failedChapters: []
- });
- const mockQualityFailResponse = JSON.stringify({
- overallScore: 68,
- overallAssessment: "部分章节存在逻辑和跑题问题,需要改写",
- passedChapters: [1, 4],
- failedChapters: [
- {
- chapterNumber: 2,
- chapterTitle: "变量与数据类型",
- scores: { fluency: 20, logic: 12, relevance: 15, completeness: 18 },
- totalScore: 65,
- issues: [
- {
- dimension: "logic",
- severity: "high",
- description: "变量作用域的解释与前面声明周期的概念矛盾",
- location: "第3段",
- suggestion: "统一变量作用域的定义,参考MDN标准术语"
- }
- ],
- rewriteInstructions: "修复逻辑矛盾,统一变量作用域概念"
- },
- {
- chapterNumber: 3,
- chapterTitle: "控制流",
- scores: { fluency: 15, logic: 14, relevance: 10, completeness: 14 },
- totalScore: 53,
- issues: [
- {
- dimension: "relevance",
- severity: "high",
- description: "大段内容在讲数据结构而非控制流,严重跑题",
- location: "第2段开始",
- suggestion: "裁减数据结构内容,聚焦if/switch/loop等控制流语句"
- },
- {
- dimension: "fluency",
- severity: "medium",
- description: "多处句子过长,可读性差",
- location: "全文多处",
- suggestion: "拆分长句,每句不超过30字"
- }
- ],
- rewriteInstructions: "去除跑题内容,拆分长句,精简文笔"
- }
- ]
- });
- // 模拟质量校验带 think 标签的响应
- const mockQualityWithThink = `<think>这个内容需要仔细评估...</think>
- ${JSON.stringify({
- overallScore: 75,
- overallAssessment: "中等水平,第3章有轻微问题",
- passedChapters: [1, 2, 4, 5],
- failedChapters: [
- {
- chapterNumber: 3,
- chapterTitle: "数组与集合",
- scores: { fluency: 18, logic: 16, relevance: 15, completeness: 17 },
- totalScore: 66,
- issues: [],
- rewriteInstructions: "需要增加代码示例"
- }
- ]
- })}`;
- // 模拟 rewrite 响应
- const mockRewriteResponse = JSON.stringify({
- chapters: [
- {
- number: 2,
- title: "变量与数据类型",
- rewrittenContent: "# 第2章 变量与数据类型\n\n修正后的内容...",
- changesSummary: "修复了变量作用域的矛盾表述"
- }
- ]
- });
- // ====== 解析函数(从源文件摘取核心逻辑用于测试)======
- function parseJSONResponse(raw: string) {
- let cleaned = raw.trim();
- const thinkEnd = cleaned.indexOf('</think>');
- if (thinkEnd !== -1) cleaned = cleaned.substring(thinkEnd + 8).trim();
- const match = cleaned.match(/\{[\s\S]*\}/);
- return match ? JSON.parse(match[0]) : null;
- }
- function parseQualityCheckResponse(response: string) {
- try {
- const data = parseJSONResponse(response);
- if (!data) return null;
- return {
- overallScore: data.overallScore || 0,
- overallAssessment: data.overallAssessment || '',
- passedChapters: data.passedChapters || [],
- failedChapters: (data.failedChapters || []).map((fc: any) => ({
- chapterNumber: fc.chapterNumber || 0,
- chapterTitle: fc.chapterTitle || '',
- scores: {
- fluency: fc.scores?.fluency ?? 0,
- logic: fc.scores?.logic ?? 0,
- relevance: fc.scores?.relevance ?? 0,
- completeness: fc.scores?.completeness ?? 0,
- },
- totalScore: fc.totalScore ?? 0,
- issues: fc.issues || [],
- rewriteInstructions: fc.rewriteInstructions || '请重新撰写本章',
- })),
- };
- } catch { return null; }
- }
- function parseRewriteResponse(response: string) {
- try {
- const data = parseJSONResponse(response);
- if (!data) return null;
- const chapters = data.chapters || data;
- if (Array.isArray(chapters) && chapters.length > 0) {
- return {
- title: chapters[0].title || '',
- rewrittenContent: chapters[0].rewrittenContent || chapters[0].content || '',
- changesSummary: chapters[0].changesSummary || '',
- };
- }
- return null;
- } catch { return null; }
- }
- // ====== 测试用例 ======
- let passed = 0, failed = 0;
- function test(name: string, fn: () => void) {
- try {
- fn();
- console.log(` ✅ ${name}`);
- passed++;
- } catch (e: any) {
- console.log(` ❌ ${name}: ${e.message}`);
- failed++;
- }
- }
- console.log('\n📋 质量校验 & 改写优化节点 - 单元测试\n');
- // ---- 质量校验响应解析 ----
- console.log('📝 质量校验响应解析:');
- test('解析全部通过的响应', () => {
- const result = parseQualityCheckResponse(mockQualityPassResponse);
- assert.ok(result, '解析结果不能为空');
- assert.strictEqual(result.overallScore, 92);
- assert.strictEqual(result.passedChapters.length, 5);
- assert.strictEqual(result.failedChapters.length, 0);
- assert.strictEqual(result.overallAssessment.includes('优秀'), true);
- });
- test('解析有不合格章节的响应', () => {
- const result = parseQualityCheckResponse(mockQualityFailResponse);
- assert.ok(result, '解析结果不能为空');
- assert.strictEqual(result.overallScore, 68);
- assert.strictEqual(result.failedChapters.length, 2);
- assert.strictEqual(result.failedChapters[0].chapterNumber, 2);
- assert.strictEqual(result.failedChapters[0].scores.logic, 12);
- assert.strictEqual(result.failedChapters[0].issues[0].severity, 'high');
- assert.strictEqual(result.failedChapters[1].issues.length, 2);
- });
- test('解析带 think 标签的响应', () => {
- const result = parseQualityCheckResponse(mockQualityWithThink);
- assert.ok(result, 'think标签不应影响解析');
- assert.strictEqual(result.overallScore, 75);
- assert.strictEqual(result.failedChapters.length, 1);
- });
- test('解析空字符串', () => {
- const result = parseQualityCheckResponse('');
- assert.strictEqual(result, null, '空字符串应返回null');
- });
- test('解析非法JSON', () => {
- const result = parseQualityCheckResponse('这不是JSON');
- assert.strictEqual(result, null, '非法JSON应返回null');
- });
- // ---- 改写响应解析 ----
- console.log('\n📝 改写响应解析:');
- test('解析正常改写响应', () => {
- const result = parseRewriteResponse(mockRewriteResponse);
- assert.ok(result, '解析结果不能为空');
- assert.strictEqual(result.title, '变量与数据类型');
- assert.ok(result.rewrittenContent.includes('修正后'), '应包含改写内容');
- assert.ok(result.changesSummary.length > 0, '应有改动摘要');
- });
- test('解析空响应', () => {
- const result = parseRewriteResponse('');
- assert.strictEqual(result, null, '空响应应返回null');
- });
- // ---- 质量评分阈值 ----
- console.log('\n📝 评分阈值逻辑:');
- test('总分≥80为通过', () => {
- const passResult = parseQualityCheckResponse(mockQualityPassResponse)!;
- const allPassed = passResult.overallScore >= 80 && passResult.failedChapters.length === 0;
- assert.strictEqual(allPassed, true);
- });
- test('总分<80且存在不合格章节为不通过', () => {
- const failResult = parseQualityCheckResponse(mockQualityFailResponse)!;
- const needsRewrite = failResult.overallScore < 80 || failResult.failedChapters.length > 0;
- assert.strictEqual(needsRewrite, true);
- });
- // ---- 改写指令完整性 ----
- console.log('\n📝 改写指令完整性:');
- test('不合格章节必须有改写指令', () => {
- const result = parseQualityCheckResponse(mockQualityFailResponse)!;
- for (const fc of result.failedChapters) {
- assert.ok(fc.rewriteInstructions.length > 0,
- `第${fc.chapterNumber}章缺少改写指令`);
- }
- });
- test('每条问题必须有四个维度之一', () => {
- const validDimensions = ['fluency', 'logic', 'relevance', 'completeness'];
- const result = parseQualityCheckResponse(mockQualityFailResponse)!;
- for (const fc of result.failedChapters) {
- for (const issue of fc.issues) {
- assert.ok(validDimensions.includes(issue.dimension),
- `无效维度: ${issue.dimension}`);
- }
- }
- });
- // ====== 结果汇总 ======
- console.log(`\n${'='.repeat(40)}`);
- console.log(`测试结果: ${passed} 通过, ${failed} 失败, ${passed + failed} 总计`);
- console.log(`${'='.repeat(40)}\n`);
- process.exit(failed > 0 ? 1 : 0);
|