Skip to content

Commit 6dccf52

Browse files
committed
test: reject malformed recurrence result integers
1 parent 10b6c45 commit 6dccf52

4 files changed

Lines changed: 53 additions & 2 deletions

File tree

src/jacobian/contracts/combinatorics.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,9 @@ def require_complete_replay_prefix(self) -> Self:
895895
raise ValueError("the greatest requested index must bind replay_scope_end")
896896
if any(item.value != self.replay_prefix[item.index] for item in self.values):
897897
raise ValueError("indexed values must match the recurrence replay prefix")
898-
if self.scope == "PREFIX" and indices != tuple(range(len(indices))):
898+
if self.scope == "PREFIX" and indices != tuple(
899+
range(self.replay_scope_end + 1)
900+
):
899901
raise ValueError(
900902
"PREFIX results must contain consecutive indices from zero"
901903
)

src/jacobian_checkers/recurrence_series.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def _validate_recurrence_values(
162162
if (
163163
not isinstance(item, dict)
164164
or set(item) != {"index", "value"}
165+
or type(item["index"]) is not int
165166
or item["index"] != index
166167
or _fraction(item["value"], max_digits=32_768) != replay[index]
167168
):
@@ -281,6 +282,7 @@ def _p_recursive_residuals_match(
281282
return all(
282283
isinstance(item, dict)
283284
and set(item) == {"index", "value"}
285+
and type(item["index"]) is int
284286
and item["index"] == index
285287
and _fraction(item["value"], max_digits=32_768) == residual
286288
and residual == 0
@@ -354,7 +356,12 @@ def _replay_polynomial_coefficient_recurrence(
354356
return False
355357
initial = _fractions(source["initial_values"], minimum=1, maximum=16, max_digits=64)
356358
order = len(polynomials) - 1
357-
if len(initial) != order or result["recurrence_order"] != order:
359+
if (
360+
len(initial) != order
361+
or type(result["recurrence_order"]) is not int
362+
or not 1 <= result["recurrence_order"] <= 16
363+
or result["recurrence_order"] != order
364+
):
358365
return False
359366
raw_indices = source["indices"]
360367
if not isinstance(raw_indices, list) or len(raw_indices) > 256:

tests/component/checkers/test_recurrence_series_checker.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,28 @@ def test_recurrence_series_checker_has_no_sympy_or_producer_dependency() -> None
224224
source = inspect.getsource(checker_module)
225225
assert "import sympy" not in source
226226
assert "domains.combinatorics" not in source
227+
228+
229+
@pytest.mark.parametrize(
230+
("path", "value"),
231+
[
232+
(("recurrence_order",), True),
233+
(("values", 0, "index"), False),
234+
(("residuals", 0, "index"), True),
235+
],
236+
)
237+
def test_polynomial_recurrence_checker_rejects_boolean_result_integers(
238+
path: tuple[str | int, ...],
239+
value: bool,
240+
) -> None:
241+
forged = copy.deepcopy(_CASES[1][1])
242+
target: Any = forged["candidate"]["payload"]
243+
for key in path[:-1]:
244+
target = target[key]
245+
target[path[-1]] = value
246+
forged["candidate"]["payload_digest"] = _digest(forged["candidate"]["payload"])
247+
248+
checked = check_polynomial_coefficient_recurrence_evaluation(forged)
249+
250+
assert checked["accepted"] is False
251+
assert checked["conclusion"] == "UNKNOWN"

tests/unit/contracts/test_recurrence_contracts.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ def _result() -> dict[str, object]:
5151
{"index": 2, "value": _q(2)},
5252
{"index": 3, "value": _q(3)},
5353
],
54+
[
55+
{"index": 0, "value": _q(1)},
56+
{"index": 2, "value": _q(2)},
57+
{"index": 1, "value": _q(1)},
58+
{"index": 3, "value": _q(3)},
59+
],
60+
[
61+
{"index": 0, "value": _q(1)},
62+
{"index": 1, "value": _q(1)},
63+
{"index": 2, "value": _q(2)},
64+
{"index": 3, "value": _q(3)},
65+
{"index": 4, "value": _q(5)},
66+
],
5467
],
5568
)
5669
def test_polynomial_recurrence_result_rejects_malformed_prefix_projection(
@@ -76,6 +89,10 @@ def test_polynomial_recurrence_result_rejects_malformed_prefix_projection(
7689
{"index": 2, "value": _q(0)},
7790
{"index": 4, "value": _q(0)},
7891
],
92+
[
93+
{"index": 3, "value": _q(0)},
94+
{"index": 2, "value": _q(0)},
95+
],
7996
],
8097
)
8198
def test_polynomial_recurrence_result_requires_exact_residual_range(

0 commit comments

Comments
 (0)