/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/unit/utils/test_schema.py
2 611 строк
94 KB
Daniel A. Wozniak
Fix Python 3.12 CI: pip/virtualenv, deb SourcesList, nacl, and test harness
04 май 2026, 13:37
04 май 2026, 13:37
c2ae29f
Код
Авторство
О чём код?
import copy import pytest import salt.utils.json import salt.utils.schema as schema import salt.utils.stringutils import salt.utils.yaml from salt.utils.versions import Version from tests.support.unit import TestCase try: import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True JSONSCHEMA_VERSION = Version(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False JSONSCHEMA_VERSION = Version("0") # pylint: disable=unused-import,function-redefined 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 # 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, } merged_props = MergedConfigClass.serialize()["properties"] self.assertEqual( dict(expected["properties"], **merged_props), expected["properties"] ) merged_serialized = MergedConfigClass.serialize() self.assertEqual(dict(merged_serialized, **expected), merged_serialized) 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="digitalocean", 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 " "DigitalOcean account which will be used to " "authenticate on the deployed VMs" ), ) class Requirements(BaseRequirements): title = "DigitalOcean" description = "DigitalOcean 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": "DigitalOcean", "description": "DigitalOcean Cloud VM configuration requirements.", "type": "object", "properties": { "driver": { "default": "digitalocean", "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 DigitalOcean " "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 = "DigitalOcean" description = "DigitalOcean 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 " "DigitalOcean 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": "DigitalOcean", "description": "DigitalOcean Cloud VM configuration requirements.", "type": "object", "properties": { "driver": { "default": "digitalocean", "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 DigitalOcean " "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, } actual = Requirements2.serialize() self.assertEqual(dict(actual, **expected), actual) class Requirements3(schema.Schema): title = "DigitalOcean" description = "DigitalOcean Cloud VM configuration requirements." merge_reqs = Requirements(flatten=True) expected = { "$schema": "http://json-schema.org/draft-04/schema#", "title": "DigitalOcean", "description": "DigitalOcean Cloud VM configuration requirements.", "type": "object", "properties": { "driver": { "default": "digitalocean", "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 DigitalOcean " "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, } actual = Requirements3.serialize() self.assertEqual(dict(actual, **expected), actual) class Requirements4(schema.Schema): title = "DigitalOcean" description = "DigitalOcean 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 " "DigitalOcean 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": "DigitalOcean", "description": "DigitalOcean Cloud VM configuration requirements.", "type": "object", "properties": { "driver": { "default": "digitalocean", "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 DigitalOcean " "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 DigitalOcean " "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, } actual = Requirements4.serialize() self.assertEqual(dict(actual, **expected), actual) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="The 'jsonschema' library is missing" ) def test_optional_requirements_config_validation(self): class BaseRequirements(schema.Schema): driver = schema.StringItem(default="digitalocean", 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 = "DigitalOcean" description = "DigitalOcean 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(f"ValidationError raised: {exc}") try: jsonschema.validate( {"personal_access_token": "foo", "ssh_key_names": "bar"}, Requirements.serialize(), ) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") try: jsonschema.validate( {"personal_access_token": "foo", "ssh_key_file": "test"}, Requirements.serialize(), ) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( {"personal_access_token": "foo"}, Requirements.serialize() ) if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version( "4.8.0" ): self.assertIn( "'ssh_key_file' is a required property", excinfo.exception.message ) else: 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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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(f"ValidationError raised: {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()) _msg = excinfo.exception.message self.assertTrue( "is too short" in _msg or "non-empty" in _msg.lower(), msg=_msg, ) 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(f"ValidationError raised: {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(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( JSONSCHEMA_VERSION <= Version("2.5.0"), reason="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(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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": salt.utils.stringutils.to_str("::1")}, TestConf.serialize(), format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") if JSONSCHEMA_VERSION < Version("4.0.0"): 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) else: jsonschema.validate( {"item": "3"}, TestConf.serialize(), format_checker=jsonschema.FormatChecker(), ) 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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="The 'jsonschema' library is missing" ) @pytest.mark.skipif( not HAS_STRICT_RFC3339, reason="The 'strict_rfc3339' 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(f"ValidationError raised: {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, }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="The 'jsonschema' library is missing" ) @pytest.mark.skipif(HAS_RFC3987 is False, reason="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(f"ValidationError raised: {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], }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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(f"ValidationError raised: {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(f"ValidationError raised: {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(f"ValidationError raised: {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], }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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(f"ValidationError raised: {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(f"ValidationError raised: {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(f"ValidationError raised: {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()], }, ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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(f"ValidationError raised: {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(), ) _msg = excinfo.exception.message self.assertTrue( "is too short" in _msg or "non-empty" in _msg.lower(), msg=_msg, ) 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(f"ValidationError raised: {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(f"ValidationError raised: {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(f"ValidationError raised: {exc}") try: jsonschema.validate( {"item": ["Tobias"]}, TestConf.serialize(), format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {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()] ), ) expected = { "$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, } actual = TestConf.serialize() self.assertEqual(dict(expected, **actual), expected) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {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(f"ValidationError raised: {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 < Version("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(f"ValidationError raised: {exc}") with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( {"item": {"sides": "4", "color": "blue"}}, TestConf.serialize() ) if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version( "4.8.0" ): self.assertIn("'4'", excinfo.exception.message) self.assertIn("is not of type", excinfo.exception.message) self.assertIn("'boolean'", excinfo.exception.message) else: 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(f"ValidationError raised: {exc}") try: jsonschema.validate( {"item": {"sides": 1, "color": "red", "rugged_surface": False}}, TestConf.serialize(), ) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {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]} ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({"item": ["maybe"]}, TestConf.serialize()) if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version( "4.8.0" ): self.assertIn("'maybe'", excinfo.exception.message) self.assertIn("is not one of", excinfo.exception.message) self.assertIn("'yes'", excinfo.exception.message) else: 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 ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") try: jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") try: jsonschema.validate({"item": [True]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") try: jsonschema.validate({"item": [False]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({"item": ["maybe"]}, TestConf.serialize()) if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version( "4.8.0" ): self.assertIn("'maybe'", excinfo.exception.message) self.assertIn("is not one of", excinfo.exception.message) self.assertIn("'yes'", excinfo.exception.message) else: 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 ) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") try: jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {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()}) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") try: jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: self.fail(f"ValidationError raised: {exc}") with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({"item": [True]}, TestConf.serialize()) if JSONSCHEMA_VERSION >= Version("4.0.0"): self.assertIn("should not be valid under", excinfo.exception.message) else: self.assertIn("is not allowed for", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({"item": [False]}, TestConf.serialize()) if JSONSCHEMA_VERSION >= Version("4.0.0"): self.assertIn("should not be valid under", excinfo.exception.message) else: 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 ComplexSchemaItem(schema.ComplexSchemaItem): _complex_attributes = ["thirsty"] thirsty = schema.BooleanItem(title="Thirsty", description="Are you thirsty?") class ComplexComplexSchemaItem(schema.ComplexSchemaItem): _complex_attributes = ["hungry", "complex_item"] hungry = schema.BooleanItem( title="Hungry", description="Are you hungry?", required=True ) complex_item = ComplexSchemaItem(definition_name="test_definition") class TestComplexDefinitionsSchema(schema.DefinitionsSchema): title = "Test Complex Definition Schema" complex_item = ComplexSchemaItem() class TestOneOfComplexDefinitionsSchema(schema.DefinitionsSchema): title = "Test OneOf Complex Definitions Schema" one_of_item = schema.OneOfItem(items=[ComplexSchemaItem(), schema.StringItem()]) class TestArrayComplexDefinitionsSchema(schema.DefinitionsSchema): title = "Test Array Complex Definitions Schema" array_item = schema.ArrayItem(items=ComplexSchemaItem()) class TestDictComplexDefinitionsSchema(schema.DefinitionsSchema): title = "Test Dict Complex Definitions Schema" dict_item = schema.DictItem( properties={"complex_obj": ComplexSchemaItem(required=True)}, additional_properties=ComplexSchemaItem(), ) class TestComplexComplexDefinitionsSchema(schema.DefinitionsSchema): title = "Test Complex Complex Definition Schema" complex_complex_item = ComplexComplexSchemaItem() class ComplexSchemaTestCase(TestCase): """Test cases with definition schemas containing complex items""" obj = ComplexSchemaItem() complex_obj = ComplexComplexSchemaItem() 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/ComplexSchemaItem"} self.assertDictEqual(obj.serialize(), expected_serialized) def test_complex_schema_item_definition(self): obj = copy.deepcopy(self.obj) expected_def = { "type": "object", "title": "ComplexSchemaItem", "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": "ComplexComplexSchemaItem", "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 = salt.utils.yaml.safe_load( salt.utils.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/ComplexSchemaItem"}}, "x-ordering": ["complex_item"], "additionalProperties": False, "definitions": { "ComplexSchemaItem": { "type": "object", "title": "ComplexSchemaItem", "properties": { "thirsty": { "type": "boolean", "title": "Thirsty", "description": "Are you thirsty?", } }, } }, } self.assertDictEqual(serialized, expected) def test_one_of_complex_definition_schema(self): serialized = salt.utils.yaml.safe_load( salt.utils.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/ComplexSchemaItem"}, {"type": "string"}, ] } }, "x-ordering": ["one_of_item"], "additionalProperties": False, "definitions": { "ComplexSchemaItem": { "type": "object", "title": "ComplexSchemaItem", "properties": { "thirsty": { "type": "boolean", "title": "Thirsty", "description": "Are you thirsty?", } }, } }, } self.assertDictEqual(serialized, expected) def test_array_complex_definition_schema(self): serialized = salt.utils.yaml.safe_load( salt.utils.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/ComplexSchemaItem"}, } }, "x-ordering": ["array_item"], "additionalProperties": False, "definitions": { "ComplexSchemaItem": { "type": "object", "title": "ComplexSchemaItem", "properties": { "thirsty": { "type": "boolean", "title": "Thirsty", "description": "Are you thirsty?", } }, } }, } self.assertDictEqual(serialized, expected) def test_dict_complex_definition_schema(self): serialized = salt.utils.yaml.safe_load( salt.utils.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/ComplexSchemaItem"} }, "additionalProperties": {"$ref": "#/definitions/ComplexSchemaItem"}, } }, "x-ordering": ["dict_item"], "additionalProperties": False, "definitions": { "ComplexSchemaItem": { "type": "object", "title": "ComplexSchemaItem", "properties": { "thirsty": { "type": "boolean", "title": "Thirsty", "description": "Are you thirsty?", } }, } }, } self.assertDictEqual(serialized, expected) def test_complex_complex_definition_schema(self): serialized = salt.utils.yaml.safe_load( salt.utils.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/ComplexComplexSchemaItem" } }, "x-ordering": ["complex_complex_item"], "additionalProperties": False, "definitions": { "ComplexComplexSchemaItem": { "type": "object", "title": "ComplexComplexSchemaItem", "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) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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) expected = "'Foo' is not of type 'boolean'" self.assertIn(expected, excinfo.exception.message) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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(f"ValidationError raised: {exc}") @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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) expected = "'Foo' is not of type 'boolean'" self.assertIn(expected, excinfo.exception.message) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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, ) expected = "'Bar' is not of type 'boolean'" self.assertIn(expected, excinfo.exception.message) @pytest.mark.skipif( HAS_JSONSCHEMA is False, reason="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)