from app.evaluation_report_parser import EvaluationReportParser def test_multipath_report_becomes_structured_business_data(): report = """## 评估报告 script_build_id=436 ### 分支评估 branch_id=3 **该支结论**:通过 **目标推进**:完成核心教学段落。 ### 分支评估 branch_id=4 **该支结论**:部分通过 **未通过项**:工具属性说明还需补充。 ### 对比与建议 **采纳建议**:组合方案 3 与方案 4。 """ parsed = EvaluationReportParser().parse(report, kind="multipath") assert parsed["subjects"] == ["方案 3", "方案 4"] assert [item["conclusion"] for item in parsed["itemConclusions"]] == ["通过", "部分通过"] assert parsed["itemConclusions"][0]["achievements"] == ["完成核心教学段落。"] assert parsed["recommendation"] == "采纳建议:组合方案 3 与方案 4。" assert "script_build_id" not in str(parsed) assert "branch_id" not in str(parsed) def test_overall_report_extracts_criteria_achievements_problems_and_next_goal(): report = """### 评审标准 - 内容完整性 - 证据可靠性 ### 已达成 - 三个核心段落已完成。 ### 问题详情 - **paragraph_id=9**:收尾段落缺失。 - 【中】数据说明仍需补充。 ### 整体结论:部分通过 ### 下一步引领目标 补齐收尾并形成闭环。 """ parsed = EvaluationReportParser().parse(report, kind="overall") assert parsed["criteria"] == ["内容完整性", "证据可靠性"] assert parsed["achievements"] == ["三个核心段落已完成。"] assert [item["summary"] for item in parsed["problems"]] == ["收尾段落缺失。", "【中】数据说明仍需补充。"] assert parsed["problems"][1]["severity"] == "中" assert parsed["conclusion"] == "部分通过" assert parsed["nextGoal"] == "补齐收尾并形成闭环。" def test_markdown_table_is_parsed_without_exposing_internal_dimension_codes(): report = """### 维度评估 | 维度 | 结论 | 已达成 | 问题 | | --- | --- | --- | --- | | D1 维度体系一致性 | 部分通过 | 主线统一 | 段 3 命名重复 | | P1 元素完整性 | 通过 | 元素齐全 | | """ parsed = EvaluationReportParser().parse(report, kind="overall") assert parsed["criteria"] == ["维度体系一致性", "元素完整性"] assert [item["subject"] for item in parsed["itemConclusions"]] == [ "维度体系一致性", "元素完整性", ] assert parsed["problems"][0]["summary"] == "段 3 命名重复" assert "D1" not in str(parsed) assert "P1" not in str(parsed) def test_json_fence_and_wrong_build_id_never_become_business_content_or_association(): report = """## 评估报告 script_build_id=1393 ```json {"script_build_id": 434, "branch_id": 99, "dimension_pass": false} ``` ### 整体结论:通过 ### 评审建议 保留当前主线。 """ parsed = EvaluationReportParser().parse(report, kind="overall") assert parsed["conclusion"] == "通过" assert parsed["recommendation"] == "保留当前主线。" serialized = str(parsed) assert "1393" not in serialized assert "434" not in serialized assert "99" not in serialized assert "dimension_pass" not in serialized def test_missing_headings_and_broken_markdown_still_extract_explicit_labels(): report = """**整体结论**:需要重试 遗留问题:开头和结尾未呼应 **下一轮目标**:重写收尾 *** 未闭合的 Markdown """ parsed = EvaluationReportParser().parse(report, kind="overall") assert parsed["conclusion"] == "需要重试" assert parsed["problems"] == [{"summary": "开头和结尾未呼应"}] assert parsed["nextGoal"] == "重写收尾" assert parsed["notices"] == [] def test_business_numbers_are_not_removed_by_identifier_sanitizing(): report = """### 已达成 - 领域信息 31/32 已确认。 - 完成 5 个段落、12 个元素和 4:3 画面说明。 ### 整体结论:通过 """ parsed = EvaluationReportParser().parse(report) assert parsed["achievements"] == [ "领域信息 31/32 已确认。", "完成 5 个段落、12 个元素和 4:3 画面说明。", ] def test_empty_or_unparseable_report_returns_stable_empty_shape(): parsed = EvaluationReportParser().parse("```json\n{broken}\n```", kind="overall") assert parsed == { "parserVersion": 1, "kind": "overall", "subjects": [], "criteria": [], "itemConclusions": [], "achievements": [], "problems": [], "conclusion": None, "recommendation": None, "nextGoal": None, "notices": ["unparsed-report"], } def test_no_problem_markers_are_not_presented_as_warnings_and_suffix_heading_splits(): parsed = EvaluationReportParser().parse( """ ### 分支评估 branch_id=5 **该支结论**:通过 **目标推进**:已完成收尾。 **未通过项**:无显著质量问题。 **领域评估维度**(已激活并判定): - 烹饪常识:通过 """, kind="multipath", ) assert parsed["problems"] == [] assert parsed["itemConclusions"][0]["problems"] == [] assert "烹饪常识:通过" in parsed["criteria"] def test_positive_sentence_after_no_problem_marker_is_not_a_problem(): parsed = EvaluationReportParser().parse( "## 问题\n无显著质量问题。脚本已达到发布标准。\n\n## 整体结论\n通过", kind="overall", ) assert parsed["problems"] == [] def test_multipath_report_keeps_branch_groups_and_does_not_treat_not_passed_as_achievement(): report = """### 分支评估 branch_id=1 **该支结论**:部分通过 **目标推进**: - 领域信息库新增 5 条事实(id 54-58)。 **未通过/存疑项**: - 元素创建尚未完整落地。 **有据扎实度**:来源可指位。 ### 分支评估 branch_id=2 **该支结论**:部分通过 **目标推进**: - 已搭建五段骨架。 **未通过/部分通过项**: - 数量红线违规。 - function_elements 全部为空。 ### 对比与建议 | 维度 | branch1 | branch2 | 差异依据 | | --- | --- | --- | --- | | 与本轮目标的契合度 | 高 | 高 | 各自完成主任务 | | 结构完备性 | 不适用 | 部分通过 | 仅方案二涉及 | """ parsed = EvaluationReportParser().parse(report, kind="multipath") assert parsed["branchEvaluations"][0] == { "subject": "方案 1", "conclusion": "部分通过", "achievements": ["领域信息库新增 5 条事实(id 54-58)。"], "problems": ["元素创建尚未完整落地。"], "evidence": "来源可指位。", } assert parsed["branchEvaluations"][1]["achievements"] == ["已搭建五段骨架。"] assert parsed["branchEvaluations"][1]["problems"] == ["数量红线违规。", "function_elements 全部为空。"] assert "数量红线违规。" not in parsed["achievements"] assert parsed["comparisonRows"][0] == { "criterion": "与本轮目标的契合度", "candidates": [ {"subject": "方案 1", "value": "高"}, {"subject": "方案 2", "value": "高"}, ], "basis": "各自完成主任务", }