def test_validate_valid_polymorphic_object(polymorphic_spec): list_of_pets_dict = { 'number_of_pets': 3, 'list': [ { 'name': 'a generic pet name', 'type': 'GenericPet', }, { 'name': 'a dog name', 'type': 'Dog', 'birth_date': '2017-03-09', }, { 'name': 'a cat name', 'type': 'Cat', 'color': 'white', }, ] } validate_object( swagger_spec=polymorphic_spec, object_spec=polymorphic_spec.spec_dict['definitions']['PetList'], value=list_of_pets_dict, )
def test_user_defined_format_success(minimal_swagger_spec, email_address_object_spec): request_body = {'email_address': '*****@*****.**'} minimal_swagger_spec.register_format(email_address_format) # No exception thrown == success validate_object(minimal_swagger_spec, email_address_object_spec, request_body)
def test_validate_object_with_different_format_configurations( minimal_swagger_spec, value, format_, x_nullable, expect_exception): minimal_swagger_spec.spec_dict['definitions']['obj'] = { 'properties': { 'prop': { 'type': 'string', 'format': format_, 'x-nullable': x_nullable } } } minimal_swagger_spec.register_format(DummyFormat) captured_exception = None try: validate_object( swagger_spec=minimal_swagger_spec, object_spec=minimal_swagger_spec.spec_dict['definitions']['obj'], value=value, ) except ValidationError as e: captured_exception = e if not expect_exception: assert captured_exception is None else: assert ( captured_exception.message == '{0} is not a \'dummy\''.format(repr(value['prop'])) or captured_exception.message == '{0} is not of type \'string\''.format(repr(value['prop'])) )
def test_success(minimal_swagger_spec, address_spec): address = { 'number': 1000, 'street_name': 'Main', 'street_type': 'Street', } validate_object(minimal_swagger_spec, address_spec, address)
def test_nullable_none_value(empty_swagger_spec, required): """When nullable is `True` and the value is set to `None`, validation should pass: (9), (12) """ content_spec = content_spec_factory(required, True) value = {'x': None} validate_object(empty_swagger_spec, content_spec, value)
def test_validate_invalid_polymorphic_object(polymorphic_spec, schema_dict, expected_validation_error): with pytest.raises(ValidationError) as e: validate_object( swagger_spec=polymorphic_spec, object_spec=polymorphic_spec.spec_dict['definitions']['PetList'], value=schema_dict, ) assert expected_validation_error in str(e.value.message)
def test_additional_property_OK(minimal_swagger_spec, address_spec): address = { 'number': 1000, 'street_name': 'Main', 'street_type': 'Street', 'city': 'Swaggerville' } validate_object(minimal_swagger_spec, address_spec, address)
def test_property_with_no_schema(minimal_swagger_spec, address_spec): address = { 'number': 1000, 'street_name': 'Main', 'street_type': 'Street', } del address_spec['properties']['street_name']['type'] validate_object(minimal_swagger_spec, address_spec, address)
def test_nullable_no_value(empty_swagger_spec, nullable): """When the value is not required and not set at all, validation should pass: (1), (7) """ content_spec = content_spec_factory(False, nullable=nullable) value = {} validate_object(empty_swagger_spec, content_spec, value)
def test_required_OK(minimal_swagger_spec, address_spec): address_spec['required'] = ['number'] address = { 'street_name': 'Main', 'street_type': 'Street', } with pytest.raises(ValidationError) as excinfo: validate_object(minimal_swagger_spec, address_spec, address) assert 'is a required property' in str(excinfo.value)
def discriminator_validator(swagger_spec, validator, discriminator_attribute, instance, schema): """ Validates instance against the schema defined by the discriminator attribute. [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined. discriminator field defines the attribute that will be used to discriminate the object type. NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param discriminator_attribute: name of the discriminator attribute :type discriminator_attribute: str :param instance: object instance value :type instance: dict :param schema: swagger spec for the object :type schema: dict """ discriminator_value = instance[discriminator_attribute] if discriminator_value not in swagger_spec.spec_dict['definitions']: raise ValidationError(message='\'{}\' is not a recognized schema'. format(discriminator_value)) if discriminator_value == schema['x-model']: return new_schema = deepcopy( swagger_spec.deref( swagger_spec.spec_dict['definitions'][discriminator_value])) if 'allOf' not in new_schema: raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format(discriminator_value, schema['x-model'])) schemas_to_remove = [ s for s in new_schema['allOf'] if is_ref(s) and swagger_spec.deref(s) == schema ] if not schemas_to_remove: # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format(discriminator_value, schema['x-model'])) # Remove the current schema from the allOf list in order to avoid unbounded recursion # (the current object is already validated against schema) new_schema['allOf'].remove(schemas_to_remove[0]) from bravado_core.validate import validate_object # Local import due to circular dependency validate_object(swagger_spec=swagger_spec, object_spec=new_schema, value=instance)
def test_user_defined_format_failure(minimal_swagger_spec, email_address_object_spec): request_body = {'email_address': 'i_am_not_a_valid_email_address'} minimal_swagger_spec.register_format(email_address_format) with pytest.raises(ValidationError) as excinfo: validate_object(minimal_swagger_spec, email_address_object_spec, request_body) assert "'i_am_not_a_valid_email_address' is not a 'email_address'" in \ str(excinfo.value)
def test_nullable_required_no_value(empty_swagger_spec, nullable): """When the value is required but not set at all, validation should fail: (4), (10) """ content_spec = content_spec_factory(True, nullable) value = {} with pytest.raises(ValidationError) as excinfo: validate_object(empty_swagger_spec, content_spec, value) assert excinfo.value.message == "'x' is a required property"
def test_nullable_false_value_none(empty_swagger_spec, required): """When nullable is `False` and the value is set to `None`, validation should fail: (3), (6) """ content_spec = content_spec_factory(required, False) value = {'x': None} with pytest.raises(ValidationError) as excinfo: validate_object(empty_swagger_spec, content_spec, value) assert excinfo.value.message == "None is not of type 'string'"
def test_user_defined_format_success(minimal_swagger_spec, email_address_object_spec): request_body = {'email_address': '*****@*****.**'} minimal_swagger_spec.register_format(email_address_format) # No exception thrown == success validate_object( minimal_swagger_spec, email_address_object_spec, request_body, )
def perform_validation(full_spec, model_spec, sample_file_name): with open(sample_file_name, 'r') as data_file: data_dict = json.load(data_file) try: validate_object(full_spec, model_spec, data_dict) except Exception as e: print("Data sample is invalid./n", e) else: print("Data sample is valid.")
def test_allOf_complex(composition_spec): pongclone_spec = composition_spec.spec_dict['definitions']['pongClone'] value = { 'additionalFeature': 'Badges', 'gameSystem': 'NES', 'pang': 'value', 'releaseDate': 'October', } validate_object(composition_spec, pongclone_spec, value)
def test_user_defined_format_sensitive_failure( minimal_swagger_spec, email_address_object_spec, ): object_properties = email_address_object_spec['properties'] object_properties['email_address']['x-sensitive'] = True request_body = {'email_address': 'i_am_not_a_valid_email_address'} minimal_swagger_spec.register_format(email_address_format) with pytest.raises(ValidationError) as excinfo: validate_object(minimal_swagger_spec, email_address_object_spec, request_body) assert "'i_am_not_a_valid_email_address'" not in str(excinfo.value)
def discriminator_validator(swagger_spec, validator, discriminator_attribute, instance, schema): """ Validates instance against the schema defined by the discriminator attribute. [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined. discriminator field defines the attribute that will be used to discriminate the object type. NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param discriminator_attribute: name of the discriminator attribute :type discriminator_attribute: str :param instance: object instance value :type instance: dict :param schema: swagger spec for the object :type schema: dict """ discriminator_value = instance[discriminator_attribute] if discriminator_value not in swagger_spec.definitions: raise ValidationError( message='\'{}\' is not a recognized schema'.format(discriminator_value) ) if discriminator_value == schema['x-model']: return new_schema = deepcopy(swagger_spec.definitions[discriminator_value]._model_spec) if 'allOf' not in new_schema: raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''.format( discriminator_value, schema['x-model'] ) ) schemas_to_remove = [s for s in new_schema['allOf'] if swagger_spec.deref(s) == schema] if not schemas_to_remove: # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''.format( discriminator_value, schema['x-model'] ) ) # Remove the current schema from the allOf list in order to avoid unbounded recursion # (the current object is already validated against schema) new_schema['allOf'].remove(schemas_to_remove[0]) from bravado_core.validate import validate_object # Local import due to circular dependency validate_object(swagger_spec=swagger_spec, object_spec=new_schema, value=instance)
def test_allOf_complex_failure(composition_spec): pongclone_spec = composition_spec.spec_dict['definitions']['pongClone'] value = { 'additionalFeature': 'Badges', 'pang': 'value', 'releaseDate': 'October', } with pytest.raises(ValidationError) as excinfo: validate_object(composition_spec, pongclone_spec, value) assert "'gameSystem' is a required property" in str(excinfo.value.message)
def test_user_defined_format_failure(minimal_swagger_spec, email_address_object_spec): request_body = {'email_address': 'i_am_not_a_valid_email_address'} minimal_swagger_spec.register_format(email_address_format) with pytest.raises(ValidationError) as excinfo: validate_object( minimal_swagger_spec, email_address_object_spec, request_body, ) assert "'i_am_not_a_valid_email_address' is not a 'email_address'" in \ str(excinfo.value)
def test_recursive_ref_depth_n_failure(recursive_swagger_spec): value = { 'name': 'foo', 'child': { 'name': 'bar', 'child': { 'kaboom': 'baz' # <-- key should be 'name', not 'kabbom' } } } with pytest.raises(ValidationError) as excinfo: validate_object(recursive_swagger_spec, {'$ref': '#/definitions/Node'}, value) assert "'name' is a required property" in str(excinfo.value)
def test_recursive_ref_depth_n(recursive_swagger_spec): value = { 'name': 'foo', 'child': { 'name': 'bar', 'child': { 'name': 'baz' } } } validate_object( recursive_swagger_spec, {'$ref': '#/definitions/Node'}, value)
def test_recursive_ref_depth_n(recursive_swagger_spec): value = { 'name': 'foo', 'child': { 'name': 'bar', 'child': { 'name': 'baz', }, }, } validate_object( recursive_swagger_spec, {'$ref': '#/definitions/Node'}, value, )
def validate(self, validation_key: str, model: dict): """ Validate based on a schema definition :param validation_key: path in swagger document to find schema :param model: model to validate :return: None if validation is successful, otherwise an exception will be raised """ spec = reach(self.spec_source, validation_key) if not spec: raise Base422Exception( 'No validation available', reason='A spec was requested but not resolved from API decs') try: validate_object(self.spec, spec, model) except ValidationError as verr: raise Base422Exception('Validation Failed', str(verr))
def test_recursive_ref_depth_n_failure(recursive_swagger_spec): value = { 'name': 'foo', 'child': { 'name': 'bar', 'child': { 'kaboom': 'baz' # <-- key should be 'name', not 'kabbom' } } } with pytest.raises(ValidationError) as excinfo: validate_object( recursive_swagger_spec, {'$ref': '#/definitions/Node'}, value) assert "'name' is a required property" in str(excinfo.value)
def test_builtin_format_still_works_when_user_defined_format_used( minimal_swagger_spec): ipaddress_spec = { 'type': 'object', 'required': ['ipaddress'], 'properties': { 'ipaddress': { 'type': 'string', 'format': 'ipv4', } } } request_body = {'ipaddress': 'not_an_ip_address'} minimal_swagger_spec.register_format(email_address_format) with pytest.raises(ValidationError) as excinfo: validate_object(minimal_swagger_spec, ipaddress_spec, request_body) assert "'not_an_ip_address' is not a 'ipv4'" in str(excinfo.value)
def test_validate_invalid_polymorphic_does_not_alter_validation_paths( polymorphic_spec): dog_dict = { 'name': 'This is a dog name', 'type': 'Dog', # 'birth_date' this is intentionally removed in order to trigger a validation error } with pytest.raises(ValidationError) as excinfo: validate_object( swagger_spec=polymorphic_spec, object_spec=polymorphic_spec.definitions['GenericPet']._model_spec, value=dog_dict, ) validation_error = excinfo.value assert validation_error.validator == 'required' assert validation_error.validator_value == ['birth_date'] # as birth_date is defined on the 2nd item of the Dog allOf list the expected schema path should be allOf/1/required assert list(validation_error.schema_path) == ['allOf', 1, 'required']
def test_validate_object_with_recursive_definition( polymorphic_abspath, polymorphic_dict, internally_dereference_refs, ): # The goal of this test is to ensure that recursive definitions are properly handled # even if internally_dereference_refs is enabled. # Introduce a recursion definition into vendor extensions, this "trick" could be used # to force bravado-core to recognize models that are defined on #/definitions of # referenced files or defined on un-referenced files polymorphic_dict['definitions']['GenericPet']['x-referred-schema'] = [ { '$ref': '#/definitions/{}'.format(definition_key) } for definition_key, definition in iteritems( polymorphic_dict['definitions']) if { '$ref': '#/definitions/GenericPet' } in definition.get('allOf', []) ] polymorphic_spec = Spec.from_dict( spec_dict=polymorphic_dict, origin_url=get_url(polymorphic_abspath), config={'internally_dereference_refs': internally_dereference_refs}, ) dog_dict = { 'name': 'This is a dog name', 'type': 'Dog', 'birth_date': '2018-01-01', } try: validate_object( swagger_spec=polymorphic_spec, object_spec=polymorphic_spec.definitions['GenericPet']._model_spec, value=dog_dict, ) except RuntimeError: # Not catching RecursionError as it was introduced in Python 3.5+ pytest.fail( 'Unbounded recursion has been detected while calling validate_object' )
def test_custom_format(self): api = API('somename', yaml_str=yaml_str, formats=[foo_format]) self.assertTrue(hasattr(api.model, 'Foo')) # marshal and unmarshal f = api.model.Foo(foo='123') j = api.api_spec.model_to_json(f) self.assertDictEqual(j, {'foo': '123'}) model_def = api.api_spec.swagger_dict['definitions']['Foo'] f = unmarshal_model(api.api_spec.spec, model_def, j) self.assertEqual(f.foo, '123') # validate validate_object(api.api_spec.spec, model_def, {'foo': 'foo'}) try: validate_object(api.api_spec.spec, model_def, {'foo': '123'}) except ValidationError as e: self.assertTrue("'123' is not a 'foo'" in str(e)) else: assert 0
def test_custom_format(self): api = API("somename", yaml_str=yaml_str, formats=[foo_format]) self.assertTrue(hasattr(api.model, "Foo")) # marshal and unmarshal f = api.model.Foo(foo="123") j = api.api_spec.model_to_json(f) self.assertDictEqual(j, {"foo": "123"}) model_def = api.api_spec.swagger_dict["definitions"]["Foo"] f = unmarshal_model(api.api_spec.spec, model_def, j) self.assertEqual(f.foo, "123") # validate validate_object(api.api_spec.spec, model_def, {"foo": "foo"}) try: validate_object(api.api_spec.spec, model_def, {"foo": "123"}) except ValidationError as e: self.assertTrue("'123' is not a 'foo'" in str(e)) else: assert 0
def test_validate_object_with_different_enum_configurations(minimal_swagger_spec, value, enum_values, expect_exception): minimal_swagger_spec.spec_dict['definitions']['obj'] = { 'properties': { 'prop': { 'type': 'string', 'enum': enum_values, 'x-nullable': True } } } captured_exception = None try: validate_object( swagger_spec=minimal_swagger_spec, object_spec=minimal_swagger_spec.spec_dict['definitions']['obj'], value=value, ) except ValidationError as e: captured_exception = e if not expect_exception: assert captured_exception is None else: assert captured_exception.message == '\'{0}\' is not one of {1}'.format(value['prop'], enum_values)
def test_validate_object_with_different_enum_configurations(minimal_swagger_spec, value, enum_values, expect_exception): minimal_swagger_spec.spec_dict['definitions']['obj'] = { 'properties': { 'prop': { 'type': 'string', 'enum': enum_values, 'x-nullable': True, }, }, } captured_exception = None try: validate_object( swagger_spec=minimal_swagger_spec, object_spec=minimal_swagger_spec.spec_dict['definitions']['obj'], value=value, ) except ValidationError as e: captured_exception = e if not expect_exception: assert captured_exception is None else: assert captured_exception.message == '\'{0}\' is not one of {1}'.format(value['prop'], enum_values)
def discriminator_validator(swagger_spec, validator, discriminator_attribute, instance, schema): """ Validates instance against the schema defined by the discriminator attribute. [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined. discriminator field defines the attribute that will be used to discriminate the object type. NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param discriminator_attribute: name of the discriminator attribute :type discriminator_attribute: str :param instance: object instance value :type instance: dict :param schema: swagger spec for the object :type schema: dict """ try: discriminator_value = instance[discriminator_attribute] except KeyError: raise ValidationError( "'{}' is a required property".format(discriminator_attribute)) if discriminator_value not in swagger_spec.definitions: raise ValidationError(message='\'{}\' is not a recognized schema'. format(discriminator_value)) if discriminator_value == schema[MODEL_MARKER]: return discriminated_schema = swagger_spec.definitions[ discriminator_value]._model_spec if 'allOf' not in discriminated_schema: raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format(discriminator_value, schema[MODEL_MARKER])) schemas_to_remove = [ s for s in discriminated_schema['allOf'] if swagger_spec.deref(s) == schema ] if not schemas_to_remove: # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format(discriminator_value, schema[MODEL_MARKER])) # Remove the current schema from the allOf list in order to avoid unbounded recursion # (the current object is already validated against schema) # WARNING: This is especially important if internally_dereference_refs is set to true # as we're modifying new_schema and new_schema is a dict (so mutable) we need to copy # it in order to have a brand new dictionary that we can modify new_schema = discriminated_schema.copy() new_schema['allOf'] = [ all_of_schema if all_of_schema not in schemas_to_remove else {} for all_of_schema in new_schema['allOf'] ] from bravado_core.validate import validate_object # Local import due to circular dependency validate_object(swagger_spec=swagger_spec, object_spec=new_schema, value=instance)
def validate_object(self, object_spec, value): validate_object(self.spec, object_spec, value)
def test_recursive_ref_depth_1(recursive_swagger_spec): validate_object( recursive_swagger_spec, {'$ref': '#/definitions/Node'}, {'name': 'foo'})
def test_nullable_with_value(empty_swagger_spec, nullable, required): """With a value set, validation should always pass: (2), (5), (8), (11)""" content_spec = content_spec_factory(required, nullable) value = {'x': 'y'} validate_object(empty_swagger_spec, content_spec, value)
def test_success(address_spec): address = {"number": 1000, "street_name": "Main", "street_type": "Street"} validate_object(address_spec, address)
def validate(self, key, obj): key_object = self.spec_dict['definitions'][key] return validate_object(self.spec, key_object, obj)
def validate_car(car): validate_object(spec, Car, car)
def validate_apikey(apikey): validate_object(spec, apikey_spec, apikey)
def test_required_OK(address_spec): address_spec["required"] = ["number"] address = {"street_name": "Main", "street_type": "Street"} with pytest.raises(ValidationError) as excinfo: validate_object(address_spec, address) assert "is a required property" in str(excinfo.value)
def test_additional_property_OK(address_spec): address = {"number": 1000, "street_name": "Main", "street_type": "Street", "city": "Swaggerville"} validate_object(address_spec, address)
def test_leaving_out_property_OK(address_spec): address = {"street_name": "Main", "street_type": "Street"} validate_object(address_spec, address)
def test_recursive_ref_depth_1(recursive_swagger_spec): validate_object(recursive_swagger_spec, {'$ref': '#/definitions/Node'}, {'name': 'foo'})
def test_allOf_minimal(empty_swagger_spec, allOf_spec): value = { 'business': 'Yelp', } validate_object(empty_swagger_spec, allOf_spec, value)
def test_list_definition(self): buckets = self.app.get('/buckets', headers=self.headers).json schema = self.spec.deref(self.spec_dict['definitions']['List']) validate_object(self.spec, schema, buckets)
def test_allOf_fails(empty_swagger_spec, allOf_spec): with pytest.raises(ValidationError) as excinfo: validate_object(empty_swagger_spec, allOf_spec, {}) assert excinfo.value.message == "'business' is a required property"
def test_error_definition(self): buckets = self.app.get('/buckets', status=401).json schema = self.spec.deref(self.spec_dict['definitions']['Error']) validate_object(self.spec, schema, buckets)
def validate_zone(zone): validate_object(spec, zone_spec, zone)
def test_leaving_out_property_OK(minimal_swagger_spec, address_spec): address = { 'street_name': 'Main', 'street_type': 'Street', } validate_object(minimal_swagger_spec, address_spec, address)