feat(validator): R1 page_index continuity

This commit is contained in:
tack
2026-04-23 15:28:44 +08:00
parent 24122a189d
commit 1793f8d957
2 changed files with 47 additions and 2 deletions
+16 -2
View File
@@ -41,8 +41,22 @@ def _schema_errors(data: dict) -> list[CheckError]:
]
_LAYER2_RULES: list[Callable[[dict], list[CheckError]]] = []
"""Populated by later tasks — keep empty for scaffold task."""
def r_page_index_continuity(data: dict) -> list[CheckError]:
for i, page in enumerate(data.get("pages", [])):
expected = i + 1
got = page.get("page_index")
if got != expected:
return [CheckError(
layer=2,
path=f"pages[{i}].page_index",
message=f"expected {expected}, got {got} (pages must be contiguous from 1)",
)]
return []
_LAYER2_RULES: list[Callable[[dict], list[CheckError]]] = [
r_page_index_continuity,
]
def _semantic_errors(data: dict) -> list[CheckError]:
@@ -28,6 +28,37 @@ def test_validate_raises_on_invalid_schema():
validate({}) # empty dict fails schema required-field check
def _book_with_pages(page_indexes: list[int]) -> dict:
book = _minimal_valid_book()
template = book["pages"][0]
book["pages"] = [{**template, "page_index": idx} for idx in page_indexes]
return book
def test_r1_page_index_sequential_from_one_passes():
assert collect_errors(_book_with_pages([1, 2, 3])) == []
def test_r1_page_index_gap_is_caught():
errs = collect_errors(_book_with_pages([1, 3, 4]))
assert len(errs) == 1
assert errs[0].layer == 2
assert errs[0].path == "pages[1].page_index"
assert "expected 2" in errs[0].message
assert "got 3" in errs[0].message
def test_r1_page_index_starts_at_wrong_number():
errs = collect_errors(_book_with_pages([2, 3]))
assert len(errs) >= 1
assert errs[0].path == "pages[0].page_index"
def test_r1_page_index_duplicate():
errs = collect_errors(_book_with_pages([1, 1]))
assert any("pages[1].page_index" in e.path for e in errs)
def _minimal_valid_book() -> dict:
return {
"version": "1.0",