test: pin the reason behind rejected document packages
The outer exception boundary also absorbs accidental TypeError and KeyError, so asserting only the exception class could not tell an explicit validation from a lucky failure. Assert the reason for nested blocks and guard every rejection against an accidental cause.
This commit is contained in:
@@ -37,9 +37,20 @@ def _parse(pages=None, **kwargs):
|
|||||||
return parse_document_package(_package(pages, **kwargs))
|
return parse_document_package(_package(pages, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_rejected_on_purpose(exc: ResultInvalidError) -> None:
|
||||||
|
"""拒绝必须出自显式校验,不能是 Python 自己绊倒后被外层边界吸收。
|
||||||
|
|
||||||
|
外层边界会把 TypeError/KeyError 也归为 ResultInvalidError,所以只断言异常类型,
|
||||||
|
「显式校验」与「碰巧在别处失败」无法区分(变异检查证实过: 删掉校验后测试仍绿)。
|
||||||
|
原因链里出现 TypeError/KeyError 即说明有校验缺口。
|
||||||
|
"""
|
||||||
|
assert not isinstance(exc.__cause__, TypeError | KeyError), repr(exc.__cause__)
|
||||||
|
|
||||||
|
|
||||||
def _invalid(pages=None, **kwargs):
|
def _invalid(pages=None, **kwargs):
|
||||||
with pytest.raises(ResultInvalidError):
|
with pytest.raises(ResultInvalidError) as ei:
|
||||||
_parse(pages, **kwargs)
|
_parse(pages, **kwargs)
|
||||||
|
_assert_rejected_on_purpose(ei.value)
|
||||||
|
|
||||||
|
|
||||||
class TestHappyPath:
|
class TestHappyPath:
|
||||||
@@ -195,6 +206,22 @@ class TestTableAndImage:
|
|||||||
assert elements[1].bbox == (1.0, 2.0, 3.0, 4.0)
|
assert elements[1].bbox == (1.0, 2.0, 3.0, 4.0)
|
||||||
assert all(e.page_index == 0 for e in elements)
|
assert all(e.page_index == 0 for e in elements)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("blocks", "reason"),
|
||||||
|
[
|
||||||
|
pytest.param("not-a-list", "blocks 无效", id="blocks-not-list"),
|
||||||
|
pytest.param(["not-a-dict"], "块结构无效", id="sub-block-not-dict"),
|
||||||
|
pytest.param([{"bbox": list(_BBOX)}], "块缺少 type", id="sub-block-without-type"),
|
||||||
|
pytest.param([{"type": " ", "bbox": list(_BBOX)}], "块缺少 type", id="blank-type"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_malformed_nested_blocks_are_rejected_with_their_own_reason(self, blocks, reason):
|
||||||
|
"""外层边界会把 TypeError 等也归为 ResultInvalid,所以必须断言**原因**,否则"显式校验"
|
||||||
|
与"碰巧在别处失败"无法区分(变异检查发现仅断言类型时删掉校验测试仍绿)。"""
|
||||||
|
block = {"type": "table", "bbox": list(_BBOX), "blocks": blocks}
|
||||||
|
with pytest.raises(ResultInvalidError, match=reason):
|
||||||
|
_parse([_page([block])])
|
||||||
|
|
||||||
def test_empty_non_body_sub_block_is_not_emitted(self):
|
def test_empty_non_body_sub_block_is_not_emitted(self):
|
||||||
caption = _body("table_caption", [_span("")])
|
caption = _body("table_caption", [_span("")])
|
||||||
elements, _, _ = _parse([_page([_table_block(extra_subs=[caption])])])
|
elements, _, _ = _parse([_page([_table_block(extra_subs=[caption])])])
|
||||||
|
|||||||
Reference in New Issue
Block a user