Files
cad-agent/tests/test_report.py
T
gjm daccc734bf test(review): 把 5 处既有瑕疵锁成 *_known_gap 用例
让既有瑕疵可见,而不是被当成正确行为;改 1~3(确定性检查前置/结论结构化/
证据交叉校验)时可据此判断行为是否被改变。

- bbox_to_window 含非数值 → ValueError(上游只判长度)
- summarize / extract_issues 硬下标 f['frame'] → KeyError
- annotate 的 created 无去重(同 handle 产生重复批注)
- extract_issues 单行 ```json[...]``` 围栏剥离失败 → []
- result_json 文本 'null' → None(与「无 text 返回 default」语义易混)
2026-10-14 19:09:56 +08:00

142 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""`dwg_review.phases.report` 单元测试。"""
import json
import pytest
from dwg_review.phases.report import extract_issues, save_report, summarize
from tests.fakes import FakeLlmClient, make_ctx
def findings_fixture():
return [
{"frame": 1, "sheet": "A1", "finding": "图框1:文字重叠"},
{"frame": 2, "sheet": "A3", "finding": "图框2:未发现明显问题"},
]
# ---------------------------------------------------------------------------
# summarize
# ---------------------------------------------------------------------------
def test_summarize_empty_returns_message_without_llm():
llm = FakeLlmClient(text="不应被调用")
ctx = make_ctx(llm=llm)
out = summarize(ctx, [], log=lambda *a: None)
assert "未识别到可审查的图框" in out
assert llm.calls == []
def test_summarize_uses_chat_text():
llm = FakeLlmClient(text="## 总报告")
ctx = make_ctx(llm=llm)
assert summarize(ctx, findings_fixture(), log=lambda *a: None) == "## 总报告"
assert len(llm.calls) == 1
assert "图框1" in llm.calls[0]["user"]
def test_summarize_falls_back_to_raw_input_on_error():
class Boom(FakeLlmClient):
def chat_text(self, system, user, **kwargs):
raise RuntimeError("汇总炸了")
logs = []
ctx = make_ctx(llm=Boom())
out = summarize(ctx, findings_fixture(), log=logs.append)
assert "图框1" in out and "图框2" in out
assert any("汇总失败" in line for line in logs)
def test_summarize_missing_frame_key_known_gap():
"""已知瑕疵:summarize 用硬下标 f['frame'],findings 缺该键直接 KeyError。"""
ctx = make_ctx(llm=FakeLlmClient(text="x"))
with pytest.raises(KeyError):
summarize(ctx, [{"finding": "缺 frame 键"}], log=lambda *a: None)
# ---------------------------------------------------------------------------
# extract_issues
# ---------------------------------------------------------------------------
def test_extract_issues_empty_returns_empty_list():
llm = FakeLlmClient(text_script=["[]"])
ctx = make_ctx(llm=llm)
assert extract_issues(ctx, [], log=lambda *a: None) == []
assert llm.calls == []
def test_extract_issues_parses_json_array():
payload = '[{"frame": 1, "handle": "A", "severity": "high", "message": "x"}]'
llm = FakeLlmClient(text_script=[payload])
ctx = make_ctx(llm=llm)
issues = extract_issues(ctx, findings_fixture(), log=lambda *a: None)
assert issues == [{"frame": 1, "handle": "A", "severity": "high", "message": "x"}]
def test_extract_issues_strips_code_fence():
fenced = '```json\n[{"frame": 1, "message": "y"}]\n```'
llm = FakeLlmClient(text_script=[fenced])
ctx = make_ctx(llm=llm)
assert extract_issues(ctx, findings_fixture(), log=lambda *a: None) == [
{"frame": 1, "message": "y"}
]
def test_extract_issues_returns_empty_on_invalid_json():
logs = []
llm = FakeLlmClient(text_script=["这不是 JSON"])
ctx = make_ctx(llm=llm)
assert extract_issues(ctx, findings_fixture(), log=logs.append) == []
assert any("结构化抽取失败" in line for line in logs)
def test_extract_issues_returns_empty_when_not_a_list():
llm = FakeLlmClient(text_script=['{"frame": 1}'])
ctx = make_ctx(llm=llm)
assert extract_issues(ctx, findings_fixture(), log=lambda *a: None) == []
def test_extract_issues_single_line_fence_known_gap():
"""已知瑕疵:围栏剥离依赖换行,单行 ```json[...]``` 会残留 json 前缀 → 解析失败。"""
llm = FakeLlmClient(text_script=['```json[{"frame": 1, "message": "z"}]```'])
ctx = make_ctx(llm=llm)
assert extract_issues(ctx, findings_fixture(), log=lambda *a: None) == []
def test_extract_issues_missing_frame_key_known_gap():
"""已知瑕疵:extract_issues 同样用硬下标 f['frame'],findings 缺该键直接 KeyError。"""
ctx = make_ctx(llm=FakeLlmClient(text="[]"))
with pytest.raises(KeyError):
extract_issues(ctx, [{"finding": "缺 frame 键"}], log=lambda *a: None)
# ---------------------------------------------------------------------------
# save_report
# ---------------------------------------------------------------------------
def test_save_report_writes_md_and_json(tmp_reports):
findings = findings_fixture()
plan = {"frames": [{"bbox": [0, 0, 10, 10]}]}
issues = [{"frame": 1, "handle": "A", "message": "x"}]
md_path, json_path = save_report("# 报告", findings, plan, issues, log=lambda *a: None)
assert md_path.exists() and json_path.exists()
assert md_path.read_text(encoding="utf-8") == "# 报告"
data = json.loads(json_path.read_text(encoding="utf-8"))
assert data["findings"] == findings
assert data["issues"] == issues
assert data["frames"] == plan["frames"]
assert "generated_at" in data
def test_save_report_issues_default_to_empty(tmp_reports):
_, json_path = save_report("md", findings_fixture(), {"frames": []}, log=lambda *a: None)
assert json.loads(json_path.read_text(encoding="utf-8"))["issues"] == []
def test_save_report_creates_missing_directory(tmp_path, monkeypatch):
from dwg_review import config
target = tmp_path / "deep" / "nested"
monkeypatch.setattr(config, "REPORT_DIR", target)
save_report("md", [], {"frames": []}, log=lambda *a: None)
assert target.is_dir()