"""标题树解析器的单元测试""" from app.core.headings import parse_headings, render_outline class TestMarkdownHeadings: """Markdown ATX 标题解析""" def test_multi_level(self): """多级 Markdown 标题,层级为 # 数量""" text = "# 一级标题\n正文内容\n## 二级标题\n更多内容\n### 三级标题\n" headings = parse_headings(text) assert [(h.title, h.level, h.line_index) for h in headings] == [ ("一级标题", 1, 0), ("二级标题", 2, 2), ("三级标题", 3, 4), ] def test_hash_without_space_not_heading(self): """# 后无空白不是标题""" assert parse_headings("#不是标题") == [] def test_up_to_six_levels(self): """最多支持 6 级标题""" text = "###### 六级标题\n####### 七级不算\n" headings = parse_headings(text) assert len(headings) == 1 assert headings[0].level == 6 class TestChineseChapterHeadings: """中文章/节/篇编号标题解析""" def test_chapter_and_section(self): """章=1 级,节=2 级""" text = "第一章 总则\n正文\n第一节 一般规定\n" headings = parse_headings(text) assert [(h.title, h.level) for h in headings] == [("第一章 总则", 1), ("第一节 一般规定", 2)] def test_pian_is_level_one(self): """篇=1 级,支持阿拉伯数字编号""" text = "第一篇 概述\n第2章 背景\n" headings = parse_headings(text) assert [(h.title, h.level) for h in headings] == [("第一篇 概述", 1), ("第2章 背景", 1)] def test_chinese_enum_is_level_one(self): """一、二、…… 固定 1 级""" text = "一、项目背景\n二、建设目标\n" headings = parse_headings(text) assert [(h.title, h.level) for h in headings] == [("一、项目背景", 1), ("二、建设目标", 1)] def test_overlong_line_not_heading(self): """编号行超过 60 字符视为正文""" text = "第一章 " + "很长的标题" * 12 + "\n" assert parse_headings(text) == [] class TestNumericHeadings: """数字编号标题解析""" def test_dot_segments_determine_level(self): """按点分段数定层级:1.=1、1.1=2、1.1.1=3""" text = "1. 概述\n1.1 背景\n1.1.1 细节\n" headings = parse_headings(text) assert [(h.title, h.level) for h in headings] == [ ("1. 概述", 1), ("1.1 背景", 2), ("1.1.1 细节", 3), ] def test_dunhao_separator(self): """顿号分隔的数字编号为 1 级""" headings = parse_headings("1、基本要求\n2、总体架构\n") assert [(h.title, h.level) for h in headings] == [("1、基本要求", 1), ("2、总体架构", 1)] class TestMixedAndPlain: """混合模式与无结构文本""" def test_no_headings_returns_empty(self): """无标题文本返回空列表""" text = "第一段正文内容。\n\n第二段正文内容。\n" assert parse_headings(text) == [] def test_empty_text(self): assert parse_headings("") == [] def test_mixed_patterns(self): """Markdown 与中文编号混合时均能识别""" text = "# Markdown 标题\n正文\n第一章 中文标题\n1.1 数字标题\n" headings = parse_headings(text) assert [(h.title, h.level) for h in headings] == [ ("Markdown 标题", 1), ("第一章 中文标题", 1), ("1.1 数字标题", 2), ] class TestRenderOutline: """标题树渲染为大纲文本""" def test_indent_by_level(self): headings = parse_headings("# 安装指南\n## 环境准备\n## 安装步骤\n### 验证\n") assert render_outline(headings) == "- 安装指南\n - 环境准备\n - 安装步骤\n - 验证"