| 1 | import { describe, expect, it } from 'vitest' |
| 2 | import { |
| 3 | canUseSourcePlanDirectly, |
| 4 | mapSourcePlanToOutlineItems, |
| 5 | sourcePlanFromSkeletonRows |
| 6 | } from '../../../src/main/generation/source-plan' |
| 7 | |
| 8 | describe('source page skeleton planning', () => { |
| 9 | it('normalizes database rows into a source plan', () => { |
| 10 | const sourcePlan = sourcePlanFromSkeletonRows([ |
| 11 | { |
| 12 | page_number: 1, |
| 13 | title: '第一篇:认知篇', |
| 14 | role: 'chapter-divider', |
| 15 | source_document_path: '/docs/source.md', |
| 16 | source_document_name: 'source.md', |
| 17 | source_heading: '# 第一篇:认知篇', |
| 18 | heading_level: 1, |
| 19 | line_start: 10, |
| 20 | line_end: 28, |
| 21 | reason: 'major # heading after the topic', |
| 22 | confidence: 'high' |
| 23 | } |
| 24 | ]) |
| 25 | |
| 26 | expect(sourcePlan).toMatchObject({ |
| 27 | confidence: 'high', |
| 28 | sourceDocumentPath: '/docs/source.md', |
| 29 | pageSkeleton: [ |
| 30 | { |
| 31 | pageNumber: 1, |
| 32 | title: '第一篇:认知篇', |
| 33 | role: 'chapter-divider', |
| 34 | sourceHeading: '# 第一篇:认知篇', |
| 35 | lineStart: 10, |
| 36 | lineEnd: 28, |
| 37 | reason: '' |
| 38 | } |
| 39 | ] |
| 40 | }) |
| 41 | }) |
| 42 | |
| 43 | it('uses only high-confidence matching skeletons without restructure requests', () => { |
| 44 | const sourcePlan = sourcePlanFromSkeletonRows([ |
| 45 | { |
| 46 | page_number: 1, |
| 47 | title: 'Market', |
| 48 | role: 'content', |
| 49 | source_document_path: '/docs/source.md', |
| 50 | source_heading: '## Market', |
| 51 | heading_level: 2, |
| 52 | line_start: 3, |
| 53 | line_end: 20, |
| 54 | confidence: 'high' |
| 55 | } |
| 56 | ]) |
| 57 | |
| 58 | expect(canUseSourcePlanDirectly({ sourcePlan, totalPages: 1, userMessage: '按文档生成' })).toBe(true) |
| 59 | expect(canUseSourcePlanDirectly({ sourcePlan, totalPages: 2, userMessage: '按文档生成' })).toBe(false) |
| 60 | expect(canUseSourcePlanDirectly({ sourcePlan, totalPages: 1, userMessage: '压缩成 1 页' })).toBe(false) |
| 61 | }) |
| 62 | |
| 63 | it('maps skeleton rows into range-bound outline items', () => { |
| 64 | const sourcePlan = sourcePlanFromSkeletonRows([ |
| 65 | { |
| 66 | page_number: 1, |
| 67 | title: '收入增长', |
| 68 | role: 'content', |
| 69 | source_document_path: '/docs/source.md', |
| 70 | source_heading: '## 收入增长', |
| 71 | heading_level: 2, |
| 72 | line_start: 30, |
| 73 | line_end: 48, |
| 74 | reason: 'leaf ## section without standalone child sections', |
| 75 | confidence: 'high' |
| 76 | } |
| 77 | ]) |
| 78 | expect(sourcePlan).not.toBeNull() |
| 79 | |
| 80 | const [item] = mapSourcePlanToOutlineItems(sourcePlan!) |
| 81 | |
| 82 | expect(item).toMatchObject({ |
| 83 | title: '收入增长', |
| 84 | layoutIntent: 'data-focus' |
| 85 | }) |
| 86 | expect(item.contentOutline).toContain('Source heading: ## 收入增长') |
| 87 | expect(item.contentOutline).toContain('Source range: lines 30-48') |
| 88 | expect(item.contentOutline).not.toContain('Page purpose:') |
| 89 | expect(item.contentOutline).not.toContain('leaf ## section') |
| 90 | }) |
| 91 | |
| 92 | it('maps section agenda rows without source headings or ranges', () => { |
| 93 | const sourcePlan = sourcePlanFromSkeletonRows([ |
| 94 | { |
| 95 | page_number: 1, |
| 96 | title: '二、技术参数与技术效率明细', |
| 97 | role: 'content', |
| 98 | source_document_path: '/docs/source.md', |
| 99 | source_heading: '## 二、技术参数与技术效率明细', |
| 100 | heading_level: 2, |
| 101 | line_start: 18, |
| 102 | line_end: 19, |
| 103 | reason: |
| 104 | '章节目录页:概览本章下的子主题,包括:2.1 主流AI动漫工具性能对比、2.2 训练数据规模、2.3 效率实证。', |
| 105 | confidence: 'high' |
| 106 | } |
| 107 | ]) |
| 108 | expect(sourcePlan).not.toBeNull() |
| 109 | |
| 110 | const [item] = mapSourcePlanToOutlineItems(sourcePlan!) |
| 111 | |
| 112 | expect(item).toMatchObject({ |
| 113 | title: '二、技术参数与技术效率明细', |
| 114 | layoutIntent: 'summary' |
| 115 | }) |
| 116 | expect(item.contentOutline).toContain('Page role: section-agenda') |
| 117 | expect(item.contentOutline).toContain('2.1 主流AI动漫工具性能对比') |
| 118 | expect(item.contentOutline).not.toContain('Source heading:') |
| 119 | expect(item.contentOutline).not.toContain('Source range:') |
| 120 | }) |
| 121 | }) |
| 122 |