openapi-core/tests/unit/schema/test_schemas.py

338 lines
9 KiB
Python
Raw Normal View History

import datetime
2019-02-26 10:37:49 +00:00
import uuid
2017-09-21 11:51:37 +00:00
import mock
import pytest
2020-01-28 15:40:45 +00:00
from openapi_core.schema.schemas.enums import SchemaType
2018-04-17 12:18:40 +00:00
from openapi_core.schema.schemas.models import Schema
from openapi_core.schema.schemas.types import NoValue
from openapi_core.unmarshalling.schemas.exceptions import (
2020-01-28 15:40:45 +00:00
InvalidSchemaFormatValue,
FormatterNotFoundError,
UnmarshalError,
2020-01-28 15:40:45 +00:00
InvalidSchemaValue,
)
2020-01-28 15:40:45 +00:00
from openapi_core.unmarshalling.schemas.formatters import Formatter
2018-08-22 10:51:06 +00:00
2017-09-21 11:51:37 +00:00
2017-10-17 13:02:21 +00:00
class TestSchemaIteritems(object):
2017-09-21 11:51:37 +00:00
@pytest.fixture
def schema(self):
properties = {
'application/json': mock.sentinel.application_json,
'text/csv': mock.sentinel.text_csv,
}
return Schema('object', properties=properties)
@property
2017-10-17 13:02:21 +00:00
def test_valid(self, schema):
2019-06-18 11:39:07 +00:00
for name in schema.properties:
2017-09-21 11:51:37 +00:00
assert schema[name] == schema.properties[name]
2017-10-17 13:02:21 +00:00
class TestSchemaUnmarshal(object):
2017-10-17 13:33:46 +00:00
def test_deprecated(self):
schema = Schema('string', deprecated=True)
value = 'test'
with pytest.warns(DeprecationWarning):
result = schema.unmarshal(value)
assert result == value
2019-09-03 00:38:19 +00:00
@pytest.mark.parametrize('schema_type', [
'boolean', 'array', 'integer', 'number',
])
def test_non_string_empty_value(self, schema_type):
schema = Schema(schema_type)
value = ''
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
schema.unmarshal(value)
2019-09-03 00:38:19 +00:00
2017-10-17 13:02:21 +00:00
def test_string_valid(self):
schema = Schema('string')
value = 'test'
result = schema.unmarshal(value)
assert result == value
2019-03-22 14:05:54 +00:00
def test_string_format_uuid_valid(self):
2020-01-28 15:40:45 +00:00
schema = Schema(SchemaType.STRING, schema_format='uuid')
value = str(uuid.uuid4())
result = schema.unmarshal(value)
assert result == uuid.UUID(value)
2020-01-28 15:40:45 +00:00
def test_string_format_uuid_uuid_quirks_invalid(self):
schema = Schema(SchemaType.STRING, schema_format='uuid')
value = uuid.uuid4()
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
schema.unmarshal(value)
2017-10-17 13:02:21 +00:00
Add missing STRING_FORMAT_CALLABLE_GETTER: SchemaFormat.PASSWORD `password` is a valid OpenAPIv3 string format, that is used as a UI hint for frontend clients to mask the input field. It was already present in the `SchemaFormat` enum, but it was not handled in `_unmarshal_string` that uses `STRING_FORMAT_CALLABLE_GETTER` to decide how to unmarshal a string, which would result in an error like this one: ``` Traceback (most recent call last): [... snip ...] File ".venv/lib/python3.7/site-packages/openapi_core/validation/request/validators.py", line 37, in validate body, body_errors = self._get_body(request, operation) File ".venv/lib/python3.7/site-packages/openapi_core/validation/request/validators.py", line 82, in _get_body body = media_type.unmarshal(raw_body, self.custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/media_types/models.py", line 45, in unmarshal unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 295, in _unmarshal_object value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 335, in _unmarshal_properties prop_value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 295, in _unmarshal_object value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 335, in _unmarshal_properties prop_value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 215, in _unmarshal_string formatstring = self.STRING_FORMAT_CALLABLE_GETTER[schema_format] KeyError: <SchemaFormat.PASSWORD: 'password'> ```
2019-04-26 19:22:54 +00:00
def test_string_format_password(self):
2020-01-28 15:40:45 +00:00
schema = Schema(SchemaType.STRING, schema_format='password')
Add missing STRING_FORMAT_CALLABLE_GETTER: SchemaFormat.PASSWORD `password` is a valid OpenAPIv3 string format, that is used as a UI hint for frontend clients to mask the input field. It was already present in the `SchemaFormat` enum, but it was not handled in `_unmarshal_string` that uses `STRING_FORMAT_CALLABLE_GETTER` to decide how to unmarshal a string, which would result in an error like this one: ``` Traceback (most recent call last): [... snip ...] File ".venv/lib/python3.7/site-packages/openapi_core/validation/request/validators.py", line 37, in validate body, body_errors = self._get_body(request, operation) File ".venv/lib/python3.7/site-packages/openapi_core/validation/request/validators.py", line 82, in _get_body body = media_type.unmarshal(raw_body, self.custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/media_types/models.py", line 45, in unmarshal unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 295, in _unmarshal_object value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 335, in _unmarshal_properties prop_value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 295, in _unmarshal_object value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 335, in _unmarshal_properties prop_value, custom_formatters=custom_formatters) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 189, in unmarshal casted = self.cast(value, custom_formatters=custom_formatters, strict=strict) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 179, in cast return cast_callable(value) File ".venv/lib/python3.7/site-packages/openapi_core/schema/schemas/models.py", line 215, in _unmarshal_string formatstring = self.STRING_FORMAT_CALLABLE_GETTER[schema_format] KeyError: <SchemaFormat.PASSWORD: 'password'> ```
2019-04-26 19:22:54 +00:00
value = 'password'
result = schema.unmarshal(value)
assert result == 'password'
2019-03-22 01:51:47 +00:00
def test_string_float_invalid(self):
schema = Schema('string')
value = 1.23
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
2017-10-17 13:02:21 +00:00
def test_string_default(self):
default_value = 'default'
schema = Schema('string', default=default_value)
value = NoValue
2017-10-17 13:02:21 +00:00
result = schema.unmarshal(value)
2017-10-17 13:02:21 +00:00
assert result == default_value
@pytest.mark.parametrize('default_value', ['default', None])
def test_string_default_nullable(self, default_value):
2017-10-17 13:02:21 +00:00
schema = Schema('string', default=default_value, nullable=True)
value = NoValue
2017-10-17 13:02:21 +00:00
result = schema.unmarshal(value)
assert result == default_value
def test_string_format_date(self):
schema = Schema('string', schema_format='date')
value = '2018-01-02'
result = schema.unmarshal(value)
assert result == datetime.date(2018, 1, 2)
2018-08-22 10:51:06 +00:00
def test_string_format_datetime(self):
schema = Schema('string', schema_format='date-time')
value = '2018-01-02T00:00:00Z'
2018-08-22 10:51:06 +00:00
result = schema.unmarshal(value)
assert result == datetime.datetime(2018, 1, 2, 0, 0)
2018-08-22 10:51:06 +00:00
def test_string_format_custom(self):
2020-01-28 15:40:45 +00:00
formatted = 'x-custom'
class CustomFormatter(Formatter):
def unmarshal(self, value):
return formatted
custom_format = 'custom'
schema = Schema('string', schema_format=custom_format)
value = 'x'
2020-01-28 15:40:45 +00:00
formatter = CustomFormatter()
custom_formatters = {
custom_format: formatter,
}
2020-01-28 15:40:45 +00:00
result = schema.unmarshal(value, custom_formatters=custom_formatters)
2019-09-03 00:38:19 +00:00
2020-01-28 15:40:45 +00:00
assert result == formatted
2019-09-03 00:38:19 +00:00
def test_string_format_custom_value_error(self):
2020-01-28 15:40:45 +00:00
class CustomFormatter(Formatter):
def unmarshal(self, value):
raise ValueError
2019-09-03 00:38:19 +00:00
custom_format = 'custom'
schema = Schema('string', schema_format=custom_format)
value = 'x'
2020-01-28 15:40:45 +00:00
formatter = CustomFormatter()
custom_formatters = {
custom_format: formatter,
}
2019-09-03 00:38:19 +00:00
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaFormatValue):
2019-09-03 00:38:19 +00:00
schema.unmarshal(
2020-01-28 15:40:45 +00:00
value, custom_formatters=custom_formatters)
def test_string_format_unknown(self):
unknown_format = 'unknown'
schema = Schema('string', schema_format=unknown_format)
value = 'x'
with pytest.raises(FormatterNotFoundError):
2018-08-22 10:51:06 +00:00
schema.unmarshal(value)
def test_string_format_invalid_value(self):
custom_format = 'custom'
schema = Schema('string', schema_format=custom_format)
value = 'x'
with pytest.raises(
2019-10-20 12:00:14 +00:00
FormatterNotFoundError,
message=(
'Formatter not found for custom format'
2019-10-20 12:00:14 +00:00
),
):
schema.unmarshal(value)
2017-10-17 13:02:21 +00:00
def test_integer_valid(self):
schema = Schema('integer')
2019-03-22 01:51:47 +00:00
value = 123
2017-10-17 13:02:21 +00:00
result = schema.unmarshal(value)
assert result == int(value)
2019-03-22 01:51:47 +00:00
def test_integer_string_invalid(self):
schema = Schema('integer')
value = '123'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
2017-10-17 13:23:26 +00:00
def test_integer_enum_invalid(self):
2017-10-17 13:49:00 +00:00
schema = Schema('integer', enum=[1, 2, 3])
2017-10-17 13:23:26 +00:00
value = '123'
2019-10-20 13:38:41 +00:00
with pytest.raises(UnmarshalError):
2017-10-17 13:23:26 +00:00
schema.unmarshal(value)
def test_integer_enum(self):
2017-10-17 13:49:00 +00:00
schema = Schema('integer', enum=[1, 2, 3])
2019-03-22 01:51:47 +00:00
value = 2
2017-10-17 13:23:26 +00:00
result = schema.unmarshal(value)
assert result == int(value)
2019-03-22 01:51:47 +00:00
def test_integer_enum_string_invalid(self):
schema = Schema('integer', enum=[1, 2, 3])
value = '2'
2019-10-20 13:38:41 +00:00
with pytest.raises(UnmarshalError):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
2017-10-17 13:02:21 +00:00
def test_integer_default(self):
default_value = 123
2017-10-17 13:02:21 +00:00
schema = Schema('integer', default=default_value)
value = NoValue
2017-10-17 13:02:21 +00:00
result = schema.unmarshal(value)
assert result == default_value
2017-10-17 13:02:21 +00:00
def test_integer_default_nullable(self):
default_value = 123
2017-10-17 13:02:21 +00:00
schema = Schema('integer', default=default_value, nullable=True)
value = None
result = schema.unmarshal(value)
assert result is None
2017-10-17 13:02:21 +00:00
def test_integer_invalid(self):
schema = Schema('integer')
value = 'abc'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2017-10-17 13:02:21 +00:00
schema.unmarshal(value)
2018-08-17 14:54:01 +00:00
2019-03-22 01:51:47 +00:00
def test_array_valid(self):
schema = Schema('array', items=Schema('integer'))
value = [1, 2, 3]
result = schema.unmarshal(value)
assert result == value
def test_array_of_string_string_invalid(self):
schema = Schema('array', items=Schema('string'))
value = '123'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
def test_array_of_integer_string_invalid(self):
schema = Schema('array', items=Schema('integer'))
value = '123'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
def test_boolean_valid(self):
schema = Schema('boolean')
value = True
result = schema.unmarshal(value)
assert result == value
def test_boolean_string_invalid(self):
schema = Schema('boolean')
value = 'True'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
def test_number_valid(self):
schema = Schema('number')
value = 1.23
result = schema.unmarshal(value)
assert result == value
def test_number_string_invalid(self):
schema = Schema('number')
value = '1.23'
2020-01-28 15:40:45 +00:00
with pytest.raises(InvalidSchemaValue):
2019-03-22 01:51:47 +00:00
schema.unmarshal(value)
def test_number_int(self):
2019-03-22 01:51:47 +00:00
schema = Schema('number')
value = 1
result = schema.unmarshal(value)
2019-03-22 01:51:47 +00:00
2019-05-21 11:54:13 +00:00
assert result == 1
assert type(result) == int
def test_number_float(self):
schema = Schema('number')
value = 1.2
result = schema.unmarshal(value)
assert result == 1.2
assert type(result) == float
def test_number_format_float(self):
schema = Schema('number', schema_format='float')
value = 1.2
result = schema.unmarshal(value)
assert result == 1.2
def test_number_format_double(self):
schema = Schema('number', schema_format='double')
value = 1.2
result = schema.unmarshal(value)
assert result == 1.2
2019-03-22 01:51:47 +00:00
2019-03-02 20:44:01 +00:00
def test_schema_any_one_of(self):
schema = Schema(one_of=[
Schema('string'),
Schema('array', items=Schema('string')),
])
assert schema.unmarshal(['hello']) == ['hello']
def test_schema_any(self):
schema = Schema()
assert schema.unmarshal('string') == 'string'