Procházet zdrojové kódy

Test forms with mixed data (partly valid)

Add a third 'float' field

Better errors test

Mixed data factory and errors
theenglishway (time) před 6 roky
rodič
revize
a627f0299b
3 změnil soubory, kde provedl 126 přidání a 39 odebrání
  1. 2 4
      pydantic_form/iterators.py
  2. 110 31
      tests/conftest.py
  3. 14 4
      tests/test_process.py

+ 2 - 4
pydantic_form/iterators.py

@@ -88,13 +88,11 @@ def iter_schema(schema, leafs_only=True):
                 else:
                     raise NotImplementedError()
             except AttributeError as e:
-                if schema.dict() != {}:
-                    raise e
+                ...
 
             continue
 
         try:
             yield key, get_schema_value(schema, key)
         except AttributeError as e:
-            if schema.dict() != {}:
-                raise e
+            ...

+ 110 - 31
tests/conftest.py

@@ -12,12 +12,12 @@ from typing import List
 
 DataFactories = namedtuple(
     "DataFactories",
-    ['valid', 'bad', 'missing']
+    ['valid', 'bad', 'missing', 'mixed']
 )
 
 ExpectedErrors = namedtuple(
     "ExpectedErrors",
-    ['valid', 'bad', 'missing']
+    ['valid', 'bad', 'missing', 'mixed']
 )
 
 Scenario = namedtuple(
@@ -43,13 +43,14 @@ class MissingDataFactory(factory.Factory):
 # Simplest case
 
 simple_keys = Keys(
-    [('integer',), ('string',)],
-    [('integer',), ('string',)]
+    [('integer',), ('string',), ('float',)],
+    [('integer',), ('string',), ('float',)]
 )
 
 class SimpleSchema(BaseModel):
     integer: int
     string: StrictStr
+    float: float
 
 
 class SimpleWTForm(Form):
@@ -57,6 +58,7 @@ class SimpleWTForm(Form):
 
     integer = fields.IntegerField()
     string = fields.StringField()
+    float = fields.FloatField()
 
 
 class SimpleForm(SimpleWTForm, PydanticForm):
@@ -69,6 +71,7 @@ class SimpleDataFactory(factory.Factory):
 
     integer = factory.Faker('pyint')
     string = factory.Faker('pystr')
+    float = factory.Faker('pyfloat')
 
 class SimpleBadDataFactory(factory.Factory):
     class Meta:
@@ -76,22 +79,37 @@ class SimpleBadDataFactory(factory.Factory):
 
     integer = factory.Faker('pystr')
     string = factory.Faker('pyint')
+    float = factory.Faker('pystr')
+
+class SimpleMixedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pyint')
+    float = factory.Faker('pystr')
 
 simple_data_factories = DataFactories(
     SimpleDataFactory,
     SimpleBadDataFactory,
-    MissingDataFactory
+    MissingDataFactory,
+    SimpleMixedDataFactory
 )
 
 simple_expected_errors = ExpectedErrors(
     {},
     {
         'integer': 'type_error.integer',
-        'string': 'type_error.str'
+        'string': 'type_error.str',
+        'float': 'type_error.float'
     },
     {
         'integer': 'value_error.missing',
         'string': 'value_error.missing',
+        'float': 'value_error.missing',
+    },
+    {
+        'string': 'value_error.missing',
+        'float': 'type_error.float'
     }
 )
 
@@ -108,19 +126,25 @@ def scenario_simple():
 # Simple list case
 
 simple_list_keys = Keys(
-    [('integers',), ('strings',)],
-    [('integers', 0), ('integers', 1), ('strings', 0), ('strings', 1)],
+    [('integers',), ('strings',), ('floats',)],
+    [
+        ('integers', 0), ('integers', 1),
+        ('strings', 0), ('strings', 1),
+        ('floats', 0), ('floats', 1)
+    ],
 )
 
 class SimpleListSchema(BaseModel):
     integers: List[int]
     strings: List[StrictStr]
+    floats: List[float]
 
 class SimpleListWTForm(Form):
     _schema = SimpleSchema
 
     integers = fields.FieldList(fields.IntegerField(), min_entries=2)
     strings = fields.FieldList(fields.StringField(), min_entries=2)
+    floats = fields.FieldList(fields.FloatField(), min_entries=2)
 
 
 class SimpleListForm(SimpleListWTForm, PydanticForm):
@@ -133,6 +157,7 @@ class SimpleListDataFactory(factory.Factory):
 
     integers = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
     strings = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
+    floats = factory.List([factory.Faker('pyfloat'), factory.Faker('pyfloat')])
 
 class SimpleListBadDataFactory(factory.Factory):
     class Meta:
@@ -140,22 +165,37 @@ class SimpleListBadDataFactory(factory.Factory):
 
     integers = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
     strings = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
+    floats = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
+
+class SimpleListMixedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integers = factory.List([factory.Faker('pyint'), factory.Faker('pyint')])
+    floats = factory.List([factory.Faker('pystr'), factory.Faker('pystr')])
 
 simple_list_data_factories = DataFactories(
     SimpleListDataFactory,
     SimpleListBadDataFactory,
-    MissingDataFactory
+    MissingDataFactory,
+    SimpleListMixedDataFactory
 )
 
 simple_list_expected_errors = ExpectedErrors(
     {},
     {
         'integers': ['type_error.integer', 'type_error.integer'],
-        'strings': ['type_error.str', 'type_error.str']
+        'strings': ['type_error.str', 'type_error.str'],
+        'floats': ['type_error.float', 'type_error.float']
     },
     {
         'integers': 'value_error.missing',
-        'strings': 'value_error.missing'
+        'strings': 'value_error.missing',
+        'floats': 'value_error.missing'
+    },
+    {
+        'strings': 'value_error.missing',
+        'floats': ['type_error.float', 'type_error.float']
     }
 )
 
@@ -173,8 +213,8 @@ def scenario_simple_list():
 # Case with one level of nesting
 
 nested_keys = Keys(
-    [('integer',), ('nested', 'integer'), ('nested', 'string')],
-    [('integer',), ('nested', 'integer'), ('nested', 'string')]
+    [('integer',), ('nested', 'integer'), ('nested', 'string'), ('nested', 'float',)],
+    [('integer',), ('nested', 'integer'), ('nested', 'string'), ('nested', 'float',)]
 )
 
 class NestedSchema(BaseModel):
@@ -208,25 +248,33 @@ class NestedBadDataFactory(factory.Factory):
     integer = factory.Faker('pystr')
     nested = factory.SubFactory(SimpleBadDataFactory)
 
+class NestedMixedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pyint')
+    nested = factory.SubFactory(SimpleMixedDataFactory)
+
 nested_data_factories = DataFactories(
     NestedDataFactory,
     NestedBadDataFactory,
-    MissingDataFactory
+    MissingDataFactory,
+    NestedMixedDataFactory
 )
 
 nested_expected_errors = ExpectedErrors(
     {},
     {
         'integer': 'type_error.integer',
-        'nested': {
-            'integer': 'type_error.integer',
-            'string': 'type_error.str'
-        }
+        'nested': simple_expected_errors.bad
     },
     {
         'integer': 'value_error.missing',
         'nested': 'value_error.missing',
-    }
+    },
+    {
+        'nested': simple_expected_errors.mixed
+    },
 )
 
 @pytest.fixture
@@ -243,13 +291,15 @@ def scenario_nested():
 # Case with one level of nesting inside a list
 
 nested_list_keys = Keys(
-    [('integer',), ('nested_list', 'integer'), ('nested_list', 'string')],
+    [('integer',), ('nested_list', 'integer'), ('nested_list', 'string'), ('nested_list', 'float')],
     [
         ('integer',),
         ('nested_list', 0, 'integer'),
         ('nested_list', 1, 'integer'),
         ('nested_list', 0, 'string'),
-        ('nested_list', 1, 'string')
+        ('nested_list', 1, 'string'),
+        ('nested_list', 0, 'float'),
+        ('nested_list', 1, 'float')
     ]
 )
 
@@ -290,10 +340,22 @@ class NestedListBadDataFactory(factory.Factory):
         factory.SubFactory(SimpleBadDataFactory)
     ])
 
+
+class NestedListMixedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pyint')
+    nested_list = factory.List([
+        factory.SubFactory(SimpleMixedDataFactory),
+        factory.SubFactory(SimpleMixedDataFactory)
+    ])
+
 nested_list_data_factories = DataFactories(
     NestedListDataFactory,
     NestedListBadDataFactory,
-    MissingDataFactory
+    MissingDataFactory,
+    NestedListMixedDataFactory
 )
 
 nested_list_expected_errors = ExpectedErrors(
@@ -308,7 +370,12 @@ nested_list_expected_errors = ExpectedErrors(
     {
         'integer': 'value_error.missing',
         'nested_list': 'value_error.missing',
-    }
+    },
+    {
+        'nested_list': [
+            simple_expected_errors.mixed
+        ]
+    },
 )
 
 @pytest.fixture
@@ -326,13 +393,15 @@ double_nested_keys = Keys([
         ('integer',),
         ('double_nested', 'integer'),
         ('double_nested', 'nested', 'integer'),
-        ('double_nested', 'nested', 'string')
+        ('double_nested', 'nested', 'string'),
+        ('double_nested', 'nested', 'float')
     ],
     [
         ('integer',),
         ('double_nested', 'integer'),
         ('double_nested', 'nested', 'integer'),
-        ('double_nested', 'nested', 'string')
+        ('double_nested', 'nested', 'string'),
+        ('double_nested', 'nested', 'float')
     ]
 )
 
@@ -368,10 +437,18 @@ class DoubleNestedBadDataFactory(factory.Factory):
     integer = factory.Faker('pystr')
     double_nested = factory.SubFactory(NestedBadDataFactory)
 
+class DoubleNestedMixedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pyint')
+    double_nested = factory.SubFactory(NestedMixedDataFactory)
+
 double_nested_data_factories = DataFactories(
     DoubleNestedDataFactory,
     DoubleNestedBadDataFactory,
-    MissingDataFactory
+    MissingDataFactory,
+    DoubleNestedMixedDataFactory
 )
 
 double_nested_expected_errors = ExpectedErrors(
@@ -380,16 +457,18 @@ double_nested_expected_errors = ExpectedErrors(
         'integer': 'type_error.integer',
         'double_nested': {
             'integer': 'type_error.integer',
-            'nested': {
-                'integer': 'type_error.integer',
-                'string': 'type_error.str'
-            }
+            'nested': simple_expected_errors.bad
         }
     },
     {
         'integer': 'value_error.missing',
         'double_nested': 'value_error.missing',
-    }
+    },
+    {
+        'double_nested': {
+            'nested': simple_expected_errors.mixed
+        }
+    },
 )
 
 @pytest.fixture

+ 14 - 4
tests/test_process.py

@@ -30,7 +30,7 @@ SCENARIOS = [
 )
 @pytest.mark.parametrize(
     'data',
-    ['valid', 'bad'],
+    ['valid', 'bad', 'missing', 'mixed'],
     indirect=True
 )
 @pytest.mark.parametrize(
@@ -42,7 +42,9 @@ def test_process_data(scenario, data, kwargs_factory):
     form = scenario.form()
     kwargs = kwargs_factory(data)
     form.process(**kwargs)
-    assert form.data == data
+
+    for key, value in nested_dict_iter(data, iter_list=True):
+        assert SchemaToForm.get_item(form.data, key) == value
 
 
 @pytest.mark.parametrize(
@@ -66,7 +68,7 @@ def test_validate_valid(scenario, data):
 
 @pytest.mark.parametrize(
     'data',
-    ['bad'],
+    ['bad', 'missing', 'mixed'],
     indirect=True
 )
 @pytest.mark.parametrize(
@@ -102,6 +104,7 @@ def test_errors_valid(scenario, data):
     [
         ('bad', lambda s: s.errors.bad),
         ('missing', lambda s: s.errors.missing),
+        ('mixed', lambda s: s.errors.mixed)
     ],
     indirect=['data']
 )
@@ -112,15 +115,22 @@ def test_errors_valid(scenario, data):
 )
 def test_errors_invalid(scenario, data, errors_factory):
     form = scenario.form(data=data)
+    expected_errors = errors_factory(scenario)
 
     form.validate()
     assert form.errors
-    for k, expected in nested_dict_iter(errors_factory(scenario), iter_list=True):
+    assert form.errors.keys() == expected_errors.keys()
+
+    for k, expected in nested_dict_iter(expected_errors, iter_list=True):
         field_errors = SchemaToForm.get_item(form.errors, k)
         errors = [e['type'] for e in field_errors]
         assert len(errors) == 1
         assert errors[0] == expected
 
+    assert (
+        dict(nested_dict_iter(form.errors)).keys()
+        == dict(nested_dict_iter(expected_errors)).keys()
+    )
 
 @pytest.mark.parametrize(
     'data',