1# -*- coding: UTF-8 -*-
2#
3# The MIT License
4#
5# Copyright (c) 2010, 2014 Felix Schwarz <felix.schwarz@oss.schwarz.eu>
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23# THE SOFTWARE.
24
25from pycerberus.api import Validator
26from pycerberus.errors import InvalidDataError
27from pycerberus.lib import PythonicTestCase
28from pycerberus.lib.pythonic_testcase import *
29from pycerberus.schema import SchemaValidator
30from pycerberus.test_util import ValidationTest
31from pycerberus.validators import IntegerValidator, StringValidator
32
33
34
35class DeclarativeSchemaTest(ValidationTest):
36
37    class DeclarativeSchema(SchemaValidator):
38        allow_additional_parameters = False
39
40        id = IntegerValidator()
41        amount = IntegerValidator
42        formvalidators = (Validator(), )
43    validator_class = DeclarativeSchema
44
45    def schema(self, schema_class=None, **kwargs):
46        if schema_class is None:
47            schema_class = self.__class__.DeclarativeSchema
48        return schema_class(**kwargs)
49
50    # -------------------------------------------------------------------------
51    # setup / introspection
52
53    def test_knows_its_fieldvalidators(self):
54        self.assert_contains('id', self.schema().fieldvalidators().keys())
55
56    def test_also_can_use_validator_classes(self):
57        self.assert_contains('amount', self.schema().fieldvalidators().keys())
58        self.assert_equals(set(['id', 'amount']), set(self.schema().fieldvalidators().keys()))
59
60    def test_instance_uses_instances_of_validators_declared_as_class(self):
61        first = self.schema().validator_for('amount')
62        second = self.schema().validator_for('amount')
63        self.assert_not_equals(first, second)
64
65    def test_declared_validators_are_no_class_attributes_after_initialization(self):
66        for fieldname in self.schema().fieldvalidators():
67            self.assert_false(hasattr(self.schema(), fieldname))
68
69    def test_can_have_formvalidators(self):
70        assert_callable(self.schema().formvalidators)
71        assert_length(1, self.schema().formvalidators())
72
73    # -------------------------------------------------------------------------
74    # warn about additional parameters
75
76    def test_can_bail_out_if_additional_items_are_detected(self):
77        e = self.assert_raises(InvalidDataError, lambda: self.schema().process(dict(id=42, amount=21, extra='foo')))
78        self.assert_equals('Undeclared field detected: "extra".', e.msg())
79
80    def test_can_override_additional_items_parameter_on_class_instantiation(self):
81        schema = self.schema(allow_additional_parameters=True)
82        schema.process(dict(id=42, amount=21, extra='foo'))
83
84    def test_additional_items_parameter_is_inherited_for_schema_subclasses(self):
85        class DerivedSchema(self.DeclarativeSchema):
86            pass
87        schema = DerivedSchema()
88        self.assert_raises(InvalidDataError, lambda: schema.process(dict(id=42, amount=21, extra='foo')))
89
90    # -------------------------------------------------------------------------
91    # pass unvalidated parameters
92
93    class PassValuesSchema(SchemaValidator):
94        filter_unvalidated_parameters=False
95        id = IntegerValidator()
96
97    def test_passes_unvalidated_parameters_if_specified(self):
98        self.init_validator(self.PassValuesSchema())
99        assert_equals({'id': 42, 'foo': 'bar'}, self.process({'id': '42', 'foo': 'bar'}))
100
101    def test_filter_unvalidated_parameter_is_inherited_for_schema_subclasses(self):
102        class DerivedSchema(self.PassValuesSchema):
103            pass
104
105        self.init_validator(DerivedSchema())
106        assert_equals({'id': 42, 'foo': 'bar'}, self.process({'id': '42', 'foo': 'bar'}))
107
108    def test_can_override_filter_unvalidated_parameter_on_class_instantiation(self):
109        self.init_validator(self.PassValuesSchema(filter_unvalidated_parameters=True))
110        assert_equals({'id': 42}, self.process({'id': '42', 'foo': 'bar'}))
111
112    # -------------------------------------------------------------------------
113    # sub-schemas
114
115    def test_can_declare_schema_with_subschema(self):
116        class PersonSchema(SchemaValidator):
117            first_name = StringValidator
118            last_name = StringValidator
119
120        class UserSchema(SchemaValidator):
121            user = PersonSchema
122        self.init_validator(UserSchema())
123
124        user_input = {'user': {'first_name': 'Foo', 'last_name': 'Bar'}}
125        assert_equals(user_input, self.process(user_input))
126
127
128
129