|
1 | 1 | """Test validation Failure.""" |
2 | 2 |
|
3 | 3 | # standard |
| 4 | +from functools import partial |
4 | 5 | from typing import Any, Callable |
5 | 6 |
|
6 | 7 | # third-party |
7 | 8 | import pytest |
8 | 9 |
|
9 | 10 | # local |
10 | | -from validators import ValidationError, between, slug, uuid |
| 11 | +from validators import ValidationError, between, slug, uuid, validator |
11 | 12 |
|
12 | 13 |
|
13 | 14 | @pytest.mark.parametrize("function,value", [(slug, "invalid_"), (uuid, "not-a-uuid")]) |
@@ -69,3 +70,72 @@ def test_arguments_as_properties(self): |
69 | 70 | assert self.is_in_between.__dict__["value"] == 3 |
70 | 71 | assert self.is_in_between.__dict__["min_val"] == 4 |
71 | 72 | assert self.is_in_between.__dict__["max_val"] == 5 |
| 73 | + |
| 74 | + |
| 75 | +def test_partial_validation_error_representation(): |
| 76 | + """Validation errors support partial callables without a name attribute.""" |
| 77 | + |
| 78 | + def above(value: int, minimum: int) -> bool: |
| 79 | + return value > minimum |
| 80 | + |
| 81 | + check = validator(partial(above, minimum=3)) |
| 82 | + error = check(1) |
| 83 | + assert isinstance(error, ValidationError) |
| 84 | + assert repr(error) == "ValidationError(func=partial, args={'value': 1})" |
| 85 | + assert str(error) == repr(error) |
| 86 | + assert check(4) is True |
| 87 | + |
| 88 | + |
| 89 | +def test_callable_instance_validation_error_representation(): |
| 90 | + """Callable instance errors use their type name.""" |
| 91 | + |
| 92 | + class Positive: |
| 93 | + def __call__(self, *, value: int) -> bool: |
| 94 | + return value > 0 |
| 95 | + |
| 96 | + error = validator(Positive())(value=-1) |
| 97 | + assert isinstance(error, ValidationError) |
| 98 | + assert repr(error) == "ValidationError(func=Positive, args={'value': -1})" |
| 99 | + |
| 100 | + |
| 101 | +def test_builtin_validation_error_keeps_raw_arguments(): |
| 102 | + """An unavailable builtin signature must not hide its validation error.""" |
| 103 | + check = validator(int) |
| 104 | + error = check("invalid") |
| 105 | + assert isinstance(error, ValidationError) |
| 106 | + assert "func=int" in repr(error) |
| 107 | + assert error.__dict__["positional_args"] == ("invalid",) |
| 108 | + assert error.__dict__["keyword_args"] == {} |
| 109 | + assert "invalid literal" in error.reason |
| 110 | + assert check("42") is True |
| 111 | + |
| 112 | + with pytest.raises(ValidationError, match="func=int"): |
| 113 | + check("invalid", r_ve=True) |
| 114 | + |
| 115 | + |
| 116 | +def test_callable_instance_positional_validation_error_arguments(): |
| 117 | + """Positional callable instance arguments exclude the bound instance.""" |
| 118 | + |
| 119 | + class Positive: |
| 120 | + def __call__(self, value: int) -> bool: |
| 121 | + return value > 0 |
| 122 | + |
| 123 | + error = validator(Positive())(-1) |
| 124 | + assert isinstance(error, ValidationError) |
| 125 | + assert error.__dict__["value"] == -1 |
| 126 | + assert "self" not in error.__dict__ |
| 127 | + assert repr(error) == "ValidationError(func=Positive, args={'value': -1})" |
| 128 | + |
| 129 | + |
| 130 | +def test_bound_method_positional_validation_error_arguments(): |
| 131 | + """Positional bound method arguments exclude the bound instance.""" |
| 132 | + |
| 133 | + class Positive: |
| 134 | + def check(self, value: int) -> bool: |
| 135 | + return value > 0 |
| 136 | + |
| 137 | + error = validator(Positive().check)(-1) |
| 138 | + assert isinstance(error, ValidationError) |
| 139 | + assert error.__dict__["value"] == -1 |
| 140 | + assert "self" not in error.__dict__ |
| 141 | + assert repr(error) == "ValidationError(func=check, args={'value': -1})" |
0 commit comments