/
test_hypothesis_plugin.py
119 lines (96 loc) · 3.89 KB
/
test_hypothesis_plugin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import typing
import pytest
import pydantic
from pydantic.networks import import_email_validator
try:
from hypothesis import HealthCheck, given, settings, strategies as st
except ImportError:
from unittest import mock
given = settings = lambda *a, **kw: (lambda f: f) # pass-through decorator
HealthCheck = st = mock.Mock()
pytestmark = pytest.mark.skipif(True, reason='"hypothesis" not installed')
def gen_models():
class MiscModel(pydantic.BaseModel):
# Each of these models contains a few related fields; the idea is that
# if there's a bug we have neither too many fields to dig through nor
# too many models to read.
obj: pydantic.PyObject
color: pydantic.color.Color
json_any: pydantic.Json
class StringsModel(pydantic.BaseModel):
card: pydantic.PaymentCardNumber
secbytes: pydantic.SecretBytes
secstr: pydantic.SecretStr
class UUIDsModel(pydantic.BaseModel):
uuid1: pydantic.UUID1
uuid3: pydantic.UUID3
uuid4: pydantic.UUID4
uuid5: pydantic.UUID5
class IPvAnyAddress(pydantic.BaseModel):
address: pydantic.IPvAnyAddress
class IPvAnyInterface(pydantic.BaseModel):
interface: pydantic.IPvAnyInterface
class IPvAnyNetwork(pydantic.BaseModel):
network: pydantic.IPvAnyNetwork
class StrictNumbersModel(pydantic.BaseModel):
strictbool: pydantic.StrictBool
strictint: pydantic.StrictInt
strictfloat: pydantic.StrictFloat
strictstr: pydantic.StrictStr
class NumbersModel(pydantic.BaseModel):
posint: pydantic.PositiveInt
negint: pydantic.NegativeInt
posfloat: pydantic.PositiveFloat
negfloat: pydantic.NegativeFloat
nonposint: pydantic.NonPositiveInt
nonnegint: pydantic.NonNegativeInt
nonposfloat: pydantic.NonPositiveFloat
nonnegfloat: pydantic.NonNegativeFloat
class JsonModel(pydantic.BaseModel):
json_int: pydantic.Json[int]
json_float: pydantic.Json[float]
json_str: pydantic.Json[str]
json_int_or_str: pydantic.Json[typing.Union[int, str]]
json_list_of_float: pydantic.Json[typing.List[float]]
class ConstrainedNumbersModel(pydantic.BaseModel):
conintt: pydantic.conint(gt=10, lt=100)
coninte: pydantic.conint(ge=10, le=100)
conintmul: pydantic.conint(ge=10, le=100, multiple_of=7)
confloatt: pydantic.confloat(gt=10, lt=100)
confloate: pydantic.confloat(ge=10, le=100)
confloatemul: pydantic.confloat(ge=10, le=100, multiple_of=4.2)
confloattmul: pydantic.confloat(gt=10, lt=100, multiple_of=10)
condecimalt: pydantic.condecimal(gt=10, lt=100)
condecimale: pydantic.condecimal(ge=10, le=100)
yield from (
MiscModel,
StringsModel,
UUIDsModel,
IPvAnyAddress,
IPvAnyInterface,
IPvAnyNetwork,
StrictNumbersModel,
NumbersModel,
JsonModel,
ConstrainedNumbersModel,
)
try:
import_email_validator()
except ImportError:
pass
else:
class EmailsModel(pydantic.BaseModel):
email: pydantic.EmailStr
name_email: pydantic.NameEmail
yield EmailsModel
@pytest.mark.parametrize('model', gen_models())
@settings(suppress_health_check={HealthCheck.too_slow})
@given(data=st.data())
def test_can_construct_models_with_all_fields(data, model):
# The value of this test is to confirm that Hypothesis knows how to provide
# valid values for each field - otherwise, this would raise ValidationError.
instance = data.draw(st.from_type(model))
# We additionally check that the instance really is of type `model`, because
# an evil implementation could avoid ValidationError by means of e.g.
# `st.register_type_strategy(model, st.none())`, skipping the constructor.
assert isinstance(instance, model)