def test_root_list_valid(self, value):
        param = JsonParam(
            {
                'age': [Number()],
                'name': [
                    MinLength(1),
                ],
                'tags':
                JsonParam(
                    {'name': [MinLength(1)]}, required=False, as_list=True)
            },
            as_list=True)

        valid_value, errors = param.validate(deepcopy(value))
        self.assertListEqual(value, valid_value)
        self.assertEqual(0, len(errors))
 def test_dict(self, param: JsonParam, data, exp):
     value, errors = param.validate(data)
     for ix, json_error in enumerate(errors):  # type: list, JsonError
         self.assertTrue(isinstance(json_error, JsonError))
         exp_depth, epx_errors_map = exp[ix]  # type: list, dict
         self.assertListEqual(json_error.depth, exp_depth)
         for key, error in json_error.errors.items():
             if isinstance(error, RulesError):
                 self.assertEqual(len(error.errors), len(epx_errors_map))
                 for ix_rule, rule_err in enumerate(error.errors):
                     self.assertTrue(
                         isinstance(rule_err, epx_errors_map[key][0]))
             else:
                 self.assertTrue(isinstance(error, epx_errors_map[key]))
 def test_list(self, param: JsonParam, data, exp):
     value, errors = param.validate(data)
     self.assertEqual(len(exp), len(errors))
     for err_ix, json_er in enumerate(errors):  # type: int, JsonError
         exp_err = exp[err_ix]
         exp_rule_err = exp_err[1]
         self.assertListEqual(json_er.depth, exp_err[0])
         self.assertEqual(len(exp_err[1]), len(json_er.errors))
         for rules_ix, rules_err in exp_rule_err.items():
             json_rules = json_er.errors[
                 rules_ix]  # type: dict or list or RulesError
             if isinstance(exp_rule_err[rules_ix], list):
                 self.assertTrue(isinstance(json_rules, RulesError))
                 for k, rule_err in enumerate(json_rules.errors):
                     self.assertTrue(
                         isinstance(rule_err, exp_rule_err[rules_ix][k]))
             else:
                 if isinstance(rules_err, dict):
                     self.assertTrue(len(json_rules), len(rules_err))
                 else:
                     # RulesError
                     self.assertTrue(
                         isinstance(json_er.errors[rules_ix], rules_err))
class TestJsonParam(unittest.TestCase):
    LIST_SCHEMA = JsonParam(
        {
            'person':
            JsonParam(
                {
                    'info':
                    JsonParam({
                        'contacts':
                        JsonParam({
                            'phones':
                            JsonParam([Enum('+375', '+49')], as_list=True),
                            'networks':
                            JsonParam(
                                {'name': [Enum('facebook', 'telegram')]},
                                as_list=True,
                            ),
                            'emails':
                            JsonParam([IsEmail()], as_list=True),
                            'addresses':
                            JsonParam({'street': []}, required=False),
                        }),
                    }),
                }, ),
        }, )

    DICT_SCHEMA = JsonParam(
        {
            'street':
            CompositeRule(Enum('Jakuba Kolasa')),
            'meta':
            JsonParam(
                {
                    'description':
                    JsonParam({
                        'color': [Enum('green', 'yellow', 'blue')],
                    }, ),
                    'buildings':
                    JsonParam({
                        'warehouses':
                        JsonParam({
                            'small':
                            JsonParam({
                                'count': CompositeRule(Min(0), Max(99)),
                            }),
                            'large': [Min(1), Max(10)]
                        }),
                    }),
                    'not_required':
                    JsonParam({'text': []}, required=False),
                }, ),
        }, )

    @parameterized.expand([
        # invalid
        (
            DICT_SCHEMA,
            {
                'street': 'Rampische',
                'meta': {
                    'buildings': {
                        'warehouses': {
                            'small': {
                                'count': 100,
                            },
                            'large': 0,
                        },
                    },
                },
            },
            [
                [
                    ['root', 'meta', 'buildings', 'warehouses', 'small'],
                    {
                        'count': [ValueMaxError]
                    },
                ],
                [
                    ['root', 'meta', 'buildings', 'warehouses'],
                    {
                        'large': [ValueMinError]
                    },
                ],
                [
                    ['root', 'meta'],
                    {
                        'description': RequiredJsonKeyError
                    },
                ],
                [
                    ['root'],
                    {
                        'street': [ValueEnumError],
                    },
                ],
            ],
        ),
        # valid
        (
            DICT_SCHEMA,
            {
                'country': 'Belarus',
                'city': 'Minsk',
                'street': 'Jakuba Kolasa',
                'meta': {
                    'buildings': {
                        'warehouses': {
                            'small': {
                                'count': 99,
                            },
                            'large': 1,
                        },
                    },
                    'description': {
                        'color': 'green',
                    },
                },
            },
            {},
        )
    ])
    def test_dict(self, param: JsonParam, data, exp):
        value, errors = param.validate(data)
        for ix, json_error in enumerate(errors):  # type: list, JsonError
            self.assertTrue(isinstance(json_error, JsonError))
            exp_depth, epx_errors_map = exp[ix]  # type: list, dict
            self.assertListEqual(json_error.depth, exp_depth)
            for key, error in json_error.errors.items():
                if isinstance(error, RulesError):
                    self.assertEqual(len(error.errors), len(epx_errors_map))
                    for ix_rule, rule_err in enumerate(error.errors):
                        self.assertTrue(
                            isinstance(rule_err, epx_errors_map[key][0]))
                else:
                    self.assertTrue(isinstance(error, epx_errors_map[key]))

    @parameterized.expand([
        # invalid
        (
            LIST_SCHEMA,
            {
                'person': {
                    'info': {
                        'contacts': {
                            'phones': [
                                '+375', '+49', {
                                    'code': '+420'
                                }, {
                                    'code': '+10000'
                                }
                            ],
                            'emails': [{
                                'work': 'bad_type1'
                            }, {
                                'work': 'bad_type2'
                            }, 'bad_mail'],
                            'networks': [
                                {
                                    'name': 'facebook'
                                },
                                {
                                    'name': 'insta'
                                },
                                {
                                    'name': 'linkedin'
                                },
                            ],
                        },
                    },
                },
            },
            [
                [
                    ['root', 'person', 'info', 'contacts', 'phones'],
                    {
                        2: JsonListItemTypeError,
                        3: JsonListItemTypeError,
                    },
                ],
                [
                    ['root', 'person', 'info', 'contacts', 'networks'],
                    {
                        1: {
                            'name': [ValueEnumError],
                        },
                        2: {
                            'name': [ValueEnumError],
                        },
                    },
                ],
                [
                    ['root', 'person', 'info', 'contacts', 'emails'],
                    {
                        0: JsonListItemTypeError,
                        1: JsonListItemTypeError,
                        2: [ValueEmailError],
                    },
                ],
            ],
        ),
        # valid
        (
            LIST_SCHEMA,
            {
                'person': {
                    'info': {
                        'contacts': {
                            'phones': ['+375', '+49'],
                            'networks': [
                                {
                                    'name': 'facebook'
                                },
                                {
                                    'name': 'telegram'
                                },
                                {
                                    'name': 'telegram'
                                },
                                {
                                    'name': 'facebook'
                                },
                            ],
                            'emails': ['*****@*****.**'],
                        },
                    },
                },
            },
            [],
        ),
    ])
    def test_list(self, param: JsonParam, data, exp):
        value, errors = param.validate(data)
        self.assertEqual(len(exp), len(errors))
        for err_ix, json_er in enumerate(errors):  # type: int, JsonError
            exp_err = exp[err_ix]
            exp_rule_err = exp_err[1]
            self.assertListEqual(json_er.depth, exp_err[0])
            self.assertEqual(len(exp_err[1]), len(json_er.errors))
            for rules_ix, rules_err in exp_rule_err.items():
                json_rules = json_er.errors[
                    rules_ix]  # type: dict or list or RulesError
                if isinstance(exp_rule_err[rules_ix], list):
                    self.assertTrue(isinstance(json_rules, RulesError))
                    for k, rule_err in enumerate(json_rules.errors):
                        self.assertTrue(
                            isinstance(rule_err, exp_rule_err[rules_ix][k]))
                else:
                    if isinstance(rules_err, dict):
                        self.assertTrue(len(json_rules), len(rules_err))
                    else:
                        # RulesError
                        self.assertTrue(
                            isinstance(json_er.errors[rules_ix], rules_err))
    def test_root_list_invalid(self):
        param = JsonParam(
            {
                'age': [Number()],
                'name': [
                    MinLength(1),
                ],
                'tags':
                JsonParam(
                    {'name': [MinLength(1)]}, required=False, as_list=True)
            },
            as_list=True)
        # invalid values
        _, errors = param.validate([
            {
                'age': 'ab',
                'name': 'test'
            },
            {
                'age': 'c',
                'name': ''
            },
            {
                'age': 15,
                'name': 'good'
            },
        ])

        self.assertEqual(1, len(errors))
        self.assertTrue(isinstance(errors[0], JsonError))
        error = errors[0]
        self.assertListEqual(['root'], error.depth)
        self.assertTrue(2, len(error.errors))
        self.assertTrue(error.errors[0], 1)
        self.assertTrue(error.errors[1], 2)

        self.assertTrue(
            isinstance(error.errors[0]['age'].errors[0], NumberError))
        self.assertTrue(
            isinstance(error.errors[1]['age'].errors[0], NumberError))
        self.assertTrue(
            isinstance(error.errors[1]['name'].errors[0], ValueMinLengthError))
        # invalid type - dict instead list
        _, errors = param.validate({'age': 18, 'name': 'test'})
        self.assertEqual(1, len(errors))
        self.assertListEqual(['root'], errors[0].depth)
        self.assertTrue(isinstance(errors[0], JsonListExpectedError))
        # invalid type - nested string instead list
        _, errors = param.validate([
            {
                'age': 27,
                'name': 'test'
            },
            {
                'age': 15,
                'name': 'good',
                'tags': 'bad_type'
            },
        ])

        self.assertEqual(1, len(errors))
        self.assertListEqual(['root', 'tags'], errors[0].depth)
        self.assertTrue(isinstance(errors[0], JsonListExpectedError))