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