From 16921ecbb4082e113bbc925d7eb0d4aaf0e545c5 Mon Sep 17 00:00:00 2001 From: lhl Date: Mon, 10 Aug 2026 15:17:44 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E7=9C=9F=E5=AE=9E=E6=A0=B7=E6=9C=AC=20?= =?UTF-8?q?Word=20=E8=A7=A3=E6=9E=90=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=EF=BC=88=E6=A8=A1=E6=9D=BF/=E8=A7=84=E5=88=99/=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E4=B9=A6=E7=94=BB=E5=B1=9Ewrite=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _AI_USAGE_LOG.md | 1 + tests/test_real_samples.py | 65 +++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/_AI_USAGE_LOG.md b/_AI_USAGE_LOG.md index cc5df14..a67afcd 100644 --- a/_AI_USAGE_LOG.md +++ b/_AI_USAGE_LOG.md @@ -66,3 +66,4 @@ | 2026-08-10 | Agent 实现 | Phase3 Task2 实现:WordTemplateParser 章构成/占位符/样式名提取。新建 src/genesis/parsers/_word_common.py(heading_level 共享 helper,Task2/3 复用,从 Heading N 样式名解析大纲级别,非数字/无后缀兜底 1)与 src/genesis/parsers/word_template_parser.py(PLACEHOLDER_RE 统一占位符正则 {{键名}}/{{键名:章节名}};Heading 段落→heading 章标记、bookmarkStart→bookmark、占位符→placeholder 并写 placeholders{名:段落上下文};styles 提取 defined/used 样式名集合去重);tests/test_word_template_parser.py 按 brief 8 用例(6 解析 + 2 heading_level 兜底分支);覆盖补齐:bookmark 用例增加无 name 的 bookmarkStart 覆盖 if name 假分支(98%→100%);TDD 验证 RED(ModuleNotFoundError: No module named 'genesis.parsers._word_common')→ GREEN(聚焦 8 passed);pytest 全量 140 passed 覆盖 100.00%(841 stmts/200 br),fail_under=99 达标 | src/genesis/parsers/_word_common.py, src/genesis/parsers/word_template_parser.py, tests/test_word_template_parser.py, _AI_USAGE_LOG.md | deepseek-v4-flash-free | | 2026-08-10 | Agent 实现 | Phase3 Task3 实现:RuleDocParser 规则文档 Markdown 化与分类。新建 src/genesis/parsers/rule_doc_parser.py(body 级遍历保段落/表格交错顺序;Heading N→#×N;列表双通道检测 List 样式或 ・/-/• 前缀;表格→GFM;空段→空行;file_type 固定 word、hash=sha256 hex;复用 _word_common.heading_level 无本地重复)与 tests/test_rule_doc_parser.py 按 brief 6 用例;覆盖补齐:test_parse_empty_document 增加 doc.add_paragraph("") 使空段分支(原 new_document 无任何 w:p 不进分支)达 100%;TDD 验证 RED(ModuleNotFoundError: No module named 'genesis.parsers.rule_doc_parser')→ GREEN(聚焦 6 passed);pytest 全量 146 passed 覆盖 100.00%(886 stmts/218 br),fail_under=99 达标 | src/genesis/parsers/rule_doc_parser.py, tests/test_rule_doc_parser.py, _AI_USAGE_LOG.md | deepseek-v4-flash-free | | 2026-08-10 | Agent 实现 | Phase3 Task4 实现:SourceParser 门面全量输入聚合。新建 src/genesis/parsers/source_aggregator.py(角色显式传参无隐式猜测;requirements 校验 .xlsx/.xls 后缀聚合 tables/comments;template 校验 .docx 用 WordTemplateParser 解析;write_instruction+rule 校验 .docx 按 category='write' 归入 rule_docs;不存在抛 FileNotFoundError、未知扩展名抛 ValueError('不支持的文件类型: ...');image_analyses=[]/existing_system=None 固定)与 tests/test_source_aggregator.py 按 brief 6 用例 + 补 4 用例(模板未知扩展名、规则路径文件缺失、requirements/规则无扩展名兜底 '无扩展名' 文案,覆盖 51/62 行缺失与 or 表达式分支)达 100%;TDD 验证 RED(ModuleNotFoundError: No module named 'genesis.parsers.source_aggregator')→ GREEN(聚焦 10 passed);pytest 全量 156 passed 覆盖 100.00%(929 stmts/236 br),fail_under=99 达标 | src/genesis/parsers/source_aggregator.py, tests/test_source_aggregator.py, _AI_USAGE_LOG.md | deepseek-v4-flash-free | +| 2026-08-10 | 测试验证 | Phase3 Task5 实现:真实样本 Word 解析集成测试。tests/test_real_samples.py 追加 4 用例(保留现有 4 个 Excel 用例与 _x 辅助):模板→7 H1(はじめに…バッチ一覧)+section:introduction/doc_title 占位符+template_start 书签;记入規則→category=write/file_type=word/# 1. 機能一覧の書き方/- 機能ID は F001 から連番で付与する。(・前缀双通道检测);做成説明書→category=write/# 2. 機能一覧;全量组装→tables 非空+template 非空+rule_docs==2 且全 write;聚焦测试 8 passed 无 skip;pytest 全量 160 passed 覆盖 100.00%(929 stmts/236 br),fail_under=99 达标 | tests/test_real_samples.py, _AI_USAGE_LOG.md | deepseek-v4-flash-free | diff --git a/tests/test_real_samples.py b/tests/test_real_samples.py index 7abd076..1ab6e02 100644 --- a/tests/test_real_samples.py +++ b/tests/test_real_samples.py @@ -3,10 +3,17 @@ from pathlib import Path import pytest from genesis.parsers.excel_parser import ExcelParser +from genesis.parsers.word_template_parser import WordTemplateParser +from genesis.parsers.rule_doc_parser import RuleDocParser +from genesis.parsers.source_aggregator import SourceParser SAMPLES = Path(__file__).resolve().parents[1] / "samples" +def _d(name: str) -> Path: + return SAMPLES / name + + def _x(name: str) -> Path: return SAMPLES / name @@ -57,4 +64,60 @@ def test_mixed_sample_segments_detected(): # 碎片段含 ・ 与 ■ 两行文本 ft = [p for p in mixed.paragraphs if p.kind == "free_text"][0] assert "改修ポイント" in (ft.text or "") - assert "対象期間" in (ft.text or "") \ No newline at end of file + assert "対象期間" in (ft.text or "") + + +def test_word_template_sample_chapters_and_placeholders(): + p = _d("概要設計書テンプレート.docx") + if not p.exists(): + pytest.skip("样本缺失") + result = WordTemplateParser().parse(p) + headings = [s for s in result.sections if s.type == "heading"] + h1 = [h for h in headings if h.level == 1] + assert len(h1) == 7 + assert h1[0].name == "1. はじめに" + assert h1[-1].name == "7. バッチ一覧" + assert "section:introduction" in result.placeholders + assert "doc_title" in result.placeholders + bookmarks = [s for s in result.sections if s.type == "bookmark"] + assert len(bookmarks) == 1 + assert bookmarks[0].name == "template_start" + + +def test_rule_doc_sample_markdown_and_category(): + p = _d("記入規則.docx") + if not p.exists(): + pytest.skip("样本缺失") + result = RuleDocParser().parse(p, category="write") + assert result.category == "write" + assert result.file_type == "word" + assert "# 1. 機能一覧の書き方" in result.markdown_content + assert "- 機能ID は F001 から連番で付与する。" in result.markdown_content + + +def test_write_instruction_sample_category_write(): + p = _d("概要設計做成説明書.docx") + if not p.exists(): + pytest.skip("样本缺失") + result = RuleDocParser().parse(p, category="write") + assert result.category == "write" + assert "# 2. 機能一覧" in result.markdown_content + + +def test_source_parser_full_sample_assembly(): + xlsx = _x("要件定義_新規開発.xlsx") + template = _d("概要設計書テンプレート.docx") + rule = _d("記入規則.docx") + instr = _d("概要設計做成説明書.docx") + if not all(p.exists() for p in [xlsx, template, rule, instr]): + pytest.skip("样本缺失") + result = SourceParser().parse( + requirement_paths=[xlsx], + template_path=template, + write_instruction_paths=[instr], + rule_paths=[rule], + ) + assert result.tables + assert result.template is not None + assert len(result.rule_docs) == 2 + assert all(r.category == "write" for r in result.rule_docs) \ No newline at end of file