/** * 质量校验 & 改写优化节点 - 单元测试 * 测试消息构建、响应解析等纯函数逻辑 */ 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 = `这个内容需要仔细评估... ${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(''); 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);