diff --git a/openapi_core/schema/schemas/models.py b/openapi_core/schema/schemas/models.py index 6ae4dc9a..e3d34b89 100644 --- a/openapi_core/schema/schemas/models.py +++ b/openapi_core/schema/schemas/models.py @@ -9,9 +9,9 @@ import re import warnings -from six import iteritems, integer_types, binary_type, text_type +from six import iteritems, integer_types, binary_type, text_type, string_types -from openapi_core.extensions.models.factories import ModelFactory +from openapi_core.extensions.models.factories import ModelFactory, Model from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType from openapi_core.schema.schemas.exceptions import ( InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty, @@ -194,11 +194,30 @@ def unmarshal(self, value, custom_formatters=None): raise InvalidSchemaValue( "Value {value} not in enum choices: {type}", value, self.enum) + valid_casted_types_by_original_type = { + str: (string_types, bytes), + bytes: string_types, + int: int, + float: float, + dict: Model, + string_types: bytes, + } + + is_string_and_format_datetime = self.format in ("date", "date-time") and isinstance(casted, (date, datetime)) + if not (value is None \ + or isinstance(casted, type(value)) \ + or is_string_and_format_datetime \ + or isinstance(casted, valid_casted_types_by_original_type[type(value)])): + raise InvalidSchemaValue("Input {value} not valid for type {type}", value, self.type) + return casted def _unmarshal_string(self, value, custom_formatters=None): try: schema_format = SchemaFormat(self.format) + if value is not None and not isinstance(value, (string_types, bytes)): + raise InvalidSchemaValue( + "Value {value} is type {type}, expected string type", value, type(value)) except ValueError: msg = "Unsupported format {type} unmarshalling for value {value}" if custom_formatters is not None: diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py index e1a3c8e9..a68afafc 100644 --- a/tests/integration/test_petstore.py +++ b/tests/integration/test_petstore.py @@ -202,7 +202,7 @@ def test_get_pets(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { - 'limit': '20', + 'limit': 20, } request = MockRequest( @@ -238,8 +238,8 @@ def test_get_pets_ids_param(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { - 'limit': '20', - 'ids': ['12', '13'], + 'limit': 20, + 'ids': [12, 13], } request = MockRequest( @@ -276,7 +276,7 @@ def test_get_pets_tags_param(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = [ - ('limit', '20'), + ('limit', 20), ('tags', 'cats,dogs'), ] @@ -419,7 +419,7 @@ def test_post_birds(self, spec, spec_dict): data_json = { 'name': pet_name, 'tag': pet_tag, - 'position': '2', + 'position': 2, 'address': { 'street': pet_street, 'city': pet_city, @@ -434,7 +434,7 @@ def test_post_birds(self, spec, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -479,7 +479,7 @@ def test_post_cats(self, spec, spec_dict): data_json = { 'name': pet_name, 'tag': pet_tag, - 'position': '2', + 'position': 2, 'address': { 'street': pet_street, 'city': pet_city, @@ -494,7 +494,7 @@ def test_post_cats(self, spec, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -535,11 +535,11 @@ def test_post_cats_boolean_string(self, spec, spec_dict): pet_tag = 'cats' pet_street = 'Piekna' pet_city = 'Warsaw' - pet_healthy = 'false' + pet_healthy = False data_json = { 'name': pet_name, 'tag': pet_tag, - 'position': '2', + 'position': 2, 'address': { 'street': pet_street, 'city': pet_city, @@ -554,7 +554,7 @@ def test_post_cats_boolean_string(self, spec, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -602,7 +602,7 @@ def test_post_no_one_of_schema(self, spec, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -641,7 +641,7 @@ def test_post_cats_only_required_body(self, spec, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -682,7 +682,7 @@ def test_post_pets_raises_invalid_mimetype(self, spec): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -752,7 +752,7 @@ def test_post_pets_missing_header(self, spec, spec_dict): } data = json.dumps(data_json) cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -782,10 +782,10 @@ def test_post_pets_raises_invalid_server_error(self, spec): } data = json.dumps(data_json) headers = { - 'api_key': '12345', + 'api_key': 12345, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( @@ -804,7 +804,7 @@ def test_get_pet(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets/{petId}' view_args = { - 'petId': '1', + 'petId': 1, } request = MockRequest( host_url, 'GET', '/pets/1', @@ -846,7 +846,7 @@ def test_get_pet_not_found(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets/{petId}' view_args = { - 'petId': '1', + 'petId': 1, } request = MockRequest( host_url, 'GET', '/pets/1', @@ -888,7 +888,7 @@ def test_get_pet_wildcard(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets/{petId}' view_args = { - 'petId': '1', + 'petId': 1, } request = MockRequest( host_url, 'GET', '/pets/1', diff --git a/tests/integration/test_validators.py b/tests/integration/test_validators.py index bfdd4f94..ed88614f 100644 --- a/tests/integration/test_validators.py +++ b/tests/integration/test_validators.py @@ -78,7 +78,7 @@ def test_missing_parameter(self, validator): def test_get_pets(self, validator): request = MockRequest( self.host_url, 'get', '/v1/pets', - path_pattern='/v1/pets', args={'limit': '10'}, + path_pattern='/v1/pets', args={'limit': 10}, ) result = validator.validate(request) @@ -98,7 +98,7 @@ def test_missing_body(self, validator): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( self.host_url, 'post', '/v1/pets', @@ -125,7 +125,7 @@ def test_invalid_content_type(self, validator): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( self.host_url, 'post', '/v1/pets', @@ -155,7 +155,7 @@ def test_post_pets(self, validator, spec_dict): data_json = { 'name': pet_name, 'tag': pet_tag, - 'position': '2', + 'position': 2, 'address': { 'street': pet_street, 'city': pet_city, @@ -169,7 +169,7 @@ def test_post_pets(self, validator, spec_dict): 'api_key': self.api_key_encoded, } cookies = { - 'user': '123', + 'user': 123, } request = MockRequest( self.host_url, 'post', '/v1/pets', @@ -203,7 +203,7 @@ def test_post_pets(self, validator, spec_dict): def test_get_pet(self, validator): request = MockRequest( self.host_url, 'get', '/v1/pets/1', - path_pattern='/v1/pets/{petId}', view_args={'petId': '1'}, + path_pattern='/v1/pets/{petId}', view_args={'petId': 1}, ) result = validator.validate(request) diff --git a/tests/unit/schema/test_schemas.py b/tests/unit/schema/test_schemas.py index 302719fb..0b09f2ce 100644 --- a/tests/unit/schema/test_schemas.py +++ b/tests/unit/schema/test_schemas.py @@ -124,13 +124,17 @@ def test_string_format_invalid_value(self): ): schema.unmarshal(value) - def test_integer_valid(self): + def test_float_as_str_invalid(self): + schema = Schema('string') + value = 1.23 + with pytest.raises(InvalidSchemaValue): + schema.unmarshal(value) + + def test_str_as_integer_invalid(self): schema = Schema('integer') value = '123' - - result = schema.unmarshal(value) - - assert result == int(value) + with pytest.raises(InvalidSchemaValue): + schema.unmarshal(value) def test_integer_enum_invalid(self): schema = Schema('integer', enum=[1, 2, 3]) @@ -139,13 +143,11 @@ def test_integer_enum_invalid(self): with pytest.raises(InvalidSchemaValue): schema.unmarshal(value) - def test_integer_enum(self): + def test_str_enum_invalid(self): schema = Schema('integer', enum=[1, 2, 3]) value = '2' - - result = schema.unmarshal(value) - - assert result == int(value) + with pytest.raises(InvalidSchemaValue): + schema.unmarshal(value) def test_integer_default(self): default_value = '123'