|
@@ -5,48 +5,43 @@ from wtforms import Form, fields
|
|
|
from werkzeug.datastructures import ImmutableMultiDict
|
|
from werkzeug.datastructures import ImmutableMultiDict
|
|
|
from pydantic_form import PydanticForm
|
|
from pydantic_form import PydanticForm
|
|
|
from pydantic import BaseModel, ValidationError
|
|
from pydantic import BaseModel, ValidationError
|
|
|
|
|
+from pydantic.types import StrictStr
|
|
|
|
|
|
|
|
|
|
|
|
|
-ScenarioClasses = namedtuple(
|
|
|
|
|
- "ScenarioClasses",
|
|
|
|
|
- ['wtf_form', 'pydantic_form', 'schema', 'data_factory', 'bad_data_factory', 'keys']
|
|
|
|
|
|
|
+
|
|
|
|
|
+DataFactories = namedtuple(
|
|
|
|
|
+ "DataFactories",
|
|
|
|
|
+ ['valid', 'bad', 'missing']
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-ScenarioInstances = namedtuple(
|
|
|
|
|
- "ScenarioInstances",
|
|
|
|
|
- [
|
|
|
|
|
- 'wtf', 'wtf_formdata',
|
|
|
|
|
- 'pydantic', 'pydantic_formdata',
|
|
|
|
|
- 'formdata', 'data',
|
|
|
|
|
- 'schema'
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ExpectedErrors = namedtuple(
|
|
|
|
|
+ "ExpectedErrors",
|
|
|
|
|
+ ['valid', 'bad', 'missing']
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+Scenario = namedtuple(
|
|
|
|
|
+ "ScenarioClasses",
|
|
|
|
|
+ ['form', 'schema', 'keys', 'data_factory', 'errors']
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.fixture
|
|
|
|
|
+def scenario(request):
|
|
|
|
|
+ return request.getfixturevalue(request.param)
|
|
|
|
|
|
|
|
-@pytest.fixture(scope="session")
|
|
|
|
|
-def instance_factory():
|
|
|
|
|
- def _factory(scenario_classes, data):
|
|
|
|
|
- formdata = ImmutableMultiDict(data)
|
|
|
|
|
- try:
|
|
|
|
|
- schema_instance = scenario_classes.schema(**data)
|
|
|
|
|
- except ValidationError as e:
|
|
|
|
|
- schema_instance = None
|
|
|
|
|
- return ScenarioInstances(
|
|
|
|
|
- scenario_classes.wtf_form(data=data),
|
|
|
|
|
- scenario_classes.wtf_form(formdata=formdata),
|
|
|
|
|
- scenario_classes.pydantic_form(data=data),
|
|
|
|
|
- scenario_classes.pydantic_form(formdata=formdata),
|
|
|
|
|
- formdata,
|
|
|
|
|
- data,
|
|
|
|
|
- schema_instance
|
|
|
|
|
- )
|
|
|
|
|
- return _factory
|
|
|
|
|
|
|
+
|
|
|
|
|
+class MissingDataFactory(factory.Factory):
|
|
|
|
|
+ class Meta:
|
|
|
|
|
+ model = dict
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# Simplest case
|
|
|
|
|
|
|
|
simple_keys = [('integer',), ('string',)]
|
|
simple_keys = [('integer',), ('string',)]
|
|
|
|
|
|
|
|
class SimpleSchema(BaseModel):
|
|
class SimpleSchema(BaseModel):
|
|
|
integer: int
|
|
integer: int
|
|
|
- string: str
|
|
|
|
|
|
|
+ string: StrictStr
|
|
|
|
|
|
|
|
|
|
|
|
|
class SimpleWTForm(Form):
|
|
class SimpleWTForm(Form):
|
|
@@ -60,16 +55,6 @@ class SimpleForm(SimpleWTForm, PydanticForm):
|
|
|
_schema = SimpleSchema
|
|
_schema = SimpleSchema
|
|
|
|
|
|
|
|
|
|
|
|
|
-@pytest.fixture(scope="session")
|
|
|
|
|
-def scenario_classes_simple():
|
|
|
|
|
- return ScenarioClasses(
|
|
|
|
|
- SimpleWTForm, SimpleForm,
|
|
|
|
|
- SimpleSchema,
|
|
|
|
|
- SimpleDataFactory, SimpleBadDataFactory,
|
|
|
|
|
- simple_keys
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
class SimpleDataFactory(factory.Factory):
|
|
class SimpleDataFactory(factory.Factory):
|
|
|
class Meta:
|
|
class Meta:
|
|
|
model = dict
|
|
model = dict
|
|
@@ -84,16 +69,35 @@ class SimpleBadDataFactory(factory.Factory):
|
|
|
integer = factory.Faker('pystr')
|
|
integer = factory.Faker('pystr')
|
|
|
string = factory.Faker('pyint')
|
|
string = factory.Faker('pyint')
|
|
|
|
|
|
|
|
|
|
+simple_data_factories = DataFactories(
|
|
|
|
|
+ SimpleDataFactory,
|
|
|
|
|
+ SimpleBadDataFactory,
|
|
|
|
|
+ MissingDataFactory
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
-@pytest.fixture
|
|
|
|
|
-def scenario_simple(instance_factory, scenario_classes_simple):
|
|
|
|
|
- return instance_factory(scenario_classes_simple, SimpleDataFactory())
|
|
|
|
|
-
|
|
|
|
|
|
|
+simple_expected_errors = ExpectedErrors(
|
|
|
|
|
+ {},
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'type_error.integer',
|
|
|
|
|
+ ('string',): 'type_error.str'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'value_error.missing',
|
|
|
|
|
+ ('string',): 'value_error.missing',
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
@pytest.fixture
|
|
|
-def scenario_simple_bad(instance_factory, scenario_classes_simple):
|
|
|
|
|
- return instance_factory(scenario_classes_simple, SimpleBadDataFactory())
|
|
|
|
|
|
|
+def scenario_simple():
|
|
|
|
|
+ return Scenario(
|
|
|
|
|
+ SimpleForm,
|
|
|
|
|
+ SimpleSchema,
|
|
|
|
|
+ simple_keys,
|
|
|
|
|
+ simple_data_factories,
|
|
|
|
|
+ simple_expected_errors
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
|
|
+# Case with one level of nesting
|
|
|
|
|
|
|
|
nested_keys = [('integer',), ('nested', 'integer'), ('nested', 'string')]
|
|
nested_keys = [('integer',), ('nested', 'integer'), ('nested', 'string')]
|
|
|
|
|
|
|
@@ -128,22 +132,102 @@ class NestedBadDataFactory(factory.Factory):
|
|
|
integer = factory.Faker('pystr')
|
|
integer = factory.Faker('pystr')
|
|
|
nested = factory.SubFactory(SimpleBadDataFactory)
|
|
nested = factory.SubFactory(SimpleBadDataFactory)
|
|
|
|
|
|
|
|
|
|
+nested_data_factories = DataFactories(
|
|
|
|
|
+ NestedDataFactory,
|
|
|
|
|
+ NestedBadDataFactory,
|
|
|
|
|
+ MissingDataFactory
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+nested_expected_errors = ExpectedErrors(
|
|
|
|
|
+ {},
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'type_error.integer',
|
|
|
|
|
+ ('nested', 'integer',): 'type_error.integer',
|
|
|
|
|
+ ('nested', 'string',): 'type_error.str'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'value_error.missing',
|
|
|
|
|
+ ('nested',): 'value_error.missing',
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
-@pytest.fixture(scope="session")
|
|
|
|
|
-def scenario_classes_nested():
|
|
|
|
|
- return ScenarioClasses(
|
|
|
|
|
- NestedWTForm, NestedForm,
|
|
|
|
|
|
|
+@pytest.fixture
|
|
|
|
|
+def scenario_nested():
|
|
|
|
|
+ return Scenario(
|
|
|
|
|
+ NestedForm,
|
|
|
NestedSchema,
|
|
NestedSchema,
|
|
|
- NestedDataFactory, NestedBadDataFactory,
|
|
|
|
|
- nested_keys
|
|
|
|
|
|
|
+ nested_keys,
|
|
|
|
|
+ nested_data_factories,
|
|
|
|
|
+ nested_expected_errors
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@pytest.fixture
|
|
|
|
|
-def scenario_nested(instance_factory, scenario_classes_nested):
|
|
|
|
|
- return instance_factory(scenario_classes_nested, NestedDataFactory())
|
|
|
|
|
|
|
+# Case with two levels of nesting
|
|
|
|
|
+double_nested_keys = [
|
|
|
|
|
+ ('integer',),
|
|
|
|
|
+ ('double_nested', 'integer'),
|
|
|
|
|
+ ('double_nested', 'nested', 'integer'),
|
|
|
|
|
+ ('double_nested', 'nested', 'string')
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+class DoubleNestedSchema(BaseModel):
|
|
|
|
|
+ integer: int
|
|
|
|
|
+ double_nested: NestedSchema
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+class DoubleNestedWTForm(Form):
|
|
|
|
|
+ _schema = DoubleNestedSchema
|
|
|
|
|
+
|
|
|
|
|
+ integer = fields.IntegerField()
|
|
|
|
|
+ double_nested = fields.FormField(form_class=NestedWTForm)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class DoubleNestedForm(DoubleNestedWTForm, PydanticForm):
|
|
|
|
|
+ _schema = DoubleNestedSchema
|
|
|
|
|
+ double_nested = fields.FormField(form_class=NestedForm)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class DoubleNestedDataFactory(factory.Factory):
|
|
|
|
|
+ class Meta:
|
|
|
|
|
+ model = dict
|
|
|
|
|
+
|
|
|
|
|
+ integer = factory.Faker('pyint')
|
|
|
|
|
+ double_nested = factory.SubFactory(NestedDataFactory)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class DoubleNestedBadDataFactory(factory.Factory):
|
|
|
|
|
+ class Meta:
|
|
|
|
|
+ model = dict
|
|
|
|
|
+
|
|
|
|
|
+ integer = factory.Faker('pystr')
|
|
|
|
|
+ double_nested = factory.SubFactory(NestedBadDataFactory)
|
|
|
|
|
+
|
|
|
|
|
+double_nested_data_factories = DataFactories(
|
|
|
|
|
+ DoubleNestedDataFactory,
|
|
|
|
|
+ DoubleNestedBadDataFactory,
|
|
|
|
|
+ MissingDataFactory
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+double_nested_expected_errors = ExpectedErrors(
|
|
|
|
|
+ {},
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'type_error.integer',
|
|
|
|
|
+ ('double_nested', 'integer',): 'type_error.integer',
|
|
|
|
|
+ ('double_nested', 'nested', 'integer',): 'type_error.integer',
|
|
|
|
|
+ ('double_nested', 'nested', 'string',): 'type_error.str'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ ('integer',): 'value_error.missing',
|
|
|
|
|
+ ('double_nested',): 'value_error.missing',
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
@pytest.fixture
|
|
@pytest.fixture
|
|
|
-def scenario_nested_bad(instance_factory, scenario_classes_nested):
|
|
|
|
|
- return instance_factory(scenario_classes_nested, NestedBadDataFactory())
|
|
|
|
|
|
|
+def scenario_double_nested():
|
|
|
|
|
+ return Scenario(
|
|
|
|
|
+ DoubleNestedForm,
|
|
|
|
|
+ DoubleNestedSchema,
|
|
|
|
|
+ double_nested_keys,
|
|
|
|
|
+ double_nested_data_factories,
|
|
|
|
|
+ double_nested_expected_errors
|
|
|
|
|
+ )
|