Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
"""Word处理器章节填充逻辑单元测试"""
|
|
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
|
|
import pytest
|
|
from docx import Document
|
|
|
|
from bidmaster.tools.word import WordProcessor
|
|
|
|
|
|
@pytest.fixture()
|
|
def word_processor() -> WordProcessor:
|
|
return WordProcessor()
|
|
|
|
|
|
def _create_document(path: Path, headings: list[tuple[str, int]]) -> None:
|
|
doc = Document()
|
|
for text, level in headings:
|
|
doc.add_heading(text, level=level)
|
|
doc.save(path)
|
|
|
|
|
|
def _count_heading_paragraphs(doc: Document) -> int:
|
|
return sum(1 for para in doc.paragraphs if para.style.name.startswith("Heading"))
|
|
|
|
|
|
def test_fill_chapter_with_numbered_heading(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "numbered.docx"
|
|
_create_document(doc_path, [("1 商务和技术偏差表", 1)])
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_1",
|
|
"title": "商务和技术偏差表",
|
|
"level": 1,
|
|
"placeholder": "{{chapter_1_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("商务和技术偏差表"),
|
|
"heading_number": "1",
|
|
"order_index": 1,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, "测试段落内容")
|
|
|
|
doc = Document(doc_path)
|
|
assert any("测试段落内容" in para.text for para in doc.paragraphs)
|
|
|
|
|
|
def test_fill_chapter_without_number_uses_title(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "title_only.docx"
|
|
_create_document(doc_path, [("商务和技术偏差表", 1)])
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_1",
|
|
"title": "商务和技术偏差表",
|
|
"level": 1,
|
|
"placeholder": "{{chapter_1_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("商务和技术偏差表"),
|
|
"heading_number": None,
|
|
"order_index": 1,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, "服务方案内容")
|
|
|
|
doc = Document(doc_path)
|
|
assert any("服务方案内容" in para.text for para in doc.paragraphs)
|
|
|
|
|
|
def test_fill_chapter_sequence_fallback(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "sequence.docx"
|
|
_create_document(
|
|
doc_path,
|
|
[
|
|
("项目背景", 1),
|
|
("售后服务和质保期服务计划", 1),
|
|
],
|
|
)
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_11",
|
|
"title": "售后服务和质保期服务计划",
|
|
"level": 1,
|
|
"placeholder": "{{chapter_11_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("售后服务和质保期服务计划"),
|
|
"heading_number": None,
|
|
"order_index": 2,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, "售后保障措施")
|
|
|
|
doc = Document(doc_path)
|
|
assert any("售后保障措施" in para.text for para in doc.paragraphs)
|
|
|
|
|
|
def test_fill_chapter_appends_when_not_found(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "fallback.docx"
|
|
_create_document(doc_path, [("项目概述", 1)])
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_missing",
|
|
"title": "未匹配章节",
|
|
"level": 1,
|
|
"placeholder": "{{chapter_missing_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("未匹配章节"),
|
|
"heading_number": None,
|
|
"order_index": 2,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, "新增内容")
|
|
|
|
doc = Document(doc_path)
|
|
assert any("【系统提示】" in para.text for para in doc.paragraphs)
|
|
assert any("新增内容" in para.text for para in doc.paragraphs)
|
|
|
|
|
|
def test_fill_does_not_create_additional_headings(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "no_new_headings.docx"
|
|
_create_document(
|
|
doc_path,
|
|
[
|
|
("1 总体方案", 1),
|
|
("1.1 服务范围", 2),
|
|
],
|
|
)
|
|
|
|
before = Document(doc_path)
|
|
before_count = _count_heading_paragraphs(before)
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_1_1",
|
|
"title": "服务范围",
|
|
"level": 2,
|
|
"placeholder": "{{chapter_1_1_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("服务范围"),
|
|
"heading_number": "1.1",
|
|
"order_index": 1,
|
|
}
|
|
|
|
content = "## 小节标题\n服务内容描述"
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, content)
|
|
|
|
after = Document(doc_path)
|
|
after_count = _count_heading_paragraphs(after)
|
|
assert after_count == before_count
|
|
|
|
|
|
def test_markdown_headings_render_as_bold_paragraphs(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "markdown.docx"
|
|
_create_document(doc_path, [("1 技术方案", 1)])
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_1",
|
|
"title": "技术方案",
|
|
"level": 1,
|
|
"placeholder": "{{chapter_1_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("技术方案"),
|
|
"heading_number": "1",
|
|
"order_index": 1,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(
|
|
doc_path,
|
|
chapter_meta,
|
|
"## 子标题\n段落内容",
|
|
)
|
|
|
|
doc = Document(doc_path)
|
|
bold_paras = [para for para in doc.paragraphs if para.text.strip() == "子标题"]
|
|
assert bold_paras, "应生成加粗段落"
|
|
assert all(not para.style.name.startswith("Heading") for para in bold_paras)
|
|
assert any(any(run.bold for run in para.runs) for para in bold_paras)
|
|
|
|
|
|
def test_fill_matches_higher_level_heading(word_processor: WordProcessor) -> None:
|
|
with TemporaryDirectory() as tmp_dir:
|
|
doc_path = Path(tmp_dir) / "deep_heading.docx"
|
|
_create_document(doc_path, [("1.1.1.1 深度章节", 4)])
|
|
|
|
chapter_meta = {
|
|
"id": "chapter_1_1_1_1",
|
|
"title": "深度章节",
|
|
"level": 4,
|
|
"placeholder": "{{chapter_1_1_1_1_content}}",
|
|
"normalized_title": word_processor._normalize_heading_text("深度章节"),
|
|
"heading_number": "1.1.1.1",
|
|
"order_index": 1,
|
|
}
|
|
|
|
word_processor.fill_chapter_content(doc_path, chapter_meta, "深度内容")
|
|
|
|
doc = Document(doc_path)
|
|
assert any("深度内容" in para.text for para in doc.paragraphs) |