def test_pattern_value(self): # this tests a invalid regexp pattern with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "pattern": "/@/\\"}) assert str( r.value ) == "<RuleError: error code 4: Syntax error when compiling regex pattern: None: Path: '/'>" assert r.value.error_key == 'pattern.syntax_error' # Test that pattern keyword is not allowed when using a map # with self.assertRaisesRegexp(RuleError, ".+map\.pattern.+"): with pytest.raises(RuleError) as r: Rule( schema={ "type": "map", "pattern": "^[a-z]+$", "allowempty": True, "mapping": { "name": { "type": "str" } } }) assert str( r.value ) == "<RuleError: error code 4: Keyword pattern is not allowed inside map: Path: '/'>" assert r.value.error_key == 'pattern.not_allowed_in_map' # Test that pattern value must be string otherwise exception is raised with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "pattern": 1}) assert str( r.value ) == "<RuleError: error code 4: Value of pattern keyword: '1' is not a string: Path: '/'>" assert r.value.error_key == 'pattern.not_string'
def test_assert_value(self): with pytest.raises(RuleError) as r: Rule(schema={ "type": "seq", "sequence": [{ "type": "str", "assert": 1 }] }) assert str( r.value ) == "<RuleError: error code 4: Value: '1' for keyword 'assert' is not a string: Path: '/sequence/0'>" assert r.value.error_key == 'assert.not_str' # Test that invalid characters is not present with pytest.raises(RuleError) as r: Rule( schema={ "type": "seq", "sequence": [{ "type": "str", "assert": "__import__" }] }) assert str( r.value ) == "<RuleError: error code 4: Value: '__import__' contain invalid content that is not allowed to be present in assertion keyword: Path: '/sequence/0'>" # NOQA: E501 assert r.value.error_key == 'assert.unsupported_content'
def test_type_value(self): # TODO: This test is currently semi broken, partial schemas might be somewhat broken possibly # # Test that when only having a schema; rule it should throw error # with pytest.raises(RuleError) as r: # Rule(schema={"schema;fooone": {"type": "map", "mapping": {"foo": {"type": "str"}}}}) # assert str(r.value) == "<RuleError: error code 4: Key 'type' not found in schema rule: Path: '/'>" # assert r.value.error_key == 'type.missing' # Test a valid rule with both "str" and "unicode" types work r = Rule(schema={"type": str("str")}) r = Rule(schema={"type": unicode("str")}) # Test that type key must be string otherwise exception is raised with pytest.raises(RuleError) as r: Rule(schema={"type": 1}) assert str( r.value ) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type (found int): Path: '/'>" assert r.value.error_key == 'type.not_string' # this tests that the type key must be a string with pytest.raises(RuleError) as r: Rule(schema={"type": 1}, parent=None) assert str( r.value ) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type (found int): Path: '/'>" assert r.value.error_key == 'type.not_string'
def test_build_sequence_multiple_values(self): """ Test with multiple values. """ # Test basic sequence rule r = Rule(schema={ 'type': 'seq', 'sequence': [{ 'type': 'str' }, { 'type': 'int' }] }) assert r._type == "seq" assert r._matching == "any" assert len(r._sequence) == 2 assert isinstance(r._sequence, list) assert all([ isinstance(r._sequence[i], Rule) for i in range(len(r._sequence)) ]) assert r._sequence[0]._type == "str" assert r._sequence[1]._type == "int" # Test sequence without explicit type r = Rule(schema={'sequence': [{'type': 'str'}, {'type': 'int'}]}) assert r._type == "seq" assert r._matching == "any" assert len(r._sequence) == 2 assert isinstance(r._sequence, list) assert all([ isinstance(r._sequence[i], Rule) for i in range(len(r._sequence)) ]) assert r._sequence[0]._type == "str" assert r._sequence[1]._type == "int"
def _start_validate(self, value=None): path = "" errors = [] done = [] s = {} # Look for schema; tags so they can be parsed before the root rule is parsed for k, v in self.schema.items(): if k.startswith("schema;"): Log.debug("Found partial schema; : {}".format(v)) r = Rule(schema=v) Log.debug(" Partial schema : {}".format(r)) pykwalify.partial_schemas[k.split(";", 1)[1]] = r else: # readd all items that is not schema; so they can be parsed s[k] = v self.schema = s Log.debug("Building root rule object") root_rule = Rule(schema=self.schema) self.root_rule = root_rule Log.debug("Done building root rule") Log.debug("Root rule: {}".format(self.root_rule)) self._validate(value, root_rule, path, errors, done) return errors
def test_mapping(self): # This tests mapping with a nested type and pattern r = Rule( schema={ "type": "map", "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) assert r._type == "map", "rule type is not map" assert isinstance(r._mapping, dict), "mapping is not dict" assert r._mapping[ "name"]._type == "str", "nested mapping is not of string type" assert r._mapping[ "name"]._pattern is not None, "nested mapping has no pattern var set" assert r._mapping[ "name"]._pattern == ".+@.+", "pattern is not set to correct value" # when type is specefied, 'mapping' key must be present with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "map"}) assert ex.value.msg.startswith( "map.nomapping"), "Wrong exception was raised"
def test_type_value(self): # Test that when only having a schema; rule it should throw error with pytest.raises(RuleError) as r: Rule( schema={ "schema;fooone": { "type": "map", "mapping": { "foo": { "type": "str" } } } }) assert str( r.value ) == "<RuleError: error code 4: Key 'type' not found in schema rule: Path: '/'>" assert r.value.error_key == 'type.missing' # Test that type key must be string otherwise exception is raised with pytest.raises(RuleError) as r: Rule(schema={"type": 1}) assert str( r.value ) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type: Path: '/'>" assert r.value.error_key == 'type.not_string' # this tests that the type key must be a string with pytest.raises(RuleError) as r: Rule(schema={"type": 1}, parent=None) assert str( r.value ) == "<RuleError: error code 4: Key 'type' in schema rule is not a string type: Path: '/'>" assert r.value.error_key == 'type.not_string'
def _start_validate(self, value=None): """ """ path = "" self.errors = [] done = [] s = {} # Look for schema; tags so they can be parsed before the root rule is parsed for k, v in self.schema.items(): if k.startswith("schema;"): log.debug(u"Found partial schema; : %s", v) r = Rule(schema=v) log.debug(u" Partial schema : %s", r) pykwalify.partial_schemas[k.split(";", 1)[1]] = r else: # readd all items that is not schema; so they can be parsed s[k] = v self.schema = s log.debug(u"Building root rule object") root_rule = Rule(schema=self.schema) self.root_rule = root_rule log.debug(u"Done building root rule") log.debug(u"Root rule: %s", self.root_rule) self._validate(value, root_rule, path, done)
def test_date_and_format_value(self): r = Rule(schema={"type": "date", "format": "%y"}) assert r.format is not None, "date var not set proper" assert isinstance(r.format, list), "date format should be a list" with pytest.raises(RuleError) as r: Rule(schema={"type": "date", "format": 1}) assert str( r.value ) == "<RuleError: error code 4: Value of format keyword: '1' must be a string or list or string values: Path: '/'>" with pytest.raises(RuleError) as r: Rule(schema={"type": "map", "format": "%y"}) assert str( r.value ) == "<RuleError: error code 4: Keyword format is only allowed when used with the following types: ('date',): Path: '/'>"
def test_unique_value(self): # this tests that this cannot be used in the root level with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "unique": True}) assert str( r.value ) == "<RuleError: error code 4: Keyword 'unique' can't be on root level of schema: Path: '/'>" assert r.value.error_key == 'unique.not_on_root_level' # this tests that unique cannot be used at root level with pytest.raises(RuleError) as r: Rule(schema={"type": "seq", "unique": True}) assert str( r.value ) == "<RuleError: error code 4: Type of the value: 'seq' for 'unique' keyword is not a scalar type: Path: '/'>" assert r.value.error_key == 'unique.not_scalar'
def test_nullable_value(self): # Test that nullable value must be bool otherwise exception is raised with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "nullable": "foobar"}) assert str( r.value ) == "<RuleError: error code 4: Value: 'foobar' for nullable keyword must be a boolean: Path: '/'>" assert r.value.error_key == 'nullable.not_bool'
def test_required_value(self): # Test that required value must be bool otherwise exception is raised with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "required": "foobar"}) assert str( r.value ) == "<RuleError: error code 4: Value: 'foobar' for required keyword must be a boolean: Path: '/'>" assert r.value.error_key == 'required.not_bool'
def test_unkknown_key(self): # Test that providing an unknown key raises exception with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "foobar": True}) assert str( r.value ) == "<RuleError: error code 4: Unknown key: foobar found: Path: '/'>" assert r.value.error_key == 'key.unknown'
def test_length(self): r = Rule(schema={"type": "int", "length": {"max": 10, "min": 1}}) assert r.length is not None, "length var not set proper" assert isinstance(r.length, dict), "range var is not of dict type" # this tests that the range key must be a dict with pytest.raises(RuleError) as r: Rule(schema={"type": "int", "length": []}) assert str( r.value ) == "<RuleError: error code 4: Length value is not a dict type: '[]': Path: '/'>" assert r.value.error_key == 'length.not_map' with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "length": {"max": "z"}}) assert str( r.value ) == "<RuleError: error code 4: Value: 'z' for 'max' keyword is not a number: Path: '/'>" assert r.value.error_key == 'length.max.not_number' # this tests that min is bigger then max that should not be possible with pytest.raises(RuleError) as r: Rule(schema={"type": "int", "length": {"max": 10, "min": 11}}) assert str( r.value ) == "<RuleError: error code 4: Value for 'max' can't be less then value for 'min'. 10 < 11: Path: '/'>" assert r.value.error_key == 'length.max_lt_min' # test that min-ex is bigger then max-ex, that should not be possible with pytest.raises(RuleError) as r: Rule(schema={ "type": "int", "length": { "max-ex": 10, "min-ex": 11 } }) assert str( r.value ) == "<RuleError: error code 4: Value for 'max-ex' can't be less then value for 'min-ex'. 10 <= 11: Path: '/'>" assert r.value.error_key == 'length.max-ex_le_min-ex' # test that a string has non negative boundaries with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "length": {"max": -1, "min": -2}}) assert str( r.value ) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type str.: Path: '/'>" assert r.value.error_key == 'length.min_negative' # test that a seq has non negative boundaries with pytest.raises(RuleError) as r: Rule(schema={"type": "seq", "length": {"max": 3, "min": -2}}) assert str( r.value ) == "<RuleError: error code 4: Value for 'min' can't be negative in case of type seq.: Path: '/'>" assert r.value.error_key == 'length.min_negative'
def test_allow_empty_map(self): r = Rule( schema={ "type": "map", "allowempty": True, "mapping": { "name": { "type": "str" } } }) assert r._allowempty_map is True
def _start_validate(self, value=None): path = "" errors = [] done = [] Log.debug("Building root rule object") root_rule = Rule(schema=self.schema) self.root_rule = root_rule Log.debug("Done building root rule") self._validate(value, root_rule, path, errors, done) return errors
def test_sequence(self): # Test basic sequence rule r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test sequence without explicit type r = Rule(schema={"sequence": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test short name 'seq' r = Rule(schema={"seq": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test error is raised when sequence key is missing with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "seq"}) assert ex.value.msg.startswith( "seq.nosequence"), "Wrong exception was raised" # sequence and pattern can't be used at same time with pytest.raises(SchemaConflict) as ex: Rule(schema={ "type": "seq", "sequence": [{ "type": "str" }], "pattern": "..." }) assert ex.value.msg.startswith( "seq.conflict :: pattern"), "Wrong exception was raised"
def test_assert_value(self): # this test the NYI exception for the assert key with pytest.raises(RuleError) as r: Rule(schema={ "type": "seq", "sequence": [{ "type": "str", "assert": "foobar" }] }) assert str( r.value ) == "<RuleError: error code 4: Keyword assert is not yet implemented: Path: '/sequence/0'>" assert r.value.error_key == 'assert.NotYetImplemented'
def test_enum_value(self): # this tests the various valid enum types Rule(schema={"type": "int", "enum": [1, 2, 3]}) Rule(schema={"type": "bool", "enum": [True, False]}) r = Rule(schema={"type": "str", "enum": ["a", "b", "c"]}) assert r._enum is not None, "enum var is not set proper" assert isinstance(r._enum, list), "enum is not set to a list" assert len(r._enum) == 3, "invalid length of enum entries" # this tests the missmatch between the type and the data inside a enum with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "enum": [1, 2, 3]}) assert str(r.value).startswith( "<RuleError: error code 4: Item: '1' in enum is not of correct class type:" ) assert r.value.error_key == 'enum.type.unmatch' with pytest.raises(RuleError) as r: Rule(schema={"type": "str", "enum": True}) assert str( r.value ) == "<RuleError: error code 4: Enum is not a sequence: Path: '/'>" assert r.value.error_key == 'enum.not_seq'
def test_matching_rule(self): # Test that exception is raised when a invalid matching rule is used with pytest.raises(RuleError) as r: Rule( schema={ "type": "map", "matching-rule": "foobar", "mapping": { "regex;.+": { "type": "seq", "sequence": [{ "type": "str" }] } } }) assert str( r.value ) == "<RuleError: error code 4: Specified rule in key: foobar is not part of allowed rule set : ['any', 'all']: Path: '/'>" assert r.value.error_key == 'matching_rule.not_allowed'
def test_sequence(self): # this tests seq type with a internal type of str r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) assert r._type is not None, "rule not contain type var" assert r._type == "seq", "type not 'seq'" assert r._sequence is not None, "rule not contain sequence var" assert isinstance(r._sequence, list), "rule is not a list" # Test basic sequence rule r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test sequence without explicit type r = Rule(schema={"sequence": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test short name 'seq' r = Rule(schema={"seq": [{"type": "str"}]}) assert r._type == "seq" assert isinstance(r._sequence, list) assert isinstance(r._sequence[0], Rule) assert r._sequence[0]._type == "str" # Test error is raised when sequence key is missing with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "seq"}) assert str( ex.value ) == "<SchemaConflict: error code 5: Type is sequence but no sequence alias found on same level: Path: '/'>" # sequence and pattern can't be used at same time with pytest.raises(SchemaConflict) as ex: Rule(schema={ "type": "seq", "sequence": [{ "type": "str" }], "pattern": "..." }) assert str( ex.value ) == "<SchemaConflict: error code 5: Sequence and pattern can't be on the same level in the schema: Path: '/'>"
def test_schema(self): # Test that when using both schema; and include tag that it throw an error because schema; tags should be parsed via Core() with pytest.raises(RuleError) as r: Rule( schema={ "schema;str": { "type": "map", "mapping": { "foo": { "type": "str" } } }, "type": "map", "mapping": { "foo": { "include": "str" } } }) assert str( r.value ) == "<RuleError: error code 4: Schema is only allowed on top level of schema file: Path: '/'>" assert r.value.error_key == 'schema.not.toplevel'
def test_check_conflicts(self): # TODO: This do not work and enum schema conflict is not raised... RuleError: <RuleError: error code 4: enum.notscalar> # with pytest.raises(SchemaConflict) as ex: # r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}], "enum": [1, 2, 3]}) # assert ex.value.msg.startswith("seq.conflict :: enum"), "Wrong exception was raised" # Test sequence and mapping can't be used at same level with pytest.raises(SchemaConflict) as ex: Rule( schema={ "type": "seq", "sequence": [{ "type": "str" }], "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) assert str( ex.value ) == "<SchemaConflict: error code 5: Sequence and mapping can't be on the same level in the schema: Path: '/'>" assert ex.value.error_key == 'seq.conflict.mapping' # Mapping and sequence can't used at same time with pytest.raises(SchemaConflict) as ex: Rule( schema={ "type": "map", "mapping": { "foo": { "type": "str" } }, "sequence": [{ "type": "str" }] }) assert str( ex.value ) == "<SchemaConflict: error code 5: Mapping and sequence can't be on the same level in the schema: Path: '/'>" assert ex.value.error_key == 'map.conflict.sequence' # scalar type and sequence can't be used at same time with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "int", "sequence": [{"type": "str"}]}) assert str( ex.value ) == "<SchemaConflict: error code 5: Scalar and sequence can't be on the same level in the schema: Path: '/'>" assert ex.value.error_key == 'scalar.conflict.sequence' # scalar type and mapping can't be used at same time with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "int", "mapping": {"foo": {"type": "str"}}}) assert str( ex.value ) == "<SchemaConflict: error code 5: Scalar and mapping can't be on the same level in the schema: Path: '/'>" assert ex.value.error_key == 'scalar.conflict.mapping' # scalar type and enum can't be used at same time with pytest.raises(SchemaConflict) as ex: Rule(schema={ "type": "int", "enum": [1, 2, 3], "range": { "max": 10, "min": 1 } }) assert str( ex.value ) == "<SchemaConflict: error code 5: Enum and range can't be on the same level in the schema: Path: '/'>" assert ex.value.error_key == 'enum.conflict.range'
def test_mapping(self): # This tests mapping with a nested type and pattern r = Rule( schema={ "type": "map", "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) assert r._type == "map", "rule type is not map" assert isinstance(r._mapping, dict), "mapping is not dict" assert r._mapping[ "name"]._type == "str", "nested mapping is not of string type" assert r._mapping[ "name"]._pattern is not None, "nested mapping has no pattern var set" assert r._mapping[ "name"]._pattern == ".+@.+", "pattern is not set to correct value" # when type is specefied, 'mapping' key must be present with pytest.raises(SchemaConflict) as ex: Rule(schema={"type": "map"}) assert str( ex.value ) == "<SchemaConflict: error code 5: Type is mapping but no mapping alias found on same level: Path: '/'>" # 'map' and 'enum' can't be used at same time # TODO: This do not work because it currently raises RuleError: <RuleError: error code 4: enum.notscalar> # with pytest.raises(SchemaConflict): # r = Rule(schema={"type": "map", "enum": [1, 2, 3]}) # Test that 'map' and 'mapping' can't be at the same level with pytest.raises(RuleError) as r: Rule( schema={ "map": { "stream": { "type": "any" } }, "mapping": { "seams": { "type": "any" } } }) assert str( r.value ) == "<RuleError: error code 4: Keywords 'map' and 'mapping' can't be used on the same level: Path: '/'>" assert r.value.error_key == 'mapping.duplicate_keywords' # This will test that a invalid regex will throw error when parsing rules with pytest.raises(RuleError) as r: Rule( schema={ "type": "map", "matching-rule": "any", "mapping": { "regex;(+": { "type": "seq", "sequence": [{ "type": "str" }] } } }) assert str( r.value ) == "<RuleError: error code 4: Unable to compile regex '(+': Path: '/'>" assert r.value.error_key == 'mapping.regex.compile_error' # this tests map/dict but with no elements with pytest.raises(RuleError) as r: Rule(schema={"type": "map", "mapping": {}}) assert str( r.value ) == "<RuleError: error code 4: Mapping do not contain any elements: Path: '/'>" assert r.value.error_key == 'mapping.no_elements'
def testRuleClass(self): # this tests seq type with a internal type of str r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) self.assertTrue(r._type is not None, msg="rule not contain type var") self.assertTrue(r._type == "seq", msg="type not 'seq'") self.assertTrue(r._sequence is not None, msg="rule not contain sequence var") self.assertTrue(isinstance(r._sequence, list), msg="rule is not a list") # this tests that the type key must be a string with self.assertRaises(RuleError): Rule(schema={"type": 1}, parent=None) # Test the name value r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) self.assertTrue(r._sequence[0]._type == "str", msg="first item in sequences type is not str") # This tests mapping with a nested type and pattern r = Rule( schema={ "type": "map", "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) self.assertTrue(r._type == "map", msg="rule type is not map") self.assertTrue(isinstance(r._mapping, dict), msg="mapping is not dict") self.assertTrue(r._mapping["name"]._type == "str", msg="nested mapping is not of string type") self.assertTrue(r._mapping["name"]._pattern is not None, msg="nested mapping has no pattern var set") self.assertTrue(r._mapping["name"]._pattern == ".+@.+", msg="pattern is not set to correct value") # this tests a invalid regexp pattern with self.assertRaises(RuleError): Rule(schema={"type": "str", "pattern": "/@/\\"}) # this tests the various valid enum types r = Rule(schema={"type": "int", "enum": [1, 2, 3]}) r = Rule(schema={"type": "bool", "enum": [True, False]}) r = Rule(schema={"type": "str", "enum": ["a", "b", "c"]}) self.assertTrue(r._enum is not None, msg="enum var is not set proper") self.assertTrue(isinstance(r._enum, list), msg="enum is not set to a list") self.assertTrue(len(r._enum) == 3, msg="invalid length of enum entries") # this tests the missmatch between the type and the data inside a enum with self.assertRaises(RuleError): Rule(schema={"type": "str", "enum": [1, 2, 3]}) # this test the NYI exception for the assert key with self.assertRaises(RuleError): Rule(schema={ "type": "seq", "sequence": [{ "type": "str", "assert": "foobar" }] }) r = Rule(schema={"type": "int", "range": {"max": 10, "min": 1}}) self.assertTrue(r._range is not None, msg="range var not set proper") self.assertTrue(isinstance(r._range, dict), msg="range var is not of dict type") # this tests that the range key must be a dict with self.assertRaises(RuleError): Rule(schema={"type": "int", "range": []}) Rule(schema={"type": "str", "range": {"max": "z", "min": "a"}}) # this tests that the range values is not for the string but only for int. # min/max must be the same type as the value of the type key with self.assertRaises(RuleError): Rule(schema={"type": "str", "range": {"max": 10, "min": 1}}) # this tests that min is bigger then max that should not be possible with self.assertRaises(RuleError): Rule(schema={"type": "int", "range": {"max": 10, "min": 11}}) # this tests that length works with str type r = Rule(schema={"type": "str", "length": {"max": 16, "min": 8}}) self.assertTrue(r._length is not None, msg="lenght var not set proper") self.assertTrue(isinstance(r._length, dict), msg="length var is not of dict type") # this tests that length do not work with int type with self.assertRaises(RuleError): Rule(schema={"type": "int", "length": {"max": 10, "min": 11}}) # this tests that min cannot be above max even with correct type with self.assertRaises(RuleError): Rule(schema={"type": "str", "length": {"max": 10, "min": 11}}) # this tests that this cannot be used in the root level with self.assertRaises(RuleError): Rule(schema={"type": "str", "unique": True}) # this tests that unique cannot be used at root level with self.assertRaises(RuleError): Rule(schema={"type": "seq", "unique": True}) # this tests map/dict but with no elements with self.assertRaises(RuleError): Rule(schema={"type": "map", "mapping": {}}) # This will test that a invalid regex will throw error when parsing rules with self.assertRaises(RuleError): Rule( schema={ "type": "map", "matching-rule": "any", "mapping": { "regex;(+": { "type": "seq", "sequence": [{ "type": "str" }] } } })
def testRuleClass(self): # this tests seq type with a internal type of str r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) self.assertTrue(r._type is not None, msg="rule not contain type var") self.assertTrue(r._type == "seq", msg="type not 'seq'") self.assertTrue(r._sequence is not None, msg="rule not contain sequence var") self.assertTrue(isinstance(r._sequence, list), msg="rule is not a list") # this tests that the type key must be a string with self.assertRaises(RuleError): Rule(schema={"type": 1}, parent=None) # Test the name value r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}]}) self.assertTrue(r._sequence[0]._type == "str", msg="first item in sequences type is not str") # This tests mapping with a nested type and pattern r = Rule( schema={ "type": "map", "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) self.assertTrue(r._type == "map", msg="rule type is not map") self.assertTrue(isinstance(r._mapping, dict), msg="mapping is not dict") self.assertTrue(r._mapping["name"]._type == "str", msg="nested mapping is not of string type") self.assertTrue(r._mapping["name"]._pattern is not None, msg="nested mapping has no pattern var set") self.assertTrue(r._mapping["name"]._pattern == ".+@.+", msg="pattern is not set to correct value") # this tests a invalid regexp pattern with self.assertRaises(RuleError): Rule(schema={"type": "str", "pattern": "/@/\\"}) # this tests the various valid enum types r = Rule(schema={"type": "int", "enum": [1, 2, 3]}) r = Rule(schema={"type": "bool", "enum": [True, False]}) r = Rule(schema={"type": "str", "enum": ["a", "b", "c"]}) self.assertTrue(r._enum is not None, msg="enum var is not set proper") self.assertTrue(isinstance(r._enum, list), msg="enum is not set to a list") self.assertTrue(len(r._enum) == 3, msg="invalid length of enum entries") # this tests the missmatch between the type and the data inside a enum with self.assertRaises(RuleError): Rule(schema={"type": "str", "enum": [1, 2, 3]}) # this test the NYI exception for the assert key with self.assertRaises(RuleError): Rule(schema={ "type": "seq", "sequence": [{ "type": "str", "assert": "foobar" }] }) r = Rule(schema={"type": "int", "range": {"max": 10, "min": 1}}) self.assertTrue(r._range is not None, msg="range var not set proper") self.assertTrue(isinstance(r._range, dict), msg="range var is not of dict type") # this tests that the range key must be a dict with self.assertRaises(RuleError): Rule(schema={"type": "int", "range": []}) with self.assertRaises(RuleError): Rule(schema={"type": "str", "range": {"max": "z", "min": "a"}}) # this tests that min is bigger then max that should not be possible with self.assertRaises(RuleError): Rule(schema={"type": "int", "range": {"max": 10, "min": 11}}) # test that min-ex is bigger then max-ex, that should not be possible with self.assertRaises(RuleError): Rule(schema={"type": "int", "range": {"max-ex": 10, "min-ex": 11}}) # this tests that this cannot be used in the root level with self.assertRaises(RuleError): Rule(schema={"type": "str", "unique": True}) # this tests that unique cannot be used at root level with self.assertRaises(RuleError): Rule(schema={"type": "seq", "unique": True}) # this tests map/dict but with no elements with self.assertRaises(RuleError): Rule(schema={"type": "map", "mapping": {}}) # This will test that a invalid regex will throw error when parsing rules with self.assertRaises(RuleError): Rule( schema={ "type": "map", "matching-rule": "any", "mapping": { "regex;(+": { "type": "seq", "sequence": [{ "type": "str" }] } } }) # Test that pattern keyword is not allowed when using a map with self.assertRaisesRegexp(RuleError, ".+map\.pattern.+"): Rule( schema={ "type": "map", "pattern": "^[a-z]+$", "allowempty": True, "mapping": { "name": { "type": "str" } } }) # Test that when only having a schema; rule it should throw error with self.assertRaises(RuleError): Rule( schema={ "schema;fooone": { "type": "map", "mapping": { "foo": { "type": "str" } } } }) # Test that when using both schema; and include tag that it throw an error because schema; tags should be parsed via Core() with self.assertRaises(RuleError): Rule( schema={ "schema;str": { "type": "map", "mapping": { "foo": { "type": "str" } } }, "type": "map", "mapping": { "foo": { "include": "str" } } }) # Test that exception is raised when a invalid matching rule is used with pytest.raises(RuleError) as ex: Rule( schema={ "type": "map", "matching-rule": "foobar", "mapping": { "regex;.+": { "type": "seq", "sequence": [{ "type": "str" }] } } }) assert ex.value.msg.startswith( "Specefied rule in key : foobar is not part of allowed rule set") # Test that providing an unknown key raises exception with pytest.raises(RuleError) as ex: Rule(schema={"type": "str", "foobar": True}) assert ex.value.msg.startswith("Unknown key: foobar found") # Test that type key must be string otherwise exception is raised with pytest.raises(RuleError) as ex: Rule(schema={"type": 1}) assert ex.value.msg.startswith( "key 'type' in schema rule is not a string type") # Test that required value must be bool otherwise exception is raised with pytest.raises(RuleError) as ex: Rule(schema={"type": "str", "required": "foobar"}) assert ex.value.msg.startswith("required.notbool : foobar") # Test that pattern value must be string otherwise exception is raised with pytest.raises(RuleError) as ex: Rule(schema={"type": "str", "pattern": 1}) assert ex.value.msg.startswith("pattern.notstr : 1 :") with pytest.raises(RuleError) as ex: Rule(schema={"type": "str", "enum": True}) assert ex.value.msg.startswith("enum.notseq") # Test that 'map' and 'mapping' can't be at the same level with pytest.raises(RuleError) as ex: Rule( schema={ "map": { "stream": { "type": "any" } }, "mapping": { "seams": { "type": "any" } } }) assert ex.value.msg.startswith("mapping.multiple-use")
def test_schema_conflicts(self): # TODO: Each exception must be checked what key is raised withiin it... # Test error is raised when sequence key is missing with pytest.raises(SchemaConflict) as ex: r = Rule(schema={"type": "seq"}) assert ex.value.msg.startswith( "seq.nosequence"), "Wrong exception was raised" # TODO: This do not work and enum schema conflict is not raised... RuleError: <RuleError: error code 4: enum.notscalar> # with pytest.raises(SchemaConflict) as ex: # r = Rule(schema={"type": "seq", "sequence": [{"type": "str"}], "enum": [1, 2, 3]}) # assert ex.value.msg.startswith("seq.conflict :: enum"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule(schema={ "type": "seq", "sequence": [{ "type": "str" }], "pattern": "..." }) assert ex.value.msg.startswith( "seq.conflict :: pattern"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule( schema={ "type": "seq", "sequence": [{ "type": "str" }], "mapping": { "name": { "type": "str", "pattern": ".+@.+" } } }) assert ex.value.msg.startswith( "seq.conflict :: mapping"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule(schema={"type": "map"}) assert ex.value.msg.startswith( "map.nomapping"), "Wrong exception was raised" # TODO: This do not work because it currently raises RuleError: <RuleError: error code 4: enum.notscalar> # with pytest.raises(SchemaConflict): # r = Rule(schema={"type": "map", "enum": [1, 2, 3]}) with pytest.raises(SchemaConflict) as ex: r = Rule( schema={ "type": "map", "mapping": { "foo": { "type": "str" } }, "sequence": [{ "type": "str" }] }) assert ex.value.msg.startswith( "map.conflict :: mapping"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule(schema={"type": "int", "sequence": [{"type": "str"}]}) assert ex.value.msg.startswith( "scalar.conflict :: sequence"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule(schema={ "type": "int", "mapping": { "foo": { "type": "str" } } }) assert ex.value.msg.startswith( "scalar.conflict :: mapping"), "Wrong exception was raised" with pytest.raises(SchemaConflict) as ex: r = Rule(schema={ "type": "int", "enum": [1, 2, 3], "range": { "max": 10, "min": 1 } }) assert ex.value.msg.startswith( "enum.conflict :: range"), "Wrong exception was raised"
def test_name_value(self): with pytest.raises(RuleError) as r: Rule(schema={'type': 'str', 'name': {}}) assert str( r.value ) == "<RuleError: error code 4: Value: {} for keyword name must be a string: Path: '/'>"
def _validate_mapping(self, value, rule, path, done=None): """ """ log.debug(u"Validate mapping") log.debug(u" Mapping : Data: %s", value) log.debug(u" Mapping : Rule: %s", rule) log.debug(u" Mapping : RuleType: %s", rule.type) log.debug(u" Mapping : Path: %s", path) log.debug(u" Mapping : Seq: %s", rule.sequence) log.debug(u" Mapping : Map: %s", rule.mapping) if not isinstance(value, dict): self.errors.append(SchemaError.SchemaErrorEntry( u"Value '{value}' is not a dict. Value path: '{path}'", path, value, )) return if rule.mapping is None: log.debug(u" + No rule to apply, prolly because of allowempty: True") return # Handle 'func' argument on this mapping self._handle_func(value, rule, path, done) m = rule.mapping log.debug(u" Mapping: Rule-Mapping: %s", m) if rule.range is not None: r = rule.range self._validate_range( r.get("max"), r.get("min"), r.get("max-ex"), r.get("min-ex"), len(value), path, "map", ) for k, rr in m.items(): # Handle if the value of the key contains a include keyword if rr.include_name is not None: include_name = rr.include_name partial_schema_rule = pykwalify.partial_schemas.get(include_name) if not partial_schema_rule: self.errors.append(SchemaError.SchemaErrorEntry( msg=u"Cannot find partial schema with name '{include_name}'. Existing partial schemas: '{existing_schemas}'. Path: '{path}'", path=path, value=value, include_name=include_name, existing_schemas=", ".join(sorted(pykwalify.partial_schemas.keys())))) return include_rule = Rule() include_rule.mapping = {k: partial_schema_rule} include_rule.regex_mappings = [] return self._validate(value, include_rule, u"{0}".format(path), done) # Find out if this is a regex rule is_regex_rule = False required_regex = "" for regex_rule in rule.regex_mappings: if k == "regex;({})".format(regex_rule.map_regex_rule) or k == "re;({})".format(regex_rule.map_regex_rule): is_regex_rule = True required_regex = regex_rule.map_regex_rule # Check for the presense of the required key is_present = False if not is_regex_rule: is_present = k in value else: is_present = any([re.search(required_regex, v) for v in value]) # Specifying =: as key is considered the "default" if no other keys match if rr.required and not is_present and k != "=": self.errors.append(SchemaError.SchemaErrorEntry( msg=u"Cannot find required key '{key}'. Path: '{path}'", path=path, value=value, key=k)) if k not in value and rr.default is not None: value[k] = rr.default for k, v in value.items(): # If no other case was a match, check if a default mapping is valid/present and use # that one instead r = m.get(k, m.get('=')) log.debug(u" Mapping-value : %s", m) log.debug(u" Mapping-value : %s %s", k, v) log.debug(u" Mapping-value : %s", r) regex_mappings = [(regex_rule, re.search(regex_rule.map_regex_rule, str(k))) for regex_rule in rule.regex_mappings] log.debug(u" Mapping-value: Mapping Regex matches: %s", regex_mappings) if r is not None: # validate recursively log.debug(u" Mapping-value: Core Map: validate recursively: %s", r) self._validate(v, r, u"{0}/{1}".format(path, k), done) elif any(regex_mappings): sub_regex_result = [] # Found at least one that matches a mapping regex for mm in regex_mappings: if mm[1]: log.debug(u" Mapping-value: Matching regex patter: %s", mm[0]) self._validate(v, mm[0], "{0}/{1}".format(path, k), done) sub_regex_result.append(True) else: sub_regex_result.append(False) if rule.matching_rule == "any": if any(sub_regex_result): log.debug(u" Mapping-value: Matched at least one regex") else: log.debug(u" Mapping-value: No regex matched") self.errors.append(SchemaError.SchemaErrorEntry( msg=u"Key '{key}' does not match any regex '{regex}'. Path: '{path}'", path=path, value=value, key=k, regex="' or '".join(sorted([mm[0].map_regex_rule for mm in regex_mappings])))) elif rule.matching_rule == "all": if all(sub_regex_result): log.debug(u" Mapping-value: Matched all regex rules") else: log.debug(u" Mapping-value: Did not match all regex rules") self.errors.append(SchemaError.SchemaErrorEntry( msg=u"Key '{key}' does not match all regex '{regex}'. Path: '{path}'", path=path, value=value, key=k, regex="' and '".join(sorted([mm[0].map_regex_rule for mm in regex_mappings])))) else: log.debug(u" Mapping-value: No mapping rule defined") else: if not rule.allowempty_map: self.errors.append(SchemaError.SchemaErrorEntry( msg=u"Key '{key}' was not defined. Path: '{path}'", path=path, value=value, key=k))
def test_example_value(self): with pytest.raises(RuleError) as r: Rule(schema={'type': 'str', 'example': []}) assert str( r.value ) == "<RuleError: error code 4: Value: [] for keyword example must be a string: Path: '/'>"