# -*- coding: utf-8 -*- # pylint: disable=function-redefined,missing-docstring # TODO: Remove the following PyLint disable as soon as we support YAML and RST rendering # pylint: disable=abstract-method # Import python libs from __future__ import absolute_import import copy import json import yaml from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf # Import Salt Libs from salt.utils import schema # Import 3rd-party libs try: import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False JSONSCHEMA_VERSION = _LooseVersion('0') # pylint: disable=unused-import try: import rfc3987 HAS_RFC3987 = True except ImportError: HAS_RFC3987 = False try: import strict_rfc3339 HAS_STRICT_RFC3339 = True except ImportError: HAS_STRICT_RFC3339 = False try: import isodate HAS_ISODATE = True except ImportError: HAS_ISODATE = False # pylint: enable=unused-import class ConfigTestCase(TestCase): ''' TestCase for salt.utils.config module ''' def test_configuration_subclass_inherits_items(self): class BaseConfig(schema.Schema): base = schema.BooleanItem(default=True, required=True) class SubClassedConfig(BaseConfig): hungry = schema.BooleanItem(title='Hungry', description='Are you hungry?', required=True) self.assertDictEqual( SubClassedConfig.serialize(), { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', 'properties': { 'base': { 'default': True, 'type': 'boolean', 'title': 'base' }, 'hungry': { 'type': 'boolean', 'description': 'Are you hungry?', 'title': 'Hungry' } }, 'required': ['base', 'hungry'], 'x-ordering': ['base', 'hungry'], 'additionalProperties': False, } ) class MergedConfigClass(schema.Schema): thirsty = schema.BooleanItem(title='Thirsty', description='Are you thirsty?', required=True) merge_subclassed = SubClassedConfig(flatten=True) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', 'properties': { 'thirsty': { 'type': 'boolean', 'description': 'Are you thirsty?', 'title': 'Thirsty' }, 'base': { 'default': True, 'type': 'boolean', 'title': 'base' }, 'hungry': { 'type': 'boolean', 'description': 'Are you hungry?', 'title': 'Hungry' } }, 'required': ['thirsty', 'base', 'hungry'], 'x-ordering': ['thirsty', 'base', 'hungry'], 'additionalProperties': False, } self.assertDictContainsSubset( MergedConfigClass.serialize()['properties'], expected['properties'] ) self.assertDictContainsSubset( expected, MergedConfigClass.serialize() ) def test_configuration_items_order(self): class One(schema.Schema): one = schema.BooleanItem() class Three(schema.Schema): three = schema.BooleanItem() class Final(One): two = schema.BooleanItem() three = Three(flatten=True) self.assertEqual(Final.serialize()['x-ordering'], ['one', 'two', 'three']) def test_optional_requirements_config(self): class BaseRequirements(schema.Schema): driver = schema.StringItem(default='digital_ocean', format='hidden') class SSHKeyFileSchema(schema.Schema): ssh_key_file = schema.StringItem( title='SSH Private Key', description='The path to an SSH private key which will be used ' 'to authenticate on the deployed VMs', ) class SSHKeyNamesSchema(schema.Schema): ssh_key_names = schema.StringItem( title='SSH Key Names', description='The names of an SSH key being managed on ' 'Digital Ocean account which will be used to ' 'authenticate on the deployed VMs', ) class Requirements(BaseRequirements): title = 'Digital Ocean' description = 'Digital Ocean Cloud VM configuration requirements.' personal_access_token = schema.StringItem( title='Personal Access Token', description='This is the API access token which can be generated ' 'under the API/Application on your account', required=True) requirements_definition = schema.AnyOfItem( items=( SSHKeyFileSchema.as_requirements_item(), SSHKeyNamesSchema.as_requirements_item() ), )(flatten=True) ssh_key_file = SSHKeyFileSchema(flatten=True) ssh_key_names = SSHKeyNamesSchema(flatten=True) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Digital Ocean', 'description': 'Digital Ocean Cloud VM configuration requirements.', 'type': 'object', 'properties': { 'driver': { 'default': 'digital_ocean', 'format': 'hidden', 'type': 'string', 'title': 'driver' }, 'personal_access_token': { 'type': 'string', 'description': 'This is the API access token which can be ' 'generated under the API/Application on your account', 'title': 'Personal Access Token' }, 'ssh_key_file': { 'type': 'string', 'description': 'The path to an SSH private key which will ' 'be used to authenticate on the deployed VMs', 'title': 'SSH Private Key' }, 'ssh_key_names': { 'type': 'string', 'description': 'The names of an SSH key being managed on Digital ' 'Ocean account which will be used to authenticate ' 'on the deployed VMs', 'title': 'SSH Key Names' } }, 'anyOf': [ {'required': ['ssh_key_file']}, {'required': ['ssh_key_names']} ], 'required': [ 'personal_access_token' ], 'x-ordering': [ 'driver', 'personal_access_token', 'ssh_key_file', 'ssh_key_names', ], 'additionalProperties': False } self.assertDictEqual(expected, Requirements.serialize()) class Requirements2(BaseRequirements): title = 'Digital Ocean' description = 'Digital Ocean Cloud VM configuration requirements.' personal_access_token = schema.StringItem( title='Personal Access Token', description='This is the API access token which can be generated ' 'under the API/Application on your account', required=True) ssh_key_file = schema.StringItem( title='SSH Private Key', description='The path to an SSH private key which will be used ' 'to authenticate on the deployed VMs') ssh_key_names = schema.StringItem( title='SSH Key Names', description='The names of an SSH key being managed on ' 'Digital Ocean account which will be used to ' 'authenticate on the deployed VMs') requirements_definition = schema.AnyOfItem( items=( schema.RequirementsItem(requirements=['ssh_key_file']), schema.RequirementsItem(requirements=['ssh_key_names']) ), )(flatten=True) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Digital Ocean', 'description': 'Digital Ocean Cloud VM configuration requirements.', 'type': 'object', 'properties': { 'driver': { 'default': 'digital_ocean', 'format': 'hidden', 'type': 'string', 'title': 'driver' }, 'personal_access_token': { 'type': 'string', 'description': 'This is the API access token which can be ' 'generated under the API/Application on your account', 'title': 'Personal Access Token' }, 'ssh_key_file': { 'type': 'string', 'description': 'The path to an SSH private key which will ' 'be used to authenticate on the deployed VMs', 'title': 'SSH Private Key' }, 'ssh_key_names': { 'type': 'string', 'description': 'The names of an SSH key being managed on Digital ' 'Ocean account which will be used to authenticate ' 'on the deployed VMs', 'title': 'SSH Key Names' } }, 'anyOf': [ {'required': ['ssh_key_file']}, {'required': ['ssh_key_names']} ], 'required': [ 'personal_access_token' ], 'x-ordering': [ 'driver', 'personal_access_token', 'ssh_key_file', 'ssh_key_names', ], 'additionalProperties': False } self.assertDictContainsSubset(expected, Requirements2.serialize()) class Requirements3(schema.Schema): title = 'Digital Ocean' description = 'Digital Ocean Cloud VM configuration requirements.' merge_reqs = Requirements(flatten=True) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Digital Ocean', 'description': 'Digital Ocean Cloud VM configuration requirements.', 'type': 'object', 'properties': { 'driver': { 'default': 'digital_ocean', 'format': 'hidden', 'type': 'string', 'title': 'driver' }, 'personal_access_token': { 'type': 'string', 'description': 'This is the API access token which can be ' 'generated under the API/Application on your account', 'title': 'Personal Access Token' }, 'ssh_key_file': { 'type': 'string', 'description': 'The path to an SSH private key which will ' 'be used to authenticate on the deployed VMs', 'title': 'SSH Private Key' }, 'ssh_key_names': { 'type': 'string', 'description': 'The names of an SSH key being managed on Digital ' 'Ocean account which will be used to authenticate ' 'on the deployed VMs', 'title': 'SSH Key Names' } }, 'anyOf': [ {'required': ['ssh_key_file']}, {'required': ['ssh_key_names']} ], 'required': [ 'personal_access_token' ], 'x-ordering': [ 'driver', 'personal_access_token', 'ssh_key_file', 'ssh_key_names', ], 'additionalProperties': False } self.assertDictContainsSubset(expected, Requirements3.serialize()) class Requirements4(schema.Schema): title = 'Digital Ocean' description = 'Digital Ocean Cloud VM configuration requirements.' merge_reqs = Requirements(flatten=True) ssh_key_file_2 = schema.StringItem( title='SSH Private Key', description='The path to an SSH private key which will be used ' 'to authenticate on the deployed VMs') ssh_key_names_2 = schema.StringItem( title='SSH Key Names', description='The names of an SSH key being managed on ' 'Digital Ocean account which will be used to ' 'authenticate on the deployed VMs') requirements_definition_2 = schema.AnyOfItem( items=( schema.RequirementsItem(requirements=['ssh_key_file_2']), schema.RequirementsItem(requirements=['ssh_key_names_2']) ), )(flatten=True) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Digital Ocean', 'description': 'Digital Ocean Cloud VM configuration requirements.', 'type': 'object', 'properties': { 'driver': { 'default': 'digital_ocean', 'format': 'hidden', 'type': 'string', 'title': 'driver' }, 'personal_access_token': { 'type': 'string', 'description': 'This is the API access token which can be ' 'generated under the API/Application on your account', 'title': 'Personal Access Token' }, 'ssh_key_file': { 'type': 'string', 'description': 'The path to an SSH private key which will ' 'be used to authenticate on the deployed VMs', 'title': 'SSH Private Key' }, 'ssh_key_names': { 'type': 'string', 'description': 'The names of an SSH key being managed on Digital ' 'Ocean account which will be used to authenticate ' 'on the deployed VMs', 'title': 'SSH Key Names' }, 'ssh_key_file_2': { 'type': 'string', 'description': 'The path to an SSH private key which will ' 'be used to authenticate on the deployed VMs', 'title': 'SSH Private Key' }, 'ssh_key_names_2': { 'type': 'string', 'description': 'The names of an SSH key being managed on Digital ' 'Ocean account which will be used to authenticate ' 'on the deployed VMs', 'title': 'SSH Key Names' } }, 'anyOf': [ {'required': ['ssh_key_file']}, {'required': ['ssh_key_names']}, {'required': ['ssh_key_file_2']}, {'required': ['ssh_key_names_2']} ], 'required': [ 'personal_access_token' ], 'x-ordering': [ 'driver', 'personal_access_token', 'ssh_key_file', 'ssh_key_names', 'ssh_key_file_2', 'ssh_key_names_2', ], 'additionalProperties': False } self.assertDictContainsSubset(expected, Requirements4.serialize()) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_optional_requirements_config_validation(self): class BaseRequirements(schema.Schema): driver = schema.StringItem(default='digital_ocean', format='hidden') class SSHKeyFileSchema(schema.Schema): ssh_key_file = schema.StringItem( title='SSH Private Key', description='The path to an SSH private key which will be used ' 'to authenticate on the deployed VMs') class SSHKeyNamesSchema(schema.Schema): ssh_key_names = schema.StringItem( title='SSH Key Names', description='The names of an SSH key being managed on ' 'Digial Ocean account which will be used to ' 'authenticate on the deployed VMs') class Requirements(BaseRequirements): title = 'Digital Ocean' description = 'Digital Ocean Cloud VM configuration requirements.' personal_access_token = schema.StringItem( title='Personal Access Token', description='This is the API access token which can be generated ' 'under the API/Application on your account', required=True) requirements_definition = schema.AnyOfItem( items=( SSHKeyFileSchema.as_requirements_item(), SSHKeyNamesSchema.as_requirements_item() ), )(flatten=True) ssh_key_file = SSHKeyFileSchema(flatten=True) ssh_key_names = SSHKeyNamesSchema(flatten=True) try: jsonschema.validate( {'personal_access_token': 'foo', 'ssh_key_names': 'bar', 'ssh_key_file': 'test'}, Requirements.serialize() ) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate( {'personal_access_token': 'foo', 'ssh_key_names': 'bar'}, Requirements.serialize() ) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate( {'personal_access_token': 'foo', 'ssh_key_file': 'test'}, Requirements.serialize() ) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( {'personal_access_token': 'foo'}, Requirements.serialize() ) self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) def test_boolean_config(self): item = schema.BooleanItem(title='Hungry', description='Are you hungry?') self.assertDictEqual( item.serialize(), { 'type': 'boolean', 'title': item.title, 'description': item.description } ) item = schema.BooleanItem(title='Hungry', description='Are you hungry?', default=False) self.assertDictEqual( item.serialize(), { 'type': 'boolean', 'title': item.title, 'description': item.description, 'default': item.default } ) item = schema.BooleanItem(title='Hungry', description='Are you hungry?', default=schema.Null) self.assertDictEqual( item.serialize(), { 'type': 'boolean', 'title': item.title, 'description': item.description, 'default': None } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_boolean_config_validation(self): class TestConf(schema.Schema): item = schema.BooleanItem(title='Hungry', description='Are you hungry?') try: jsonschema.validate({'item': False}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 1}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) def test_string_config(self): item = schema.StringItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description } ) item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3) self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'minLength': item.min_length, 'maxLength': item.max_length } ) item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3, default='foo') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'minLength': item.min_length, 'maxLength': item.max_length, 'default': 'foo' } ) item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3, enum=('foo', 'bar')) self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'minLength': item.min_length, 'maxLength': item.max_length, 'enum': ['foo', 'bar'] } ) item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3, enum=('foo', 'bar'), enumNames=('Foo', 'Bar')) self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'minLength': item.min_length, 'maxLength': item.max_length, 'enum': ['foo', 'bar'], 'enumNames': ['Foo', 'Bar'] } ) item = schema.StringItem(title='Foo', description='Foo Item', pattern=r'^([\w_-]+)$') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'pattern': item.pattern } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_string_config_validation(self): class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item') try: jsonschema.validate({'item': 'the item'}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=10) try: jsonschema.validate({'item': 'the item'}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 'the item the item'}, TestConf.serialize()) self.assertIn('is too long', excinfo.exception.message) class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item', min_length=10, max_length=100) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 'the item'}, TestConf.serialize()) self.assertIn('is too short', excinfo.exception.message) class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item', enum=('foo', 'bar')) try: jsonschema.validate({'item': 'foo'}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item', enum=('foo', 'bar')) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 'bin'}, TestConf.serialize()) self.assertIn('is not one of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.StringItem(title='Foo', description='Foo Item', pattern=r'^([\w_-]+)$') try: jsonschema.validate({'item': 'the-item'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 'the item'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('does not match', excinfo.exception.message) def test_email_config(self): item = schema.EMailItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_email_config_validation(self): class TestConf(schema.Schema): item = schema.EMailItem(title='Item', description='Item description') try: jsonschema.validate({'item': 'nobody@nowhere.com'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_ipv4_config(self): item = schema.IPv4Item(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(JSONSCHEMA_VERSION <= _LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') def test_ipv4_config_validation(self): class TestConf(schema.Schema): item = schema.IPv4Item(title='Item', description='Item description') try: jsonschema.validate({'item': '127.0.0.1'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_ipv6_config(self): item = schema.IPv6Item(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_ipv6_config_validation(self): class TestConf(schema.Schema): item = schema.IPv6Item(title='Item', description='Item description') try: jsonschema.validate({'item': '::1'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_hostname_config(self): item = schema.HostnameItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_hostname_config_validation(self): class TestConf(schema.Schema): item = schema.HostnameItem(title='Item', description='Item description') try: jsonschema.validate({'item': 'localhost'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_datetime_config(self): item = schema.DateTimeItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') @skipIf(any([HAS_ISODATE, HAS_STRICT_RFC3339]) is False, 'The \'strict_rfc3339\' or \'isodate\' library is missing') def test_datetime_config_validation(self): class TestConf(schema.Schema): item = schema.DateTimeItem(title='Item', description='Item description') try: jsonschema.validate({'item': '2015-07-01T18:05:27+01:00'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_secret_config(self): item = schema.SecretItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) def test_uri_config(self): item = schema.UriItem(title='Foo', description='Foo Item') self.assertDictEqual( item.serialize(), { 'type': 'string', 'title': item.title, 'description': item.description, 'format': item.format } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') @skipIf(HAS_RFC3987 is False, 'The \'rfc3987\' library is missing') def test_uri_config_validation(self): class TestConf(schema.Schema): item = schema.UriItem(title='Item', description='Item description') try: jsonschema.validate({'item': 'ssh://localhost'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not a', excinfo.exception.message) def test_number_config(self): item = schema.NumberItem(title='How many dogs', description='Question') self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description } ) item = schema.NumberItem(title='How many dogs', description='Question', minimum=0, maximum=10) self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum } ) item = schema.NumberItem(title='How many dogs', description='Question', multiple_of=2) self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description, 'multipleOf': item.multiple_of } ) item = schema.NumberItem(title='How many dogs', description='Question', minimum=0, exclusive_minimum=True, maximum=10, exclusive_maximum=True) self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'exclusiveMinimum': True, 'exclusiveMaximum': True } ) item = schema.NumberItem(title='How many dogs', description='Question', minimum=0, maximum=10, default=0) self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'default': 0 } ) item = schema.NumberItem(title='How many dogs', description='Question', minimum=0, maximum=10, default=0, enum=(0, 2, 4, 6)) self.assertDictEqual( item.serialize(), { 'type': 'number', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'default': 0, 'enum': [0, 2, 4, 6] } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_number_config_validation(self): class TestConf(schema.Schema): item = schema.NumberItem(title='How many dogs', description='Question') try: jsonschema.validate({'item': 2}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': '3'}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.NumberItem(title='How many dogs', description='Question', multiple_of=2.2) try: jsonschema.validate({'item': 4.4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 4}, TestConf.serialize()) self.assertIn('is not a multiple of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.NumberItem(title='Foo', description='Foo Item', minimum=1, maximum=10) try: jsonschema.validate({'item': 3}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 11}, TestConf.serialize()) self.assertIn('is greater than the maximum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.NumberItem(title='Foo', description='Foo Item', minimum=10, maximum=100) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is less than the minimum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.NumberItem(title='How many dogs', description='Question', minimum=0, exclusive_minimum=True, maximum=10, exclusive_maximum=True) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 0}, TestConf.serialize()) self.assertIn('is less than or equal to the minimum of', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 10}, TestConf.serialize()) self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.NumberItem(title='Foo', description='Foo Item', enum=(0, 2, 4, 6)) try: jsonschema.validate({'item': 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) class TestConf(schema.Schema): item = schema.NumberItem(title='Foo', description='Foo Item', enum=(0, 2, 4, 6)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is not one of', excinfo.exception.message) def test_integer_config(self): item = schema.IntegerItem(title='How many dogs', description='Question') self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description } ) item = schema.IntegerItem(title='How many dogs', description='Question', minimum=0, maximum=10) self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum } ) item = schema.IntegerItem(title='How many dogs', description='Question', multiple_of=2) self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description, 'multipleOf': item.multiple_of } ) item = schema.IntegerItem(title='How many dogs', description='Question', minimum=0, exclusive_minimum=True, maximum=10, exclusive_maximum=True) self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'exclusiveMinimum': True, 'exclusiveMaximum': True } ) item = schema.IntegerItem(title='How many dogs', description='Question', minimum=0, maximum=10, default=0) self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'default': 0 } ) item = schema.IntegerItem(title='How many dogs', description='Question', minimum=0, maximum=10, default=0, enum=(0, 2, 4, 6)) self.assertDictEqual( item.serialize(), { 'type': 'integer', 'title': item.title, 'description': item.description, 'minimum': item.minimum, 'maximum': item.maximum, 'default': 0, 'enum': [0, 2, 4, 6] } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_integer_config_validation(self): class TestConf(schema.Schema): item = schema.IntegerItem(title='How many dogs', description='Question') try: jsonschema.validate({'item': 2}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3.1}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.IntegerItem(title='How many dogs', description='Question', multiple_of=2) try: jsonschema.validate({'item': 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is not a multiple of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.IntegerItem(title='Foo', description='Foo Item', minimum=1, maximum=10) try: jsonschema.validate({'item': 3}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 11}, TestConf.serialize()) self.assertIn('is greater than the maximum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.IntegerItem(title='Foo', description='Foo Item', minimum=10, maximum=100) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is less than the minimum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.IntegerItem(title='How many dogs', description='Question', minimum=0, exclusive_minimum=True, maximum=10, exclusive_maximum=True) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 0}, TestConf.serialize()) self.assertIn('is less than or equal to the minimum of', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 10}, TestConf.serialize()) self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message) class TestConf(schema.Schema): item = schema.IntegerItem(title='Foo', description='Foo Item', enum=(0, 2, 4, 6)) try: jsonschema.validate({'item': 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) class TestConf(schema.Schema): item = schema.IntegerItem(title='Foo', description='Foo Item', enum=(0, 2, 4, 6)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 3}, TestConf.serialize()) self.assertIn('is not one of', excinfo.exception.message) def test_array_config(self): string_item = schema.StringItem(title='Dog Name', description='The dog name') item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=string_item) self.assertDictEqual( item.serialize(), { 'type': 'array', 'title': item.title, 'description': item.description, 'items': { 'type': 'string', 'title': string_item.title, 'description': string_item.description } } ) integer_item = schema.IntegerItem(title='Dog Age', description='The dog age') item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=(string_item, integer_item)) self.assertDictEqual( item.serialize(), { 'type': 'array', 'title': item.title, 'description': item.description, 'items': [ { 'type': 'string', 'title': string_item.title, 'description': string_item.description }, { 'type': 'integer', 'title': integer_item.title, 'description': integer_item.description } ] } ) item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=(schema.StringItem(), schema.IntegerItem()), min_items=1, max_items=3, additional_items=False, unique_items=True) self.assertDictEqual( item.serialize(), { 'type': 'array', 'title': item.title, 'description': item.description, 'minItems': item.min_items, 'maxItems': item.max_items, 'uniqueItems': item.unique_items, 'additionalItems': item.additional_items, 'items': [ { 'type': 'string', }, { 'type': 'integer', } ] } ) class HowManyConfig(schema.Schema): item = schema.IntegerItem(title='How many dogs', description='Question') item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=HowManyConfig()) self.assertDictEqual( item.serialize(), { 'type': 'array', 'title': item.title, 'description': item.description, 'items': HowManyConfig.serialize() } ) class AgesConfig(schema.Schema): item = schema.IntegerItem() item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=(HowManyConfig(), AgesConfig())) self.assertDictEqual( item.serialize(), { 'type': 'array', 'title': item.title, 'description': item.description, 'items': [ HowManyConfig.serialize(), AgesConfig.serialize() ] } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_array_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=schema.StringItem()) try: jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['Tobias', 'Óscar', 3]}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=schema.StringItem(), min_items=1, max_items=2) try: jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['Tobias', 'Óscar', 'Pepe']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is too long', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': []}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is too short', excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem(title='Dog Names', description='Name your dogs', items=schema.StringItem(), uniqueItems=True) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['Tobias', 'Tobias']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('has non-unique elements', excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem(items=(schema.StringItem(), schema.IntegerItem())) try: jsonschema.validate({'item': ['Óscar', 4]}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem( items=schema.ArrayItem( items=(schema.StringItem(), schema.IntegerItem()) ) ) try: jsonschema.validate({'item': [['Tobias', 8], ['Óscar', 4]]}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': [['Tobias', 8], ['Óscar', '4']]}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem(items=schema.StringItem(enum=['Tobias', 'Óscar'])) try: jsonschema.validate({'item': ['Óscar']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': ['Tobias']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['Pepe']}, TestConf.serialize(), format_checker=jsonschema.FormatChecker()) self.assertIn('is not one of', excinfo.exception.message) def test_dict_config(self): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem() } ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'properties': { 'sides': {'type': 'integer'} } } ) item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem() }, min_properties=1, max_properties=2 ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'properties': { 'sides': {'type': 'integer'} }, 'minProperties': 1, 'maxProperties': 2 } ) item = schema.DictItem( title='Poligon', description='Describe the Poligon', pattern_properties={ 's.*': schema.IntegerItem() }, min_properties=1, max_properties=2 ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'patternProperties': { 's.*': {'type': 'integer'} }, 'minProperties': 1, 'maxProperties': 2 } ) item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, pattern_properties={ 's*': schema.IntegerItem() }, min_properties=1, max_properties=2 ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'properties': { 'color': { 'type': 'string', 'enum': ['red', 'green', 'blue'] } }, 'patternProperties': { 's*': {'type': 'integer'} }, 'minProperties': 1, 'maxProperties': 2 } ) item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, pattern_properties={ 's*': schema.IntegerItem() }, additional_properties=True, min_properties=1, max_properties=2 ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'properties': { 'color': { 'type': 'string', 'enum': ['red', 'green', 'blue'] } }, 'patternProperties': { 's*': {'type': 'integer'} }, 'minProperties': 1, 'maxProperties': 2, 'additionalProperties': True } ) item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem() }, additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), schema.StringItem()]) ) self.assertDictEqual( item.serialize(), { 'type': 'object', 'title': item.title, 'description': item.description, 'properties': { 'sides': {'type': 'integer'} }, 'additionalProperties': { 'oneOf': [ {'type': 'boolean'}, {'type': 'string'} ] } } ) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem(required=True) }, additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), schema.StringItem()]) ) self.assertDictContainsSubset( TestConf.serialize(), { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', 'properties': { 'item': { 'title': 'Poligon', 'description': 'Describe the Poligon', 'type': 'object', 'properties': { 'sides': { 'type': 'integer' } }, 'additionalProperties': { 'oneOf': [ { 'type': 'boolean' }, { 'type': 'string' } ] }, 'required': [ 'sides' ], } }, 'x-ordering': [ 'item' ], 'additionalProperties': False } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_dict_config_validation(self): class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem() } ) try: jsonschema.validate({'item': {'sides': 1}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'sides': '1'}}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 2}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, pattern_properties={ 'si.*': schema.IntegerItem() }, ) try: jsonschema.validate({'item': {'sides': 1, 'color': 'red'}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 2}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, pattern_properties={ 'si.*': schema.IntegerItem() }, additional_properties=False ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'green', 'sides': 4, 'surfaces': 4}}, TestConf.serialize()) if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): self.assertIn( 'Additional properties are not allowed', excinfo.exception.message) else: self.assertIn( '\'surfaces\' does not match any of the regexes', excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, additional_properties=schema.OneOfItem(items=[ schema.BooleanItem(), schema.IntegerItem() ]) ) try: jsonschema.validate({'item': {'sides': 1, 'color': 'red', 'rugged_surface': False}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize()) self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'color': schema.StringItem(enum=['red', 'green', 'blue']) }, additional_properties=schema.OneOfItem(items=[ schema.BooleanItem(), schema.IntegerItem() ]), min_properties=2, max_properties=3 ) try: jsonschema.validate({'item': {'color': 'red', 'sides': 1}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': {'sides': 1, 'color': 'red', 'rugged_surface': False}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'blue'}}, TestConf.serialize()) self.assertIn('does not have enough properties', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'sides': 4, 'color': 'blue', 'rugged_surface': False, 'opaque': True}}, TestConf.serialize()) self.assertIn('has too many properties', excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties={ 'sides': schema.IntegerItem(required=True) }, additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), schema.StringItem()]) ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'blue', 'rugged_surface': False, 'opaque': True}}, TestConf.serialize()) self.assertIn('\'sides\' is a required property', excinfo.exception.message) class Props(schema.Schema): sides = schema.IntegerItem(required=True) class TestConf(schema.Schema): item = schema.DictItem( title='Poligon', description='Describe the Poligon', properties=Props(), additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), schema.StringItem()]) ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'blue', 'rugged_surface': False, 'opaque': True}}, TestConf.serialize()) self.assertIn('\'sides\' is a required property', excinfo.exception.message) def test_oneof_config(self): item = schema.OneOfItem( items=(schema.StringItem(title='Yes', enum=['yes']), schema.StringItem(title='No', enum=['no'])) ) self.assertEqual( item.serialize(), { 'oneOf': [i.serialize() for i in item.items] } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_oneof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( title='Hungry', description='Are you hungry?', items=schema.OneOfItem( items=(schema.StringItem(title='Yes', enum=['yes']), schema.StringItem(title='No', enum=['no'])) ) ) try: jsonschema.validate({'item': ['no']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 2}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) def test_anyof_config(self): item = schema.AnyOfItem( items=(schema.StringItem(title='Yes', enum=['yes']), schema.StringItem(title='No', enum=['no'])) ) self.assertEqual( item.serialize(), { 'anyOf': [i.serialize() for i in item.items] # pylint: disable=E1133 } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_anyof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( title='Hungry', description='Are you hungry?', items=schema.AnyOfItem( items=(schema.StringItem(title='Yes', enum=['yes']), schema.StringItem(title='No', enum=['no']), schema.BooleanItem()) ) ) try: jsonschema.validate({'item': ['no']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': ['yes']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': [True]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': [False]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 2}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) def test_allof_config(self): item = schema.AllOfItem( items=(schema.StringItem(min_length=2), schema.StringItem(max_length=3)) ) self.assertEqual( item.serialize(), { 'allOf': [i.serialize() for i in item.items] # pylint: disable=E1133 } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_allof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( title='Hungry', description='Are you hungry?', items=schema.AllOfItem( items=(schema.StringItem(min_length=2), schema.StringItem(max_length=3)) ) ) try: jsonschema.validate({'item': ['no']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': ['yes']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) self.assertIn('is too long', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['hmmmm']}, TestConf.serialize()) self.assertIn('is too long', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': 2}, TestConf.serialize()) self.assertIn('is not of type', excinfo.exception.message) def test_not_config(self): item = schema.NotItem(item=schema.BooleanItem()) self.assertEqual( item.serialize(), { 'not': item.item.serialize() } ) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_not_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( title='Hungry', description='Are you hungry?', items=schema.NotItem(item=schema.BooleanItem()) ) try: jsonschema.validate({'item': ['no']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) try: jsonschema.validate({'item': ['yes']}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': [True]}, TestConf.serialize()) self.assertIn('is not allowed for', excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': [False]}, TestConf.serialize()) self.assertIn('is not allowed for', excinfo.exception.message) def test_item_name_override_class_attrname(self): class TestConf(schema.Schema): item = schema.BooleanItem(name='hungry', title='Hungry', description='Are you hungry?') expected = { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "hungry": { "type": "boolean", "description": "Are you hungry?", "title": "Hungry" } }, "x-ordering": [ "hungry" ], "additionalProperties": False } self.assertDictEqual(TestConf.serialize(), expected) def test_config_name_override_class_attrname(self): class TestConf(schema.Schema): item = schema.BooleanItem(title='Hungry', description='Are you hungry?') class TestConf2(schema.Schema): a_name = TestConf(name='another_name') expected = { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "another_name": { "id": "https://non-existing.saltstack.com/schemas/another_name.json#", "type": "object", "properties": { "item": { "type": "boolean", "description": "Are you hungry?", "title": "Hungry" } }, "x-ordering": [ "item" ], "additionalProperties": False } }, "x-ordering": [ "another_name" ], "additionalProperties": False } self.assertDictEqual(TestConf2.serialize(), expected) class TestComplexSchemaItem(schema.ComplexSchemaItem): _complex_attributes = ['thirsty'] thirsty = schema.BooleanItem(title='Thirsty', description='Are you thirsty?') class TestComplexComplexSchemaItem(schema.ComplexSchemaItem): _complex_attributes = ['hungry', 'complex_item'] hungry = schema.BooleanItem(title='Hungry', description='Are you hungry?', required=True) complex_item = TestComplexSchemaItem(definition_name='test_definition') class TestComplexDefinitionsSchema(schema.DefinitionsSchema): title = 'Test Complex Definition Schema' complex_item = TestComplexSchemaItem() class TestOneOfComplexDefinitionsSchema(schema.DefinitionsSchema): title = 'Test OneOf Complex Definitions Schema' one_of_item = schema.OneOfItem( items=[TestComplexSchemaItem(), schema.StringItem()]) class TestArrayComplexDefinitionsSchema(schema.DefinitionsSchema): title = 'Test Array Complex Definitions Schema' array_item = schema.ArrayItem(items=TestComplexSchemaItem()) class TestDictComplexDefinitionsSchema(schema.DefinitionsSchema): title = 'Test Dict Complex Definitions Schema' dict_item = schema.DictItem( properties={'complex_obj': TestComplexSchemaItem(required=True)}, additional_properties=TestComplexSchemaItem()) class TestComplexComplexDefinitionsSchema(schema.DefinitionsSchema): title = 'Test Complex Complex Definition Schema' complex_complex_item = TestComplexComplexSchemaItem() class ComplexSchemaTestCase(TestCase): ''' Test cases with definition schemas containing complex items''' obj = TestComplexSchemaItem() complex_obj = TestComplexComplexSchemaItem() schema = TestComplexDefinitionsSchema() one_of_schema = TestOneOfComplexDefinitionsSchema() array_schema = TestArrayComplexDefinitionsSchema() dict_schema = TestDictComplexDefinitionsSchema() complex_schema = TestComplexComplexDefinitionsSchema() def test_complex_schema_item_serialize(self): obj = copy.deepcopy(self.obj) expected_serialized = {'$ref': '#/definitions/TestComplexSchemaItem'} self.assertDictEqual(obj.serialize(), expected_serialized) def test_complex_schema_item_definition(self): obj = copy.deepcopy(self.obj) expected_def = { 'type': 'object', 'title': 'TestComplexSchemaItem', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}} self.assertDictEqual(obj.get_definition(), expected_def) def test_complex_complex_schema_item_definition(self): complex_obj = copy.deepcopy(self.complex_obj) expected_def = { 'type': 'object', 'title': 'TestComplexComplexSchemaItem', 'properties': { 'hungry': { 'type': 'boolean', 'title': 'Hungry', 'description': 'Are you hungry?'}, 'complex_item': { 'type': 'object', '$ref': '#/definitions/test_definition'}}, 'required': ['hungry']} self.assertDictEqual(complex_obj.get_definition(), expected_def) def test_complex_definition_schema(self): serialized = yaml.safe_load(json.dumps(self.schema.serialize())) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Test Complex Definition Schema', 'type': 'object', 'properties': { 'complex_item': { '$ref': '#/definitions/TestComplexSchemaItem'}}, 'x-ordering': ['complex_item'], 'additionalProperties': False, 'definitions': { 'TestComplexSchemaItem': { 'type': 'object', 'title': 'TestComplexSchemaItem', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}}}} self.assertDictEqual(serialized, expected) def test_one_of_complex_definition_schema(self): serialized = yaml.safe_load(json.dumps(self.one_of_schema.serialize())) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Test OneOf Complex Definitions Schema', 'type': 'object', 'properties': { 'one_of_item': { 'oneOf': [{'$ref': '#/definitions/TestComplexSchemaItem'}, {'type': 'string'}]}}, 'x-ordering': ['one_of_item'], 'additionalProperties': False, 'definitions': { 'TestComplexSchemaItem': { 'type': 'object', 'title': 'TestComplexSchemaItem', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}}}} self.assertDictEqual(serialized, expected) def test_array_complex_definition_schema(self): serialized = yaml.safe_load(json.dumps(self.array_schema.serialize())) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Test Array Complex Definitions Schema', 'type': 'object', 'properties': { 'array_item': { 'type': 'array', 'title': 'array_item', 'items': {'$ref': '#/definitions/TestComplexSchemaItem'}}}, 'x-ordering': ['array_item'], 'additionalProperties': False, 'definitions': { 'TestComplexSchemaItem': { 'type': 'object', 'title': 'TestComplexSchemaItem', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}}}} self.assertDictEqual(serialized, expected) def test_dict_complex_definition_schema(self): serialized = yaml.safe_load(json.dumps(self.dict_schema.serialize())) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Test Dict Complex Definitions Schema', 'type': 'object', 'properties': { 'dict_item': { 'type': 'object', 'title': 'dict_item', 'required': ['complex_obj'], 'properties': {'complex_obj': {'$ref': '#/definitions/TestComplexSchemaItem'}}, 'additionalProperties': {'$ref': '#/definitions/TestComplexSchemaItem'}}}, 'x-ordering': ['dict_item'], 'additionalProperties': False, 'definitions': { 'TestComplexSchemaItem': { 'type': 'object', 'title': 'TestComplexSchemaItem', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}}}} self.assertDictEqual(serialized, expected) def test_complex_complex_definition_schema(self): serialized = yaml.safe_load(json.dumps( self.complex_schema.serialize())) expected = { '$schema': 'http://json-schema.org/draft-04/schema#', 'title': 'Test Complex Complex Definition Schema', 'type': 'object', 'properties': { 'complex_complex_item': { '$ref': '#/definitions/TestComplexComplexSchemaItem'}}, 'x-ordering': ['complex_complex_item'], 'additionalProperties': False, 'definitions': { 'TestComplexComplexSchemaItem': { 'type': 'object', 'title': 'TestComplexComplexSchemaItem', 'properties': { 'hungry': { 'type': 'boolean', 'title': 'Hungry', 'description': 'Are you hungry?'}, 'complex_item': { 'type': 'object', '$ref': '#/definitions/test_definition'}}, 'required': ['hungry']}, 'test_definition': { 'type': 'object', 'title': 'test_definition', 'properties': { 'thirsty': { 'type': 'boolean', 'title': 'Thirsty', 'description': 'Are you thirsty?'}}}}} self.assertDictEqual(serialized, expected) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_schema_item_thirsty_valid(self): serialized = self.schema.serialize() try: jsonschema.validate({'complex_item': {'thirsty': True}}, serialized) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_schema_item_thirsty_invalid(self): serialized = self.schema.serialize() with self.assertRaises(jsonschema.exceptions.ValidationError) \ as excinfo: jsonschema.validate({'complex_item': {'thirsty': 'Foo'}}, serialized) self.assertIn('\'Foo\' is not of type \'boolean\'', excinfo.exception.message) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_complex_schema_item_hungry_valid(self): serialized = self.complex_schema.serialize() try: jsonschema.validate({'complex_complex_item': {'hungry': True}}, serialized) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_both_complex_complex_schema_all_items_valid(self): serialized = self.complex_schema.serialize() try: jsonschema.validate({'complex_complex_item': {'hungry': True, 'complex_item': {'thirsty': True}}}, serialized) except jsonschema.exceptions.ValidationError as exc: self.fail('ValidationError raised: {0}'.format(exc)) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_complex_schema_item_hungry_invalid(self): serialized = self.complex_schema.serialize() with self.assertRaises(jsonschema.exceptions.ValidationError) \ as excinfo: jsonschema.validate({'complex_complex_item': {'hungry': 'Foo'}}, serialized) self.assertIn('\'Foo\' is not of type \'boolean\'', excinfo.exception.message) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_complex_schema_item_inner_thirsty_invalid(self): serialized = self.complex_schema.serialize() with self.assertRaises(jsonschema.exceptions.ValidationError) \ as excinfo: jsonschema.validate( {'complex_complex_item': {'hungry': True, 'complex_item': {'thirsty': 'Bar'}}}, serialized) self.assertIn('\'Bar\' is not of type \'boolean\'', excinfo.exception.message) @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') def test_complex_complex_schema_item_missing_required_hungry(self): serialized = self.complex_schema.serialize() with self.assertRaises(jsonschema.exceptions.ValidationError) \ as excinfo: jsonschema.validate( {'complex_complex_item': {'complex_item': {'thirsty': True}}}, serialized) self.assertIn('\'hungry\' is a required property', excinfo.exception.message)