diff --git a/plenum/common/messages/fields.py b/plenum/common/messages/fields.py index fa29df1abd..cd270bccb6 100644 --- a/plenum/common/messages/fields.py +++ b/plenum/common/messages/fields.py @@ -194,14 +194,28 @@ def _specific_validation(self, val): class IterableField(FieldBase): _base_types = (list, tuple) - def __init__(self, inner_field_type: FieldValidator, **kwargs): + def __init__(self, inner_field_type: FieldValidator, min_length=None, + max_length=None, **kwargs): assert inner_field_type assert isinstance(inner_field_type, FieldValidator) + for m in (min_length, max_length): + if m is not None: + assert isinstance(m, int) + assert m > 0 self.inner_field_type = inner_field_type + self.min_length = min_length + self.max_length = max_length super().__init__(**kwargs) def _specific_validation(self, val): + if self.min_length is not None: + if len(val) < self.min_length: + return 'length should be at least {}'.format(self.min_length) + if self.max_length is not None: + if len(val) > self.max_length: + return 'length should be at most {}'.format(self.max_length) + for v in val: check_er = self.inner_field_type.validate(v) if check_er: diff --git a/plenum/test/helper.py b/plenum/test/helper.py index c86652cc30..2cf0d9bd20 100644 --- a/plenum/test/helper.py +++ b/plenum/test/helper.py @@ -828,7 +828,8 @@ def chk(): if fails >= acceptable_fails: logger.debug('Too many fails, the last one: {}'.format(repr(ex))) last_ex = ex - assert fails <= acceptable_fails, str(last_ex) + assert fails <= acceptable_fails, '{} out of {} failed. Last exception:' \ + ' {}'.format(fails, len(funcs), last_ex) kwargs = {} if retry_wait: diff --git a/plenum/test/input_validation/fields_validation/test_iterable_field.py b/plenum/test/input_validation/fields_validation/test_iterable_field.py index 5a5742eb77..8a10abcf68 100644 --- a/plenum/test/input_validation/fields_validation/test_iterable_field.py +++ b/plenum/test/input_validation/fields_validation/test_iterable_field.py @@ -13,3 +13,34 @@ def test_valid_inner_type(): validator = IterableField(NonNegativeNumberField()) assert not validator.validate([1, 2, 3]) assert validator.validate([1, 2, -3]) + + +def test_valid_min_max_vals(): + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), min_length=0) + + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), max_length=0) + + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), min_length=-1) + + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), max_length=-5) + + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), min_length=2.5) + + with pytest.raises(Exception): + IterableField(NonNegativeNumberField(), max_length=3.5) + + +def test_min_max_checks(): + validator = IterableField(NonNegativeNumberField(), min_length=3, + max_length=10) + assert validator.validate(list(range(2))) + + for i in range(3, 11): + assert not validator.validate(list(range(i))) + + assert validator.validate(list(range(12)))