- Writer: 表格表头行/Table Grid 边框、列表 List Bullet/Number 样式、行内字符格式不再塌缩(外视 #6 反转) - Writer: 打通章间引用(WriterState 摘要 → 后章 prompt prior_summaries) - Writer: 删除 _chunk_source 死代码,章节数据经 DataGate 控 token 预算 - Writer: 章节结果落盘快照,命中即跳过 LLM(录制重拍可续跑,损坏快照自动忽略) - Parser: 按 body 顺序遍历正文+单元格(含嵌套表、合并单元格去重),修复表格内锚点漏检导致的静默丢章 - Parser/服务层: .xls 显式拒绝(可操作提示),上传即校验扩展名,rules 对齐 docx-only - 测试: 全量 680 通过,覆盖率 99.37%(红线 99%)
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""WriterState 测试(P5-T5)。"""
|
||
from genesis.writer.writer_state import WriterState
|
||
from genesis.writer.models import ChapterContent, ContentBlock
|
||
from genesis.eval.scorer import EvalReport
|
||
|
||
|
||
def _content(cid, version=1):
|
||
return ChapterContent(chapter_id=cid, version=version, title="x", blocks=[])
|
||
|
||
|
||
def _report(failed):
|
||
return EvalReport(
|
||
dimensions=[], total_score=0.0, passed=False, failed_chapters=failed,
|
||
)
|
||
|
||
|
||
def test_initial_state_empty():
|
||
st = WriterState(["a", "b"])
|
||
assert st.needs_regeneration() == ["a", "b"]
|
||
|
||
|
||
def test_record_success_clears_need():
|
||
st = WriterState(["a"])
|
||
st.record_success(_content("a"))
|
||
assert st.needs_regeneration() == []
|
||
|
||
|
||
def test_failed_eval_marks_regeneration():
|
||
st = WriterState(["a"])
|
||
st.record_success(_content("a"))
|
||
st.record_eval("a", _report(["a"]))
|
||
assert st.needs_regeneration() == ["a"]
|
||
|
||
|
||
# ---------- 章间引用摘要(design.md §6.9) ----------
|
||
|
||
def _content_with_table(cid, title, headers, nrows):
|
||
rows = [[str(i) for _ in headers] for i in range(nrows)]
|
||
return ChapterContent(chapter_id=cid, version=1, title=title, blocks=[
|
||
ContentBlock(block_id="b1", type="table", headers=headers, rows=rows),
|
||
])
|
||
|
||
|
||
def test_summary_of_includes_title_and_table_structure():
|
||
st = WriterState(["a"])
|
||
st.record_success(_content_with_table("a", "機能一覧", ["機能ID", "機能名"], 3))
|
||
s = st.summary_of("a")
|
||
assert "a" in s and "機能一覧" in s
|
||
assert "機能ID" in s and "機能名" in s
|
||
assert "3" in s
|
||
|
||
|
||
def test_summary_of_none_when_not_generated():
|
||
st = WriterState(["a"])
|
||
assert st.summary_of("a") is None
|
||
|
||
|
||
def test_summaries_before_respects_order():
|
||
st = WriterState(["a", "b", "c"])
|
||
st.record_success(_content_with_table("a", "第一章", ["ID"], 1))
|
||
st.record_success(_content_with_table("b", "第二章", ["ID"], 2))
|
||
before_c = st.summaries_before("c")
|
||
assert "第一章" in before_c and "第二章" in before_c
|
||
assert st.summaries_before("a") == ""
|
||
|
||
|
||
def test_summaries_before_unknown_chapter_empty():
|
||
st = WriterState(["a"])
|
||
assert st.summaries_before("zzz") == ""
|