conftest.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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. Keys = namedtuple(
  23. "Keys",
  24. ["class_", "instance"]
  25. )
  26. @pytest.fixture
  27. def scenario(request):
  28. return request.getfixturevalue(request.param)
  29. class MissingDataFactory(factory.Factory):
  30. class Meta:
  31. model = dict
  32. # Simplest case
  33. simple_keys = Keys(
  34. [('integer',), ('string',)],
  35. [('integer',), ('string',)]
  36. )
  37. class SimpleSchema(BaseModel):
  38. integer: int
  39. string: StrictStr
  40. class SimpleWTForm(Form):
  41. _schema = SimpleSchema
  42. integer = fields.IntegerField()
  43. string = fields.StringField()
  44. class SimpleForm(SimpleWTForm, PydanticForm):
  45. _schema = SimpleSchema
  46. class SimpleDataFactory(factory.Factory):
  47. class Meta:
  48. model = dict
  49. integer = factory.Faker('pyint')
  50. string = factory.Faker('pystr')
  51. class SimpleBadDataFactory(factory.Factory):
  52. class Meta:
  53. model = dict
  54. integer = factory.Faker('pystr')
  55. string = factory.Faker('pyint')
  56. simple_data_factories = DataFactories(
  57. SimpleDataFactory,
  58. SimpleBadDataFactory,
  59. MissingDataFactory
  60. )
  61. simple_expected_errors = ExpectedErrors(
  62. {},
  63. {
  64. ('integer',): 'type_error.integer',
  65. ('string',): 'type_error.str'
  66. },
  67. {
  68. ('integer',): 'value_error.missing',
  69. ('string',): 'value_error.missing',
  70. }
  71. )
  72. @pytest.fixture
  73. def scenario_simple():
  74. return Scenario(
  75. SimpleForm,
  76. SimpleSchema,
  77. simple_keys,
  78. simple_data_factories,
  79. simple_expected_errors
  80. )
  81. # Simple list case
  82. simple_list_keys = Keys(
  83. [('integers',), ('strings',)],
  84. [('integers', 0), ('integers', 1), ('strings', 0), ('strings', 1)],
  85. )
  86. class SimpleListSchema(BaseModel):
  87. integers: List[int]
  88. strings: List[StrictStr]
  89. class SimpleListWTForm(Form):
  90. _schema = SimpleSchema
  91. integers = fields.FieldList(fields.IntegerField(), min_entries=2)
  92. strings = fields.FieldList(fields.StringField(), min_entries=2)
  93. class SimpleListForm(SimpleListWTForm, PydanticForm):
  94. _schema = SimpleListSchema
  95. class SimpleListDataFactory(factory.Factory):
  96. class Meta:
  97. model = dict
  98. integers = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
  99. strings = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
  100. class SimpleListBadDataFactory(factory.Factory):
  101. class Meta:
  102. model = dict
  103. integers = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
  104. strings = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
  105. simple_list_data_factories = DataFactories(
  106. SimpleListDataFactory,
  107. SimpleListBadDataFactory,
  108. MissingDataFactory
  109. )
  110. simple_list_expected_errors = ExpectedErrors(
  111. {},
  112. {
  113. ('integers',): ['type_error.integer', 'type_error.integer'],
  114. ('strings',): ['type_error.str', 'type_error.str']
  115. },
  116. {
  117. ('integers',): 'value_error.missing',
  118. ('strings',): 'value_error.missing'
  119. }
  120. )
  121. @pytest.fixture
  122. def scenario_simple_list():
  123. return Scenario(
  124. SimpleListForm,
  125. SimpleListSchema,
  126. simple_list_keys,
  127. simple_list_data_factories,
  128. simple_list_expected_errors
  129. )
  130. # Case with one level of nesting
  131. nested_keys = Keys(
  132. [('integer',), ('nested', 'integer'), ('nested', 'string')],
  133. [('integer',), ('nested', 'integer'), ('nested', 'string')]
  134. )
  135. class NestedSchema(BaseModel):
  136. integer: int
  137. nested: SimpleSchema
  138. class NestedWTForm(Form):
  139. _schema = NestedSchema
  140. integer = fields.IntegerField()
  141. nested = fields.FormField(form_class=SimpleForm)
  142. class NestedForm(NestedWTForm, PydanticForm):
  143. _schema = NestedSchema
  144. class NestedDataFactory(factory.Factory):
  145. class Meta:
  146. model = dict
  147. integer = factory.Faker('pyint')
  148. nested = factory.SubFactory(SimpleDataFactory)
  149. class NestedBadDataFactory(factory.Factory):
  150. class Meta:
  151. model = dict
  152. integer = factory.Faker('pystr')
  153. nested = factory.SubFactory(SimpleBadDataFactory)
  154. nested_data_factories = DataFactories(
  155. NestedDataFactory,
  156. NestedBadDataFactory,
  157. MissingDataFactory
  158. )
  159. nested_expected_errors = ExpectedErrors(
  160. {},
  161. {
  162. ('integer',): 'type_error.integer',
  163. ('nested', 'integer',): 'type_error.integer',
  164. ('nested', 'string',): 'type_error.str'
  165. },
  166. {
  167. ('integer',): 'value_error.missing',
  168. ('nested',): 'value_error.missing',
  169. }
  170. )
  171. @pytest.fixture
  172. def scenario_nested():
  173. return Scenario(
  174. NestedForm,
  175. NestedSchema,
  176. nested_keys,
  177. nested_data_factories,
  178. nested_expected_errors
  179. )
  180. # Case with one level of nesting inside a list
  181. nested_list_keys = Keys(
  182. [('integer',), ('nested_list', 'integer'), ('nested_list', 'string')],
  183. [
  184. ('integer',),
  185. ('nested_list', 0, 'integer'),
  186. ('nested_list', 1, 'integer'),
  187. ('nested_list', 0, 'string'),
  188. ('nested_list', 1, 'string')
  189. ]
  190. )
  191. class NestedListSchema(BaseModel):
  192. integer: int
  193. nested_list: List[SimpleSchema]
  194. class NestedListWTForm(Form):
  195. _schema = NestedListSchema
  196. integer = fields.IntegerField()
  197. nested_list = fields.FieldList(fields.FormField(form_class=SimpleForm), min_entries=2)
  198. class NestedListForm(NestedListWTForm, PydanticForm):
  199. _schema = NestedListSchema
  200. class NestedListDataFactory(factory.Factory):
  201. class Meta:
  202. model = dict
  203. integer = factory.Faker('pyint')
  204. nested_list = factory.List([
  205. factory.SubFactory(SimpleDataFactory),
  206. factory.SubFactory(SimpleDataFactory)
  207. ])
  208. class NestedListBadDataFactory(factory.Factory):
  209. class Meta:
  210. model = dict
  211. integer = factory.Faker('pystr')
  212. nested_list = factory.List([
  213. factory.SubFactory(SimpleBadDataFactory),
  214. factory.SubFactory(SimpleBadDataFactory)
  215. ])
  216. nested_list_data_factories = DataFactories(
  217. NestedListDataFactory,
  218. NestedListBadDataFactory,
  219. MissingDataFactory
  220. )
  221. nested_list_expected_errors = ExpectedErrors(
  222. {},
  223. {
  224. ('integer',): 'type_error.integer',
  225. ('nested_list', 'integer',): ['type_error.integer', 'type_error.integer'],
  226. ('nested_list', 'string',): ['type_error.str', 'type_error.str']
  227. },
  228. {
  229. ('integer',): 'value_error.missing',
  230. ('nested_list',): 'value_error.missing',
  231. }
  232. )
  233. @pytest.fixture
  234. def scenario_nested_list():
  235. return Scenario(
  236. NestedListForm,
  237. NestedListSchema,
  238. nested_list_keys,
  239. nested_list_data_factories,
  240. nested_list_expected_errors
  241. )
  242. # Case with two levels of nesting
  243. double_nested_keys = Keys([
  244. ('integer',),
  245. ('double_nested', 'integer'),
  246. ('double_nested', 'nested', 'integer'),
  247. ('double_nested', 'nested', 'string')
  248. ],
  249. [
  250. ('integer',),
  251. ('double_nested', 'integer'),
  252. ('double_nested', 'nested', 'integer'),
  253. ('double_nested', 'nested', 'string')
  254. ]
  255. )
  256. class DoubleNestedSchema(BaseModel):
  257. integer: int
  258. double_nested: NestedSchema
  259. class DoubleNestedWTForm(Form):
  260. _schema = DoubleNestedSchema
  261. integer = fields.IntegerField()
  262. double_nested = fields.FormField(form_class=NestedWTForm)
  263. class DoubleNestedForm(DoubleNestedWTForm, PydanticForm):
  264. _schema = DoubleNestedSchema
  265. double_nested = fields.FormField(form_class=NestedForm)
  266. class DoubleNestedDataFactory(factory.Factory):
  267. class Meta:
  268. model = dict
  269. integer = factory.Faker('pyint')
  270. double_nested = factory.SubFactory(NestedDataFactory)
  271. class DoubleNestedBadDataFactory(factory.Factory):
  272. class Meta:
  273. model = dict
  274. integer = factory.Faker('pystr')
  275. double_nested = factory.SubFactory(NestedBadDataFactory)
  276. double_nested_data_factories = DataFactories(
  277. DoubleNestedDataFactory,
  278. DoubleNestedBadDataFactory,
  279. MissingDataFactory
  280. )
  281. double_nested_expected_errors = ExpectedErrors(
  282. {},
  283. {
  284. ('integer',): 'type_error.integer',
  285. ('double_nested', 'integer',): 'type_error.integer',
  286. ('double_nested', 'nested', 'integer',): 'type_error.integer',
  287. ('double_nested', 'nested', 'string',): 'type_error.str'
  288. },
  289. {
  290. ('integer',): 'value_error.missing',
  291. ('double_nested',): 'value_error.missing',
  292. }
  293. )
  294. @pytest.fixture
  295. def scenario_double_nested():
  296. return Scenario(
  297. DoubleNestedForm,
  298. DoubleNestedSchema,
  299. double_nested_keys,
  300. double_nested_data_factories,
  301. double_nested_expected_errors
  302. )