def test_validate_or(): """ Test for the or_ validator, also in combination with not_""" # empty list error with pytest.raises(ValueError): or_() # empty list error 2 with pytest.raises(ValueError): or_([]) # single element simplification # assert or_(is_even) == failure_raiser(is_even) TODO enable when equality works # lists a=or_(is_even, is_multiple_of(3)) b=not_(or_(is_even, is_multiple_of(3))) # -- check that the validation works assert a(9) is True # a is not even but is a multiple of 3 > ok assert a(4) is True # a is even but is not a multiple of 3 > ok assert b(7) is True # b is not even AND not a multiple of 3 > ok with pytest.raises(AllValidatorsFailed): a(7) # 7 is odd and not multiple of 3 with pytest.raises(DidNotFail): b(3) # 3 is odd but it is a multiple of 3
def test_readme_index_usage_function(): """ Tests that the examples provided in the index page under Usage examples/Function are correct """ from mini_lambda import s, Len from valid8 import validate_arg from valid8.validation_lib import instance_of @validate_arg('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') def build_house(name, surface=None): print('Building house... DONE !') return name, surface build_house('sweet home', 200) with pytest.raises(InputValidationError) as exc_info: build_house('', 200) # name is invalid e = exc_info.value assert str(e) == "name should be a non-empty string. " \ "Error validating input [name=''] for function [build_house]. " \ "At least one validation function failed for value ''. " \ "Successes: [\"instance_of_%r\"] / Failures: {'len(s) > 0': 'Returned False.'}." \ "" % str from mini_lambda import s, x, l, Len from valid8 import validate_arg, validate_out from valid8.validation_lib import instance_of, is_multiple_of @validate_arg('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') @validate_arg( 'surface', (x >= 0) & (x < 10000), is_multiple_of(100), help_msg='Surface should be a multiple of 100 between 0 and 10000.') @validate_out(instance_of(tuple), Len(l) == 2) def build_house(name, surface=None): print('Building house... DONE !') return name, surface build_house('sweet home') build_house('sweet home', None) # No error ! with pytest.raises(TypeError): is_multiple_of(100)(None) with pytest.raises(TypeError): (Len(s) > 0).evaluate(None)
def test_validate_xor(): """ Test for the xor_ validator """ # empty list error with pytest.raises(ValueError): xor_() # empty list error 2 with pytest.raises(ValueError): xor_([]) # single element simplification # assert xor_(is_even) == failure_raiser(is_even) TODO enable when equality works # lists a=xor_(is_even, is_multiple_of(3)) # -- check that the validation works assert a(9) is True # a is not even but is a multiple of 3 > ok assert a(4) is True # a is even but is not a multiple of 3 > ok with pytest.raises(XorTooManySuccess): a(6) # a is both even and a multiple of 3 with pytest.raises(AllValidatorsFailed): a(7) # a is neither even nor a multiple of 3
def test_not_not_all(): """ Test for the not_ and not_all validators """ def gtcustom(x): assert x < 10 a = not_(is_even) b = not_all(is_even, is_multiple_of(3)) c = not_(gtcustom, catch_all=True) d = not_(gtcustom) assert a(11) is True assert b(11) is True assert c(11) is True # 11 leads to an AssertionError (not a ValidationFailure), but the not_ handles the exception correctly with pytest.raises(DidNotFail) as exc_info: a(84) # 84 is invalid (not even) e = exc_info.value assert str(e) == 'is_even validated value 84 with success, therefore the not() is a failure. ' \ 'Function [is_even] returned [True] for value 84.' with pytest.raises(DidNotFail): b(6) # 6 is invalid (is even and it is a multiple of 3) with pytest.raises(DidNotFail): c(9) # 9 is invalid (is less than 10) with pytest.raises(DidNotFail): d(9) # 9 is invalid (is less than 10) with pytest.raises(AssertionError): d(11) # 11 is a *valid* value, but the not_ operator does not catch the exception so we get the error
def test_readme_index_combining_autoclass(): """ Tests that the examples provided in the index page under Combining/autoclass are correct """ from autoclass import autoclass from mini_lambda import s, x, Len from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) @autoclass class House(object): def __init__(self, name, surface=None): pass h = House('sweet home', 200) h.surface = None # Valid (surface is nonable by signature) with pytest.raises(InvalidNameError): h.name = '' # InvalidNameError with pytest.raises(InvalidSurfaceError): h.surface = 10000 # InvalidSurfaceError
class House: @validate_arg('name', Len(s) > 0, error_type=InvalidName) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurface) def __init__(self, name: str, surface: Integral = None): pass
class House(object): @validate_arg('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) def __init__(self, name, surface=None): pass
def test_readme_combining_enforce(): # Imports - for type validation from numbers import Integral from typing import Tuple, Optional from enforce import runtime_validation, config config(dict(mode='covariant') ) # means that subclasses of required types are valid too from enforce.exceptions import RuntimeTypeError # Imports - for value validation from mini_lambda import s, x, Len from valid8 import validate_arg from valid8.validation_lib import is_multiple_of # Define our 2 applicative error types class InvalidNameError(InputValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(InputValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' # Apply type + value validation @runtime_validation @validate_arg('name', Len(s) > 0, error_type=InvalidNameError) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) def build_house( name: str, surface: Optional[Integral] = None ) -> Tuple[str, Optional[Integral]]: print('Building house... DONE !') return name, surface build_house('sweet home', 200) build_house('sweet home') with pytest.raises(InvalidNameError): build_house('', 100) # InvalidNameError with pytest.raises(InvalidSurfaceError): build_house('sweet home', 10000) # InvalidSurfaceError with pytest.raises(RuntimeTypeError): build_house('test', 100.1) # RuntimeTypeError
def test_validate_field_custom_type(): """""" from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of from mini_lambda import x, s, Len class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) class House(object): """Properties work only with new-style classes""" def __init__(self, name, surface=None): self.name = name self.surface = surface @property def surface(self): return self.__surface @surface.setter def surface(self, surface=None): self.__surface = surface h = House('sweet home') h.name = '' # DOES NOT RAISE InvalidNameError with pytest.raises(InvalidNameError): h = House('') h.surface = 100 with pytest.raises(InvalidSurfaceError): h.surface = 10000
def test_readme_index_usage_class_fields(): """ Tests that the examples provided in the index page under Usage examples/class fields are correct""" from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of from mini_lambda import x, s, Len @validate_field('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') @validate_field( 'surface', (x >= 0) & (x < 10000), is_multiple_of(100), help_msg='Surface should be a multiple of 100 between 0 and 10000.') class House(object): def __init__(self, name, surface=None): self.name = name self.surface = surface @property def surface(self): return self.__surface @surface.setter def surface(self, surface=None): self.__surface = surface h = House('sweet home') h.name = '' # DOES NOT RAISE InvalidNameError with pytest.raises(ClassFieldValidationError): h = House('') h.surface = 100 with pytest.raises(ClassFieldValidationError): h.surface = 10000
def test_readme_index_usage_basic(): """ Tests that the examples provided in the index page under Usage examples/Basic are correct """ from valid8 import assert_valid from valid8.validation_lib import instance_of, is_multiple_of surf = -1 # (1) simplest: one named variable to validate, one validation function assert_valid('surface', surf, instance_of(int)) with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, is_multiple_of(100)) e = exc_info.value assert str(e) == 'Error validating [surface=-1]. ' \ 'IsNotMultipleOf: Value should be a multiple of 100. Wrong value: -1.' # (2) native mini_lambda support to define validation functions from mini_lambda import x with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, x > 0) e = exc_info.value assert str( e ) == 'Error validating [surface=-1]. InvalidValue: Function [x > 0] returned [False] for value -1.'
def test_readme_index_combining_attrs(): """ Tests that the examples provided in the index page under Combining/autoclass are correct """ import attr from mini_lambda import s, x, Len from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) @attr.s class House(object): name = attr.ib() surface = attr.ib(default=None) h = House( 'sweet home') # Valid (surface is nonable by generated signature) h.name = '' # DOES NOT RAISE InvalidNameError (no setter!) with pytest.raises(InvalidNameError): House('', 10000) # InvalidNameError with pytest.raises(InvalidSurfaceError): House('sweet home', 10000) # InvalidSurfaceError
def test_readme_index_usage_composition(): """ Tests that the examples provided in the index page under Usage examples/Composition are correct """ from valid8 import assert_valid from valid8.validation_lib import is_multiple_of from mini_lambda import x surf = -1 # (7) composition of several base validation functions with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, (x >= 0) & (x < 10000), is_multiple_of(100)) e = exc_info.value assert str(e) == "Error validating [surface=-1]. " \ "At least one validation function failed for value -1. " \ "Successes: [] / Failures: {'(x >= 0) & (x < 10000)': 'Returned False.', " \ "'is_multiple_of_100': 'IsNotMultipleOf: Value should be a multiple of 100.'}." # (8) ... with a global custom error type. Oh by the way this supports templating class InvalidSurface(ValidationError): help_msg = 'Surface should be between {min_s} and {max_s} and be a multiple of {mul_s}, found {var_value}' min_surface, mul_surface, max_surface = 0, 100, 10000 with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, (x >= min_surface) & (x < max_surface), is_multiple_of(mul_surface), error_type=InvalidSurface, min_s=min_surface, mul_s=mul_surface, max_s=max_surface) e = exc_info.value assert str(e) == "Surface should be between 0 and 10000 and be a multiple of 100, found -1. " \ "Error validating [surface=-1]. " \ "At least one validation function failed for value -1. " \ "Successes: [] / Failures: {" \ "'(x >= 0) & (x < 10000)': 'Returned False.', " \ "'is_multiple_of_100': 'IsNotMultipleOf: Value should be a multiple of 100.'}." # (9) ... and possible user-friendly intermediate failure messages with pytest.raises(ValidationError) as exc_info: assert_valid( 'surface', surf, ((x >= 0) & (x < 10000), 'Surface should be between 0 and 10000'), (is_multiple_of(100), 'Surface should be a multiple of 100')) e = exc_info.value assert str(e) == "Error validating [surface=-1]. " \ "At least one validation function failed for value -1. " \ "Successes: [] / Failures: {" \ "'(x >= 0) & (x < 10000)': 'InvalidValue: Surface should be between 0 and 10000. " \ "Returned False.', " \ "'is_multiple_of_100': 'InvalidValue: Surface should be a multiple of 100. " \ "IsNotMultipleOf: Value should be a multiple of 100.'}." # *********** other even more complex tests *********** # + unique applicative error type with pytest.raises(ValidationError) as exc_info: assert_valid( 'surface', surf, failure_raiser( (x >= min_surface) & (x < max_surface), help_msg='Surface should be between {min_val} and {max_val}', min_val=min_surface, max_val=max_surface), (is_multiple_of(100), 'Surface should be a multiple of 100, found {wrong_value}'), error_type=InvalidSurface, min_s=min_surface, mul_s=mul_surface, max_s=max_surface) e = exc_info.value assert str(e) == "Surface should be between 0 and 10000 and be a multiple of 100, found -1. " \ "Error validating [surface=-1]. " \ "At least one validation function failed for value -1. " \ "Successes: [] / Failures: {" \ "'(x >= 0) & (x < 10000)': 'InvalidValue: Surface should be between 0 and 10000. " \ "Returned False.', " \ "'is_multiple_of_100': 'InvalidValue: Surface should be a multiple of 100, found -1. " \ "IsNotMultipleOf: Value should be a multiple of 100.'}."
def test_readme_index_usage_customization(): """ Tests that the examples provided in the index page under Usage examples/Customization are correct """ from valid8 import assert_valid, ValidationError from valid8.validation_lib import is_multiple_of from mini_lambda import x from valid8 import NonePolicy surf = -1 # (3) explicit validation policy for None with pytest.raises(ValidationError) as exc_info: assert_valid('surface', None, x > 0, none_policy=NonePolicy.FAIL) e = exc_info.value assert str( e ) == 'Error validating [surface=None]. ValueIsNone: The value must be non-None. Wrong value: None.' # *** (4) TEST: custom ValidationFailure (not ValidationError) message. Does it have any interest ? *** with pytest.raises(ValidationError) as exc_info: assert_valid( 'surface', surf, (is_multiple_of(100), 'Surface should be a multiple of 100')) e = exc_info.value assert str(e) == 'Error validating [surface=-1]. ' \ 'InvalidValue: Surface should be a multiple of 100. ' \ 'Function [is_multiple_of_100] raised ' \ 'IsNotMultipleOf: Value should be a multiple of 100. Wrong value: -1.' # (4) custom error message (exception is still a ValidationError) with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, x > 0, help_msg='Surface should be positive') e = exc_info.value assert str(e) == "Surface should be positive. " \ "Error validating [surface=-1]. InvalidValue: Function [x > 0] returned [False] for value -1." # (5) custom error types (recommended to provide unique applicative errors) class InvalidSurface(ValidationError): help_msg = 'Surface should be a positive number' with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, is_multiple_of(100), error_type=InvalidSurface) e = exc_info.value assert isinstance(e, InvalidSurface) assert str(e) == 'Surface should be a positive number. ' \ 'Error validating [surface=-1]. ' \ 'IsNotMultipleOf: Value should be a multiple of 100. Wrong value: -1.' # (6) custom error types with templating class InvalidSurface(ValidationError): help_msg = 'Surface should be > {minimum}, found {var_value}' min_value = 0 with pytest.raises(ValidationError) as exc_info: assert_valid('surface', surf, x > min_value, error_type=InvalidSurface, minimum=min_value) e = exc_info.value assert str(e) == "Surface should be > 0, found -1. " \ "Error validating [surface=-1]. InvalidValue: Function [x > 0] returned [False] for value -1."
class House: @validate_arg('name', Len(s) > 0) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100)) def __init__(self, name: str, surface: int = 100): pass
def test_is_multiple_of(): assert is_multiple_of(3)(-9) with pytest.raises(IsNotMultipleOf): is_multiple_of(3)(-10)