Merge pull request #191 from p1c2u/refactor/move-cast-out-of-schema-models

Move deserialize/cast out of schema models
This commit is contained in:
A 2020-02-03 15:35:49 +00:00 committed by GitHub
commit 80485788c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 351 additions and 284 deletions

View file

@ -1,15 +1,20 @@
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.schema.schemas.types import NoValue
class PrimitiveCaster(object):
def __init__(self, caster_callable):
def __init__(self, schema, caster_callable):
self.schema = schema
self.caster_callable = caster_callable
def __call__(self, value):
if value in (None, NoValue):
return value
return self.caster_callable(value)
try:
return self.caster_callable(value)
except (ValueError, TypeError):
raise CastError(value, self.schema.type.value)
class DummyCaster(object):

View file

@ -8,22 +8,24 @@ from openapi_core.casting.schemas.util import forcebool
class SchemaCastersFactory(object):
DUMMY_CASTER = DummyCaster()
DUMMY_CASTERS = [
SchemaType.STRING, SchemaType.OBJECT, SchemaType.ANY,
]
PRIMITIVE_CASTERS = {
SchemaType.STRING: DUMMY_CASTER,
SchemaType.INTEGER: PrimitiveCaster(int),
SchemaType.NUMBER: PrimitiveCaster(float),
SchemaType.BOOLEAN: PrimitiveCaster(forcebool),
SchemaType.OBJECT: DUMMY_CASTER,
SchemaType.ANY: DUMMY_CASTER,
SchemaType.INTEGER: int,
SchemaType.NUMBER: float,
SchemaType.BOOLEAN: forcebool,
}
COMPLEX_CASTERS = {
SchemaType.ARRAY: ArrayCaster,
}
def create(self, schema):
if schema.type in self.PRIMITIVE_CASTERS:
return self.PRIMITIVE_CASTERS[schema.type]
if schema.type in self.DUMMY_CASTERS:
return DummyCaster()
elif schema.type in self.PRIMITIVE_CASTERS:
caster_callable = self.PRIMITIVE_CASTERS[schema.type]
return PrimitiveCaster(schema, caster_callable)
elif schema.type in self.COMPLEX_CASTERS:
caster_class = self.COMPLEX_CASTERS[schema.type]
return caster_class(schema=schema, casters_factory=self)
return caster_class(schema, self)

View file

View file

@ -0,0 +1,14 @@
import attr
from openapi_core.exceptions import OpenAPIError
@attr.s(hash=True)
class DeserializeError(OpenAPIError):
"""Deserialize operation error"""
value = attr.ib()
style = attr.ib()
def __str__(self):
return "Failed to deserialize value {value} with style {style}".format(
value=self.value, style=self.style)

View file

@ -0,0 +1,14 @@
from openapi_core.deserializing.exceptions import DeserializeError
class PrimitiveDeserializer(object):
def __init__(self, mimetype, deserializer_callable):
self.mimetype = mimetype
self.deserializer_callable = deserializer_callable
def __call__(self, value):
try:
return self.deserializer_callable(value)
except (ValueError, TypeError, AttributeError):
raise DeserializeError(value, self.mimetype)

View file

@ -0,0 +1,18 @@
from openapi_core.schema.media_types.util import json_loads
from openapi_core.deserializing.media_types.deserializers import (
PrimitiveDeserializer,
)
class MediaTypeDeserializersFactory(object):
MEDIA_TYPE_DESERIALIZERS = {
'application/json': json_loads,
}
def create(self, media_type):
deserialize_callable = self.MEDIA_TYPE_DESERIALIZERS.get(
media_type.mimetype, lambda x: x)
return PrimitiveDeserializer(
media_type.mimetype, deserialize_callable)

View file

@ -0,0 +1,25 @@
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.deserializing.parameters.exceptions import (
EmptyParameterValue,
)
from openapi_core.schema.parameters.enums import ParameterLocation
class PrimitiveDeserializer(object):
def __init__(self, param, deserializer_callable):
self.param = param
self.deserializer_callable = deserializer_callable
def __call__(self, value):
if (self.param.location == ParameterLocation.QUERY and value == "" and
not self.param.allow_empty_value):
raise EmptyParameterValue(
value, self.param.style, self.param.name)
if not self.param.aslist or self.param.explode:
return value
try:
return self.deserializer_callable(value)
except (ValueError, TypeError, AttributeError):
raise DeserializeError(value, self.param.style)

View file

@ -0,0 +1,11 @@
import attr
from openapi_core.deserializing.exceptions import DeserializeError
@attr.s(hash=True)
class EmptyParameterValue(DeserializeError):
name = attr.ib()
def __str__(self):
return "Value of parameter cannot be empty: {0}".format(self.name)

View file

@ -0,0 +1,26 @@
import warnings
from openapi_core.deserializing.parameters.deserializers import (
PrimitiveDeserializer,
)
from openapi_core.schema.parameters.enums import ParameterStyle
class ParameterDeserializersFactory(object):
PARAMETER_STYLE_DESERIALIZERS = {
ParameterStyle.FORM: lambda x: x.split(','),
ParameterStyle.SIMPLE: lambda x: x.split(','),
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
}
def create(self, param):
if param.deprecated:
warnings.warn(
"{0} parameter is deprecated".format(param.name),
DeprecationWarning,
)
deserialize_callable = self.PARAMETER_STYLE_DESERIALIZERS[param.style]
return PrimitiveDeserializer(param, deserialize_callable)

View file

@ -7,14 +7,6 @@ class OpenAPIMediaTypeError(OpenAPIMappingError):
pass
@attr.s(hash=True)
class InvalidMediaTypeValue(OpenAPIMediaTypeError):
original_exception = attr.ib()
def __str__(self):
return "Mimetype invalid: {0}".format(self.original_exception)
@attr.s(hash=True)
class InvalidContentType(OpenAPIMediaTypeError):
mimetype = attr.ib()

View file

@ -1,14 +1,4 @@
"""OpenAPI core media types models module"""
from collections import defaultdict
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
from openapi_core.schema.media_types.util import json_loads
from openapi_core.casting.schemas.exceptions import CastError
MEDIA_TYPE_DESERIALIZERS = {
'application/json': json_loads,
}
class MediaType(object):
@ -18,29 +8,3 @@ class MediaType(object):
self.mimetype = mimetype
self.schema = schema
self.example = example
def get_deserializer_mapping(self):
mapping = MEDIA_TYPE_DESERIALIZERS.copy()
return defaultdict(lambda: lambda x: x, mapping)
def get_dererializer(self):
mapping = self.get_deserializer_mapping()
return mapping[self.mimetype]
def deserialize(self, value):
deserializer = self.get_dererializer()
return deserializer(value)
def cast(self, value):
try:
deserialized = self.deserialize(value)
except ValueError as exc:
raise InvalidMediaTypeValue(exc)
if not self.schema:
return deserialized
try:
return self.schema.cast(deserialized)
except CastError as exc:
raise InvalidMediaTypeValue(exc)

View file

@ -27,21 +27,3 @@ class MissingRequiredParameter(MissingParameterError):
def __str__(self):
return "Missing required parameter: {0}".format(self.name)
@attr.s(hash=True)
class EmptyParameterValue(OpenAPIParameterError):
name = attr.ib()
def __str__(self):
return "Value of parameter cannot be empty: {0}".format(self.name)
@attr.s(hash=True)
class InvalidParameterValue(OpenAPIParameterError):
name = attr.ib()
original_exception = attr.ib()
def __str__(self):
return "Invalid parameter value for `{0}`: {1}".format(
self.name, self.original_exception)

View file

@ -1,16 +1,10 @@
"""OpenAPI core parameters models module"""
import logging
import warnings
from openapi_core.schema.parameters.enums import (
ParameterLocation, ParameterStyle,
)
from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter, MissingParameter, InvalidParameterValue,
EmptyParameterValue,
)
from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.casting.schemas.exceptions import CastError
log = logging.getLogger(__name__)
@ -18,13 +12,6 @@ log = logging.getLogger(__name__)
class Parameter(object):
"""Represents an OpenAPI operation Parameter."""
PARAMETER_STYLE_DESERIALIZERS = {
ParameterStyle.FORM: lambda x: x.split(','),
ParameterStyle.SIMPLE: lambda x: x.split(','),
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
}
def __init__(
self, name, location, schema=None, required=False,
deprecated=False, allow_empty_value=False,
@ -61,53 +48,3 @@ class Parameter(object):
@property
def default_explode(self):
return self.style == ParameterStyle.FORM
def get_dererializer(self):
return self.PARAMETER_STYLE_DESERIALIZERS[self.style]
def deserialize(self, value):
if not self.aslist or self.explode:
return value
deserializer = self.get_dererializer()
return deserializer(value)
def get_raw_value(self, request):
location = request.parameters[self.location.value]
if self.name not in location:
if self.required:
raise MissingRequiredParameter(self.name)
raise MissingParameter(self.name)
if self.aslist and self.explode:
if hasattr(location, 'getall'):
return location.getall(self.name)
return location.getlist(self.name)
return location[self.name]
def cast(self, value):
if self.deprecated:
warnings.warn(
"{0} parameter is deprecated".format(self.name),
DeprecationWarning,
)
if (self.location == ParameterLocation.QUERY and value == "" and
not self.allow_empty_value):
raise EmptyParameterValue(self.name)
try:
deserialized = self.deserialize(value)
except (ValueError, AttributeError) as exc:
raise InvalidParameterValue(self.name, exc)
if not self.schema:
return deserialized
try:
return self.schema.cast(deserialized)
except CastError as exc:
raise InvalidParameterValue(self.name, exc)

View file

@ -2,7 +2,6 @@
from openapi_core.schema.content.exceptions import MimeTypeNotFound
from openapi_core.schema.content.models import Content
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
class RequestBody(object):
@ -17,8 +16,3 @@ class RequestBody(object):
return self.content[mimetype]
except MimeTypeNotFound:
raise InvalidContentType(mimetype)
def get_value(self, request):
if not request.body and self.required:
raise MissingRequestBody(request)
return request.body

View file

@ -2,7 +2,6 @@
from openapi_core.schema.content.exceptions import MimeTypeNotFound
from openapi_core.schema.content.models import Content
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.responses.exceptions import MissingResponseContent
class Response(object):
@ -24,9 +23,3 @@ class Response(object):
return self.content[mimetype]
except MimeTypeNotFound:
raise InvalidContentType(mimetype)
def get_value(self, response):
if not response.data:
raise MissingResponseContent(response)
return response.data

View file

@ -90,14 +90,3 @@ class Schema(object):
def get_all_properties_names(self):
all_properties = self.get_all_properties()
return set(all_properties.keys())
def cast(self, value):
"""Cast value from string to schema type"""
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.casting.schemas.factories import SchemaCastersFactory
casters_factory = SchemaCastersFactory()
caster = casters_factory.create(self)
try:
return caster(value)
except (ValueError, TypeError):
raise CastError(value, self.type)

View file

@ -2,12 +2,12 @@
from itertools import chain
from six import iteritems
from openapi_core.schema.media_types.exceptions import (
InvalidMediaTypeValue, InvalidContentType,
)
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.parameters.exceptions import (
OpenAPIParameterError, MissingRequiredParameter, MissingParameter,
MissingRequiredParameter, MissingParameter,
)
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
@ -99,7 +99,7 @@ class RequestValidator(object):
continue
seen.add((param_name, param.location.value))
try:
raw_value = param.get_raw_value(request)
raw_value = self._get_parameter_value(param, request)
except MissingRequiredParameter as exc:
errors.append(exc)
continue
@ -109,8 +109,15 @@ class RequestValidator(object):
casted = param.schema.default
else:
try:
casted = param.cast(raw_value)
except OpenAPIParameterError as exc:
deserialised = self._deserialise_parameter(
param, raw_value)
except DeserializeError as exc:
errors.append(exc)
continue
try:
casted = self._cast(param, deserialised)
except CastError as exc:
errors.append(exc)
continue
@ -125,33 +132,82 @@ class RequestValidator(object):
return RequestParameters(**locations), errors
def _get_body(self, request, operation):
errors = []
if operation.request_body is None:
return None, errors
return None, []
body = None
try:
media_type = operation.request_body[request.mimetype]
except InvalidContentType as exc:
errors.append(exc)
else:
try:
raw_body = operation.request_body.get_value(request)
except MissingRequestBody as exc:
errors.append(exc)
else:
try:
casted = media_type.cast(raw_body)
except InvalidMediaTypeValue as exc:
errors.append(exc)
else:
try:
body = self._unmarshal(media_type, casted)
except (ValidateError, UnmarshalError) as exc:
errors.append(exc)
return None, [exc, ]
return body, errors
try:
raw_body = self._get_body_value(operation.request_body, request)
except MissingRequestBody as exc:
return None, [exc, ]
try:
deserialised = self._deserialise_media_type(media_type, raw_body)
except DeserializeError as exc:
return None, [exc, ]
try:
casted = self._cast(media_type, deserialised)
except CastError as exc:
return None, [exc, ]
try:
body = self._unmarshal(media_type, casted)
except (ValidateError, UnmarshalError) as exc:
return None, [exc, ]
return body, []
def _get_parameter_value(self, param, request):
location = request.parameters[param.location.value]
if param.name not in location:
if param.required:
raise MissingRequiredParameter(param.name)
raise MissingParameter(param.name)
if param.aslist and param.explode:
if hasattr(location, 'getall'):
return location.getall(param.name)
return location.getlist(param.name)
return location[param.name]
def _get_body_value(self, request_body, request):
if not request.body and request_body.required:
raise MissingRequestBody(request)
return request.body
def _deserialise_media_type(self, media_type, value):
from openapi_core.deserializing.media_types.factories import (
MediaTypeDeserializersFactory,
)
deserializers_factory = MediaTypeDeserializersFactory()
deserializer = deserializers_factory.create(media_type)
return deserializer(value)
def _deserialise_parameter(self, param, value):
from openapi_core.deserializing.parameters.factories import (
ParameterDeserializersFactory,
)
deserializers_factory = ParameterDeserializersFactory()
deserializer = deserializers_factory.create(param)
return deserializer(value)
def _cast(self, param_or_media_type, value):
# return param_or_media_type.cast(value)
if not param_or_media_type.schema:
return value
from openapi_core.casting.schemas.factories import SchemaCastersFactory
casters_factory = SchemaCastersFactory()
caster = casters_factory.create(param_or_media_type.schema)
return caster(value)
def _unmarshal(self, param_or_media_type, value):
if not param_or_media_type.schema:

View file

@ -1,8 +1,8 @@
"""OpenAPI core validation response validators module"""
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.media_types.exceptions import (
InvalidMediaTypeValue, InvalidContentType,
)
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.responses.exceptions import (
InvalidResponse, MissingResponseContent,
)
@ -64,33 +64,35 @@ class ResponseValidator(object):
return ResponseValidationResult(data_errors, data, None)
def _get_data(self, response, operation_response):
errors = []
if not operation_response.content:
return None, errors
return None, []
data = None
try:
media_type = operation_response[response.mimetype]
except InvalidContentType as exc:
errors.append(exc)
else:
try:
raw_data = operation_response.get_value(response)
except MissingResponseContent as exc:
errors.append(exc)
else:
try:
casted = media_type.cast(raw_data)
except InvalidMediaTypeValue as exc:
errors.append(exc)
else:
try:
data = self._unmarshal(media_type, casted)
except (ValidateError, UnmarshalError) as exc:
errors.append(exc)
return None, [exc, ]
return data, errors
try:
raw_data = self._get_data_value(response)
except MissingResponseContent as exc:
return None, [exc, ]
try:
deserialised = self._deserialise_media_type(media_type, raw_data)
except DeserializeError as exc:
return None, [exc, ]
try:
casted = self._cast(media_type, deserialised)
except CastError as exc:
return None, [exc, ]
try:
data = self._unmarshal(media_type, casted)
except (ValidateError, UnmarshalError) as exc:
return None, [exc, ]
return data, []
def _get_headers(self, response, operation_response):
errors = []
@ -100,6 +102,30 @@ class ResponseValidator(object):
return headers, errors
def _get_data_value(self, response):
if not response.data:
raise MissingResponseContent(response)
return response.data
def _deserialise_media_type(self, media_type, value):
from openapi_core.deserializing.media_types.factories import (
MediaTypeDeserializersFactory,
)
deserializers_factory = MediaTypeDeserializersFactory()
deserializer = deserializers_factory.create(media_type)
return deserializer(value)
def _cast(self, param_or_media_type, value):
# return param_or_media_type.cast(value)
if not param_or_media_type.schema:
return value
from openapi_core.casting.schemas.factories import SchemaCastersFactory
casters_factory = SchemaCastersFactory()
caster = casters_factory.create(param_or_media_type.schema)
return caster(value)
def _unmarshal(self, param_or_media_type, value):
if not param_or_media_type.schema:
return value

View file

@ -100,14 +100,12 @@ class TestFlaskOpenAPIDecorator(object):
'errors': [
{
'class': (
"<class 'openapi_core.schema.parameters."
"exceptions.InvalidParameterValue'>"
"<class 'openapi_core.casting.schemas.exceptions."
"CastError'>"
),
'status': 400,
'title': (
'Invalid parameter value for `id`: '
'Failed to cast value invalidparameter to type '
'SchemaType.INTEGER'
"Failed to cast value invalidparameter to type integer"
)
}
]

View file

@ -87,14 +87,12 @@ class TestFlaskOpenAPIView(object):
'errors': [
{
'class': (
"<class 'openapi_core.schema.parameters."
"exceptions.InvalidParameterValue'>"
"<class 'openapi_core.casting.schemas.exceptions."
"CastError'>"
),
'status': 400,
'title': (
'Invalid parameter value for `id`: '
'Failed to cast value invalidparameter to type '
'SchemaType.INTEGER'
"Failed to cast value invalidparameter to type integer"
)
}
]

View file

@ -5,10 +5,15 @@ from base64 import b64encode
from uuid import UUID
from six import text_type
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.deserializing.parameters.exceptions import (
EmptyParameterValue,
)
from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter, InvalidParameterValue, EmptyParameterValue,
MissingRequiredParameter,
)
from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.schema.servers.exceptions import InvalidServer
@ -272,7 +277,7 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params,
)
with pytest.raises(InvalidParameterValue):
with pytest.raises(DeserializeError):
validate_parameters(spec, request)
body = validate_body(spec, request)
@ -291,7 +296,7 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params,
)
with pytest.raises(InvalidParameterValue):
with pytest.raises(CastError):
validate_parameters(spec, request)
body = validate_body(spec, request)

View file

@ -3,13 +3,14 @@ import json
import pytest
from six import text_type
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import (
InvalidContentType, InvalidMediaTypeValue,
InvalidContentType,
)
from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
from openapi_core.schema.parameters.exceptions import InvalidParameterValue
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
from openapi_core.schema.responses.exceptions import (
@ -317,7 +318,7 @@ class TestPathItemParamsValidator(object):
result = validator.validate(request)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidParameterValue
assert type(result.errors[0]) == CastError
assert result.body is None
assert result.parameters == RequestParameters()
@ -455,7 +456,7 @@ class TestResponseValidator(object):
result = validator.validate(request, response)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidMediaTypeValue
assert type(result.errors[0]) == DeserializeError
assert result.data is None
assert result.headers == {}

View file

@ -0,0 +1,71 @@
import pytest
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.deserializing.media_types.factories import (
MediaTypeDeserializersFactory,
)
from openapi_core.deserializing.parameters.factories import (
ParameterDeserializersFactory,
)
from openapi_core.deserializing.parameters.exceptions import (
EmptyParameterValue,
)
from openapi_core.schema.media_types.models import MediaType
from openapi_core.schema.parameters.models import Parameter
class TestParameterDeserialise(object):
@pytest.fixture
def deserializer_factory(self):
def create_deserializer(param):
return ParameterDeserializersFactory().create(param)
return create_deserializer
def test_deprecated(self, deserializer_factory):
param = Parameter('param', 'query', deprecated=True)
value = 'test'
with pytest.warns(DeprecationWarning):
result = deserializer_factory(param)(value)
assert result == value
def test_query_empty(self, deserializer_factory):
param = Parameter('param', 'query')
value = ''
with pytest.raises(EmptyParameterValue):
deserializer_factory(param)(value)
def test_query_valid(self, deserializer_factory):
param = Parameter('param', 'query')
value = 'test'
result = deserializer_factory(param)(value)
assert result == value
class TestMediaTypeDeserialise(object):
@pytest.fixture
def deserializer_factory(self):
def create_deserializer(media_type):
return MediaTypeDeserializersFactory().create(media_type)
return create_deserializer
def test_empty(self, deserializer_factory):
media_type = MediaType('application/json')
value = ''
with pytest.raises(DeserializeError):
deserializer_factory(media_type)(value)
def test_no_schema_deserialised(self, deserializer_factory):
media_type = MediaType('application/json')
value = "{}"
result = deserializer_factory(media_type)(value)
assert result == {}

View file

@ -1,22 +0,0 @@
import pytest
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
from openapi_core.schema.media_types.models import MediaType
class TestMediaTypeCast(object):
def test_empty(self):
media_type = MediaType('application/json')
value = ''
with pytest.raises(InvalidMediaTypeValue):
media_type.cast(value)
def test_no_schema_deserialised(self):
media_type = MediaType('application/json')
value = "{}"
result = media_type.cast(value)
assert result == {}

View file

@ -1,8 +1,3 @@
import pytest
from openapi_core.schema.parameters.exceptions import (
EmptyParameterValue,
)
from openapi_core.schema.parameters.enums import ParameterStyle
from openapi_core.schema.parameters.models import Parameter
@ -36,30 +31,3 @@ class TestParameterInit(object):
assert param.allow_empty_value is False
assert param.style == ParameterStyle.FORM
assert param.explode is True
class TestParameterCast(object):
def test_deprecated(self):
param = Parameter('param', 'query', deprecated=True)
value = 'test'
with pytest.warns(DeprecationWarning):
result = param.cast(value)
assert result == value
def test_query_empty(self):
param = Parameter('param', 'query')
value = ''
with pytest.raises(EmptyParameterValue):
param.cast(value)
def test_query_valid(self):
param = Parameter('param', 'query')
value = 'test'
result = param.cast(value)
assert result == value