import pytest import factory from collections import namedtuple from wtforms import Form, fields from werkzeug.datastructures import ImmutableMultiDict from pydantic_form import PydanticForm from pydantic import BaseModel, ValidationError from pydantic.types import StrictStr DataFactories = namedtuple( "DataFactories", ['valid', 'bad', 'missing'] ) 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) class MissingDataFactory(factory.Factory): class Meta: model = dict # Simplest case simple_keys = [('integer',), ('string',)] class SimpleSchema(BaseModel): integer: int string: StrictStr class SimpleWTForm(Form): _schema = SimpleSchema integer = fields.IntegerField() string = fields.StringField() class SimpleForm(SimpleWTForm, PydanticForm): _schema = SimpleSchema class SimpleDataFactory(factory.Factory): class Meta: model = dict integer = factory.Faker('pyint') string = factory.Faker('pystr') class SimpleBadDataFactory(factory.Factory): class Meta: model = dict integer = factory.Faker('pystr') string = factory.Faker('pyint') simple_data_factories = DataFactories( SimpleDataFactory, SimpleBadDataFactory, MissingDataFactory ) simple_expected_errors = ExpectedErrors( {}, { ('integer',): 'type_error.integer', ('string',): 'type_error.str' }, { ('integer',): 'value_error.missing', ('string',): 'value_error.missing', } ) @pytest.fixture 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')] class NestedSchema(BaseModel): integer: int nested: SimpleSchema class NestedWTForm(Form): _schema = NestedSchema integer = fields.IntegerField() nested = fields.FormField(form_class=SimpleForm) class NestedForm(NestedWTForm, PydanticForm): _schema = NestedSchema class NestedDataFactory(factory.Factory): class Meta: model = dict integer = factory.Faker('pyint') nested = factory.SubFactory(SimpleDataFactory) class NestedBadDataFactory(factory.Factory): class Meta: model = dict integer = factory.Faker('pystr') 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 def scenario_nested(): return Scenario( NestedForm, NestedSchema, nested_keys, nested_data_factories, nested_expected_errors ) # 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 def scenario_double_nested(): return Scenario( DoubleNestedForm, DoubleNestedSchema, double_nested_keys, double_nested_data_factories, double_nested_expected_errors )