conftest.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import pytest
  2. import factory
  3. from collections import namedtuple
  4. from wtforms import Form, fields
  5. from werkzeug.datastructures import ImmutableMultiDict
  6. from pydantic_form import PydanticForm
  7. from pydantic import BaseModel, ValidationError
  8. from pydantic.types import StrictStr
  9. from typing import List
  10. DataFactories = namedtuple(
  11. "DataFactories",
  12. ['valid', 'bad', 'missing']
  13. )
  14. ExpectedErrors = namedtuple(
  15. "ExpectedErrors",
  16. ['valid', 'bad', 'missing']
  17. )
  18. Scenario = namedtuple(
  19. "ScenarioClasses",
  20. ['form', 'schema', 'keys', 'data_factory', 'errors']
  21. )
  22. @pytest.fixture
  23. def scenario(request):
  24. return request.getfixturevalue(request.param)
  25. class MissingDataFactory(factory.Factory):
  26. class Meta:
  27. model = dict
  28. # Simplest case
  29. simple_keys = [('integer',), ('string',)]
  30. class SimpleSchema(BaseModel):
  31. integer: int
  32. string: StrictStr
  33. class SimpleWTForm(Form):
  34. _schema = SimpleSchema
  35. integer = fields.IntegerField()
  36. string = fields.StringField()
  37. class SimpleForm(SimpleWTForm, PydanticForm):
  38. _schema = SimpleSchema
  39. class SimpleDataFactory(factory.Factory):
  40. class Meta:
  41. model = dict
  42. integer = factory.Faker('pyint')
  43. string = factory.Faker('pystr')
  44. class SimpleBadDataFactory(factory.Factory):
  45. class Meta:
  46. model = dict
  47. integer = factory.Faker('pystr')
  48. string = factory.Faker('pyint')
  49. simple_data_factories = DataFactories(
  50. SimpleDataFactory,
  51. SimpleBadDataFactory,
  52. MissingDataFactory
  53. )
  54. simple_expected_errors = ExpectedErrors(
  55. {},
  56. {
  57. ('integer',): 'type_error.integer',
  58. ('string',): 'type_error.str'
  59. },
  60. {
  61. ('integer',): 'value_error.missing',
  62. ('string',): 'value_error.missing',
  63. }
  64. )
  65. @pytest.fixture
  66. def scenario_simple():
  67. return Scenario(
  68. SimpleForm,
  69. SimpleSchema,
  70. simple_keys,
  71. simple_data_factories,
  72. simple_expected_errors
  73. )
  74. # Simple list case
  75. simple_list_keys = [('integer',), ('string',)]
  76. class SimpleListSchema(BaseModel):
  77. integers: List[int]
  78. strings: List[StrictStr]
  79. class SimpleListWTForm(Form):
  80. _schema = SimpleSchema
  81. integers = fields.FieldList(fields.IntegerField(), min_entries=2)
  82. strings = fields.FieldList(fields.StringField(), min_entries=2)
  83. class SimpleListForm(SimpleListWTForm, PydanticForm):
  84. _schema = SimpleListSchema
  85. class SimpleListDataFactory(factory.Factory):
  86. class Meta:
  87. model = dict
  88. integers = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
  89. strings = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
  90. class SimpleListBadDataFactory(factory.Factory):
  91. class Meta:
  92. model = dict
  93. integers = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
  94. strings = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
  95. simple_list_data_factories = DataFactories(
  96. SimpleListDataFactory,
  97. SimpleListBadDataFactory,
  98. MissingDataFactory
  99. )
  100. simple_list_expected_errors = ExpectedErrors(
  101. {},
  102. {
  103. ('integers',): ['type_error.integer', 'type_error.integer'],
  104. ('strings',): ['type_error.str', 'type_error.str']
  105. },
  106. {
  107. ('integers',): 'value_error.missing',
  108. ('strings',): 'value_error.missing'
  109. }
  110. )
  111. @pytest.fixture
  112. def scenario_simple_list():
  113. return Scenario(
  114. SimpleListForm,
  115. SimpleListSchema,
  116. simple_list_keys,
  117. simple_list_data_factories,
  118. simple_list_expected_errors
  119. )
  120. # Case with one level of nesting
  121. nested_keys = [('integer',), ('nested', 'integer'), ('nested', 'string')]
  122. class NestedSchema(BaseModel):
  123. integer: int
  124. nested: SimpleSchema
  125. class NestedWTForm(Form):
  126. _schema = NestedSchema
  127. integer = fields.IntegerField()
  128. nested = fields.FormField(form_class=SimpleForm)
  129. class NestedForm(NestedWTForm, PydanticForm):
  130. _schema = NestedSchema
  131. class NestedDataFactory(factory.Factory):
  132. class Meta:
  133. model = dict
  134. integer = factory.Faker('pyint')
  135. nested = factory.SubFactory(SimpleDataFactory)
  136. class NestedBadDataFactory(factory.Factory):
  137. class Meta:
  138. model = dict
  139. integer = factory.Faker('pystr')
  140. nested = factory.SubFactory(SimpleBadDataFactory)
  141. nested_data_factories = DataFactories(
  142. NestedDataFactory,
  143. NestedBadDataFactory,
  144. MissingDataFactory
  145. )
  146. nested_expected_errors = ExpectedErrors(
  147. {},
  148. {
  149. ('integer',): 'type_error.integer',
  150. ('nested', 'integer',): 'type_error.integer',
  151. ('nested', 'string',): 'type_error.str'
  152. },
  153. {
  154. ('integer',): 'value_error.missing',
  155. ('nested',): 'value_error.missing',
  156. }
  157. )
  158. @pytest.fixture
  159. def scenario_nested():
  160. return Scenario(
  161. NestedForm,
  162. NestedSchema,
  163. nested_keys,
  164. nested_data_factories,
  165. nested_expected_errors
  166. )
  167. # Case with two levels of nesting
  168. double_nested_keys = [
  169. ('integer',),
  170. ('double_nested', 'integer'),
  171. ('double_nested', 'nested', 'integer'),
  172. ('double_nested', 'nested', 'string')
  173. ]
  174. class DoubleNestedSchema(BaseModel):
  175. integer: int
  176. double_nested: NestedSchema
  177. class DoubleNestedWTForm(Form):
  178. _schema = DoubleNestedSchema
  179. integer = fields.IntegerField()
  180. double_nested = fields.FormField(form_class=NestedWTForm)
  181. class DoubleNestedForm(DoubleNestedWTForm, PydanticForm):
  182. _schema = DoubleNestedSchema
  183. double_nested = fields.FormField(form_class=NestedForm)
  184. class DoubleNestedDataFactory(factory.Factory):
  185. class Meta:
  186. model = dict
  187. integer = factory.Faker('pyint')
  188. double_nested = factory.SubFactory(NestedDataFactory)
  189. class DoubleNestedBadDataFactory(factory.Factory):
  190. class Meta:
  191. model = dict
  192. integer = factory.Faker('pystr')
  193. double_nested = factory.SubFactory(NestedBadDataFactory)
  194. double_nested_data_factories = DataFactories(
  195. DoubleNestedDataFactory,
  196. DoubleNestedBadDataFactory,
  197. MissingDataFactory
  198. )
  199. double_nested_expected_errors = ExpectedErrors(
  200. {},
  201. {
  202. ('integer',): 'type_error.integer',
  203. ('double_nested', 'integer',): 'type_error.integer',
  204. ('double_nested', 'nested', 'integer',): 'type_error.integer',
  205. ('double_nested', 'nested', 'string',): 'type_error.str'
  206. },
  207. {
  208. ('integer',): 'value_error.missing',
  209. ('double_nested',): 'value_error.missing',
  210. }
  211. )
  212. @pytest.fixture
  213. def scenario_double_nested():
  214. return Scenario(
  215. DoubleNestedForm,
  216. DoubleNestedSchema,
  217. double_nested_keys,
  218. double_nested_data_factories,
  219. double_nested_expected_errors
  220. )