fix(workflows): reject bool max_iterations in while/do-while validation (#3237)

* fix(workflows): reject bool max_iterations in while/do-while validation

while/do-while validate() checked 'not isinstance(max_iter, int) or max_iter < 1'. Since bool is a subclass of int, isinstance(True, int) is True and True < 1 is False, so 'max_iterations: true' passed validation and then ran as a single iteration (range(True) == range(1)) instead of being reported as a type error. Reject bools explicitly, matching the fail-fast-on-bool handling already used for number inputs and gate options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: assert empty error list for the valid do-while max_iterations case

Address Copilot review: the accepted-config assertion only checked that no error mentioned 'max_iterations', which could let an unrelated validation error pass unnoticed. For a known-good config, assert the entire error list is empty (consistent with the other validate tests in this file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali jawwad
2026-06-30 20:17:05 +05:00
committed by GitHub
parent 6fb7e77b3e
commit 3571ba72d8
3 changed files with 29 additions and 2 deletions

View File

@@ -1822,6 +1822,12 @@ class TestWhileStep:
step = WhileStep()
errors = step.validate({"id": "test", "condition": "{{ true }}", "max_iterations": 0, "steps": []})
assert any("must be an integer >= 1" in e for e in errors)
# bool is an int subclass; `max_iterations: true` must be rejected, not
# silently treated as a single iteration.
bool_errors = step.validate(
{"id": "test", "condition": "{{ true }}", "max_iterations": True, "steps": []}
)
assert any("must be an integer >= 1" in e for e in bool_errors)
class TestDoWhileStep:
@@ -1861,6 +1867,21 @@ class TestDoWhileStep:
assert len(result.next_steps) == 1
assert result.output["max_iterations"] == 5
def test_validate_rejects_bool_max_iterations(self):
from specify_cli.workflows.steps.do_while import DoWhileStep
step = DoWhileStep()
# bool is an int subclass; `max_iterations: true` must be rejected.
errors = step.validate(
{"id": "test", "condition": "{{ true }}", "max_iterations": True, "steps": []}
)
assert any("must be an integer >= 1" in e for e in errors)
# a real positive integer is fully valid (no errors at all).
ok = step.validate(
{"id": "test", "condition": "{{ true }}", "max_iterations": 3, "steps": []}
)
assert ok == [], ok
def test_execute_empty_steps(self):
from specify_cli.workflows.steps.do_while import DoWhileStep
from specify_cli.workflows.base import StepContext