diff --git a/openapi_core/__init__.py b/openapi_core/__init__.py index 75f382c8..05711ee1 100644 --- a/openapi_core/__init__.py +++ b/openapi_core/__init__.py @@ -1,10 +1,16 @@ """OpenAPI core module""" +from openapi_core.shortcuts import unmarshal_apicall_request +from openapi_core.shortcuts import unmarshal_apicall_response from openapi_core.shortcuts import unmarshal_request from openapi_core.shortcuts import unmarshal_response from openapi_core.shortcuts import unmarshal_webhook_request from openapi_core.shortcuts import unmarshal_webhook_response +from openapi_core.shortcuts import validate_apicall_request +from openapi_core.shortcuts import validate_apicall_response from openapi_core.shortcuts import validate_request from openapi_core.shortcuts import validate_response +from openapi_core.shortcuts import validate_webhook_request +from openapi_core.shortcuts import validate_webhook_response from openapi_core.spec import Spec from openapi_core.unmarshalling.request import RequestValidator from openapi_core.unmarshalling.request import V3RequestUnmarshaller @@ -47,8 +53,14 @@ "Spec", "unmarshal_request", "unmarshal_response", + "unmarshal_apicall_request", "unmarshal_webhook_request", + "unmarshal_apicall_response", "unmarshal_webhook_response", + "validate_apicall_request", + "validate_webhook_request", + "validate_apicall_response", + "validate_webhook_response", "validate_request", "validate_response", "V30RequestUnmarshaller", diff --git a/openapi_core/shortcuts.py b/openapi_core/shortcuts.py index 91a5fc3e..3ffe688e 100644 --- a/openapi_core/shortcuts.py +++ b/openapi_core/shortcuts.py @@ -1,4 +1,4 @@ -"""OpenAPI core validation shortcuts module""" +"""OpenAPI core shortcuts module""" import warnings from typing import Any from typing import Dict @@ -29,12 +29,6 @@ from openapi_core.unmarshalling.request.types import ( WebhookRequestUnmarshallerType, ) -from openapi_core.unmarshalling.request.unmarshallers import ( - BaseAPICallRequestUnmarshaller, -) -from openapi_core.unmarshalling.request.unmarshallers import ( - BaseWebhookRequestUnmarshaller, -) from openapi_core.unmarshalling.response import V30ResponseUnmarshaller from openapi_core.unmarshalling.response import V31ResponseUnmarshaller from openapi_core.unmarshalling.response import V31WebhookResponseUnmarshaller @@ -58,9 +52,19 @@ from openapi_core.validation.request import V30RequestValidator from openapi_core.validation.request import V31RequestValidator from openapi_core.validation.request import V31WebhookRequestValidator +from openapi_core.validation.request.protocols import RequestValidator +from openapi_core.validation.request.protocols import WebhookRequestValidator +from openapi_core.validation.request.types import AnyRequestValidatorType +from openapi_core.validation.request.types import RequestValidatorType +from openapi_core.validation.request.types import WebhookRequestValidatorType from openapi_core.validation.response import V30ResponseValidator from openapi_core.validation.response import V31ResponseValidator from openapi_core.validation.response import V31WebhookResponseValidator +from openapi_core.validation.response.protocols import ResponseValidator +from openapi_core.validation.response.protocols import WebhookResponseValidator +from openapi_core.validation.response.types import AnyResponseValidatorType +from openapi_core.validation.response.types import ResponseValidatorType +from openapi_core.validation.response.types import WebhookResponseValidatorType AnyRequest = Union[Request, WebhookRequest] @@ -92,7 +96,7 @@ def get_classes(spec: Spec) -> SpecClasses: return SpecFinder(SPECS).get_classes(spec) -def unmarshal_request( +def unmarshal_apicall_request( request: Request, spec: Spec, base_url: Optional[str] = None, @@ -140,7 +144,46 @@ def unmarshal_webhook_request( return result -def unmarshal_response( +def unmarshal_request( + request: AnyRequest, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[AnyRequestUnmarshallerType] = None, + **unmarshaller_kwargs: Any, +) -> RequestUnmarshalResult: + if not isinstance(request, (Request, WebhookRequest)): + raise TypeError("'request' argument is not type of (Webhook)Request") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if isinstance(request, WebhookRequest): + if cls is None or issubclass(cls, WebhookRequestUnmarshaller): + return unmarshal_webhook_request( + request, + spec, + base_url=base_url, + cls=cls, + **unmarshaller_kwargs, + ) + else: + raise TypeError( + "'cls' argument is not type of WebhookRequestUnmarshaller" + ) + else: + if cls is None or issubclass(cls, RequestUnmarshaller): + return unmarshal_apicall_request( + request, + spec, + base_url=base_url, + cls=cls, + **unmarshaller_kwargs, + ) + else: + raise TypeError( + "'cls' argument is not type of RequestUnmarshaller" + ) + + +def unmarshal_apicall_response( request: Request, response: Response, spec: Spec, @@ -194,14 +237,58 @@ def unmarshal_webhook_response( return result +def unmarshal_response( + request: AnyRequest, + response: Response, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[AnyResponseUnmarshallerType] = None, + **unmarshaller_kwargs: Any, +) -> ResponseUnmarshalResult: + if not isinstance(request, (Request, WebhookRequest)): + raise TypeError("'request' argument is not type of (Webhook)Request") + if not isinstance(response, Response): + raise TypeError("'response' argument is not type of Response") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if isinstance(request, WebhookRequest): + if cls is None or issubclass(cls, WebhookResponseUnmarshaller): + return unmarshal_webhook_response( + request, + response, + spec, + base_url=base_url, + cls=cls, + **unmarshaller_kwargs, + ) + else: + raise TypeError( + "'cls' argument is not type of WebhookResponseUnmarshaller" + ) + else: + if cls is None or issubclass(cls, ResponseUnmarshaller): + return unmarshal_apicall_response( + request, + response, + spec, + base_url=base_url, + cls=cls, + **unmarshaller_kwargs, + ) + else: + raise TypeError( + "'cls' argument is not type of ResponseUnmarshaller" + ) + + def validate_request( request: AnyRequest, spec: Spec, base_url: Optional[str] = None, validator: Optional[SpecRequestValidatorProxy] = None, - cls: Optional[AnyRequestUnmarshallerType] = None, + cls: Optional[AnyRequestValidatorType] = None, **validator_kwargs: Any, -) -> RequestUnmarshalResult: +) -> Optional[RequestUnmarshalResult]: if isinstance(spec, (Request, WebhookRequest)) and isinstance( request, Spec ): @@ -226,24 +313,49 @@ def validate_request( result.raise_for_errors() return result + # redirect to unmarshaller for backward compatibility + if cls is None or issubclass( + cls, (RequestUnmarshaller, WebhookRequestUnmarshaller) + ): + warnings.warn( + "validate_request is deprecated for unmarshalling data " + "and it will not return any result in the future. " + "Use unmarshal_request function instead.", + DeprecationWarning, + ) + return unmarshal_request( + request, + spec=spec, + base_url=base_url, + cls=cls, + **validator_kwargs, + ) if isinstance(request, WebhookRequest): - if cls is None or issubclass(cls, WebhookRequestUnmarshaller): - return unmarshal_webhook_request( - request, spec, base_url=base_url, cls=cls, **validator_kwargs + if cls is None or issubclass(cls, WebhookRequestValidator): + validate_webhook_request( + request, + spec, + base_url=base_url, + cls=cls, + **validator_kwargs, ) + return None else: raise TypeError( - "'cls' argument is not type of WebhookRequestUnmarshaller" + "'cls' argument is not type of WebhookRequestValidator" ) - elif isinstance(request, Request): - if cls is None or issubclass(cls, RequestUnmarshaller): - return unmarshal_request( - request, spec, base_url=base_url, cls=cls, **validator_kwargs + else: + if cls is None or issubclass(cls, RequestValidator): + validate_apicall_request( + request, + spec, + base_url=base_url, + cls=cls, + **validator_kwargs, ) + return None else: - raise TypeError( - "'cls' argument is not type of RequestUnmarshaller" - ) + raise TypeError("'cls' argument is not type of RequestValidator") def validate_response( @@ -252,9 +364,9 @@ def validate_response( spec: Union[Spec, Response], base_url: Optional[str] = None, validator: Optional[SpecResponseValidatorProxy] = None, - cls: Optional[AnyResponseUnmarshallerType] = None, + cls: Optional[AnyResponseValidatorType] = None, **validator_kwargs: Any, -) -> ResponseUnmarshalResult: +) -> Optional[ResponseUnmarshalResult]: if ( isinstance(request, Spec) and isinstance(response, (Request, WebhookRequest)) @@ -284,9 +396,27 @@ def validate_response( result.raise_for_errors() return result + # redirect to unmarshaller for backward compatibility + if cls is None or issubclass( + cls, (ResponseUnmarshaller, WebhookResponseUnmarshaller) + ): + warnings.warn( + "validate_response is deprecated for unmarshalling data " + "and it will not return any result in the future. " + "Use unmarshal_response function instead.", + DeprecationWarning, + ) + return unmarshal_response( + request, + response, + spec=spec, + base_url=base_url, + cls=cls, + **validator_kwargs, + ) if isinstance(request, WebhookRequest): - if cls is None or issubclass(cls, WebhookResponseUnmarshaller): - return unmarshal_webhook_response( + if cls is None or issubclass(cls, WebhookResponseValidator): + validate_webhook_response( request, response, spec, @@ -294,13 +424,14 @@ def validate_response( cls=cls, **validator_kwargs, ) + return None else: raise TypeError( - "'cls' argument is not type of WebhookResponseUnmarshaller" + "'cls' argument is not type of WebhookResponseValidator" ) - elif isinstance(request, Request): - if cls is None or issubclass(cls, ResponseUnmarshaller): - return unmarshal_response( + else: + if cls is None or issubclass(cls, ResponseValidator): + validate_apicall_response( request, response, spec, @@ -308,7 +439,100 @@ def validate_response( cls=cls, **validator_kwargs, ) + return None else: - raise TypeError( - "'cls' argument is not type of ResponseUnmarshaller" - ) + raise TypeError("'cls' argument is not type of ResponseValidator") + + +def validate_apicall_request( + request: Request, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[RequestValidatorType] = None, + **validator_kwargs: Any, +) -> None: + if not isinstance(request, Request): + raise TypeError("'request' argument is not type of Request") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if cls is None: + classes = get_classes(spec) + cls = classes.request_validator_cls + if not issubclass(cls, RequestValidator): + raise TypeError("'cls' argument is not type of RequestValidator") + v = cls(spec, base_url=base_url, **validator_kwargs) + return v.validate(request) + + +def validate_webhook_request( + request: WebhookRequest, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[WebhookRequestValidatorType] = None, + **validator_kwargs: Any, +) -> None: + if not isinstance(request, WebhookRequest): + raise TypeError("'request' argument is not type of WebhookRequest") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if cls is None: + classes = get_classes(spec) + cls = classes.webhook_request_validator_cls + if cls is None: + raise SpecError("Validator class not found") + if not issubclass(cls, WebhookRequestValidator): + raise TypeError( + "'cls' argument is not type of WebhookRequestValidator" + ) + v = cls(spec, base_url=base_url, **validator_kwargs) + return v.validate(request) + + +def validate_apicall_response( + request: Request, + response: Response, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[ResponseValidatorType] = None, + **validator_kwargs: Any, +) -> None: + if not isinstance(request, Request): + raise TypeError("'request' argument is not type of Request") + if not isinstance(response, Response): + raise TypeError("'response' argument is not type of Response") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if cls is None: + classes = get_classes(spec) + cls = classes.response_validator_cls + if not issubclass(cls, ResponseValidator): + raise TypeError("'cls' argument is not type of ResponseValidator") + v = cls(spec, base_url=base_url, **validator_kwargs) + return v.validate(request, response) + + +def validate_webhook_response( + request: WebhookRequest, + response: Response, + spec: Spec, + base_url: Optional[str] = None, + cls: Optional[WebhookResponseValidatorType] = None, + **validator_kwargs: Any, +) -> None: + if not isinstance(request, WebhookRequest): + raise TypeError("'request' argument is not type of WebhookRequest") + if not isinstance(response, Response): + raise TypeError("'response' argument is not type of Response") + if not isinstance(spec, Spec): + raise TypeError("'spec' argument is not type of Spec") + if cls is None: + classes = get_classes(spec) + cls = classes.webhook_response_validator_cls + if cls is None: + raise SpecError("Validator class not found") + if not issubclass(cls, WebhookResponseValidator): + raise TypeError( + "'cls' argument is not type of WebhookResponseValidator" + ) + v = cls(spec, base_url=base_url, **validator_kwargs) + return v.validate(request, response) diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py index 3f2b2781..5ab2ea42 100644 --- a/tests/integration/test_petstore.py +++ b/tests/integration/test_petstore.py @@ -1,13 +1,14 @@ import json from base64 import b64encode from dataclasses import is_dataclass -from dataclasses import make_dataclass from datetime import datetime from uuid import UUID import pytest from isodate.tzinfo import UTC +from openapi_core import unmarshal_request +from openapi_core import unmarshal_response from openapi_core import validate_request from openapi_core import validate_response from openapi_core.casting.schemas.exceptions import CastError @@ -16,7 +17,6 @@ from openapi_core.deserializing.parameters.exceptions import ( EmptyQueryParameterValue, ) -from openapi_core.spec import Spec from openapi_core.templating.media_types.exceptions import MediaTypeNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.templating.security.exceptions import SecurityNotFound @@ -44,8 +44,18 @@ from openapi_core.validation.request.exceptions import ParameterError from openapi_core.validation.request.exceptions import RequestBodyError from openapi_core.validation.request.exceptions import SecurityError +from openapi_core.validation.request.validators import V30RequestBodyValidator +from openapi_core.validation.request.validators import ( + V30RequestParametersValidator, +) +from openapi_core.validation.request.validators import ( + V30RequestSecurityValidator, +) from openapi_core.validation.response.exceptions import InvalidData from openapi_core.validation.response.exceptions import MissingRequiredHeader +from openapi_core.validation.response.validators import ( + V30ResponseDataValidator, +) from openapi_core.validation.schemas.exceptions import InvalidSchemaValue @@ -100,7 +110,7 @@ def test_get_pets(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller, @@ -118,7 +128,8 @@ def test_get_pets(self, spec): } response = MockResponse(data, headers=headers) - response_result = validate_response(request, response, spec=spec) + with pytest.warns(DeprecationWarning): + response_result = validate_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -157,7 +168,7 @@ def test_get_pets_response(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -177,7 +188,7 @@ def test_get_pets_response(self, spec): data = json.dumps(data_json) response = MockResponse(data) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -215,7 +226,7 @@ def test_get_pets_response_no_schema(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -225,7 +236,7 @@ def test_get_pets_response_no_schema(self, spec): response = MockResponse(data, status_code=404, mimetype="text/html") with pytest.warns(UserWarning): - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert response_result.data == data @@ -260,7 +271,7 @@ def test_get_pets_invalid_response(self, spec, response_unmarshaller): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -284,7 +295,7 @@ def test_get_pets_invalid_response(self, spec, response_unmarshaller): request, response, spec=spec, - cls=V30ResponseDataUnmarshaller, + cls=V30ResponseDataValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -331,7 +342,7 @@ def test_get_pets_ids_param(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -343,7 +354,7 @@ def test_get_pets_ids_param(self, spec): data = json.dumps(data_json) response = MockResponse(data) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -381,7 +392,7 @@ def test_get_pets_tags_param(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -393,7 +404,7 @@ def test_get_pets_tags_param(self, spec): data = json.dumps(data_json) response = MockResponse(data) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -424,7 +435,7 @@ def test_get_pets_parameter_deserialization_error(self, spec): ) assert type(exc_info.value.__cause__) is DeserializeError - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -450,11 +461,11 @@ def test_get_pets_wrong_parameter_type(self, spec): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) assert type(exc_info.value.__cause__) is CastError - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -475,10 +486,10 @@ def test_get_pets_raises_missing_required_param(self, spec): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -504,11 +515,11 @@ def test_get_pets_empty_value(self, spec): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) assert type(exc_info.value.__cause__) is EmptyQueryParameterValue - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -545,7 +556,7 @@ def test_get_pets_allow_empty_value(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -581,7 +592,7 @@ def test_get_pets_none_value(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -619,7 +630,7 @@ def test_get_pets_param_order(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -660,7 +671,7 @@ def test_get_pets_param_coordinates(self, spec): assert result.parameters.query["coordinates"].lat == coordinates["lat"] assert result.parameters.query["coordinates"].lon == coordinates["lon"] - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -710,7 +721,7 @@ def test_post_birds(self, spec, spec_dict): cookies=cookies, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -723,7 +734,7 @@ def test_post_birds(self, spec, spec_dict): ) assert result.parameters.cookie["userdata"].name == "user1" - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -739,7 +750,7 @@ def test_post_birds(self, spec, spec_dict): assert result.body.address.city == pet_city assert result.body.healthy == pet_healthy - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestSecurityUnmarshaller, @@ -787,7 +798,7 @@ def test_post_cats(self, spec, spec_dict): cookies=cookies, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -802,7 +813,7 @@ def test_post_cats(self, spec, spec_dict): }, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -858,7 +869,7 @@ def test_post_cats_boolean_string(self, spec, spec_dict): cookies=cookies, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -873,9 +884,10 @@ def test_post_cats_boolean_string(self, spec, spec_dict): }, ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] @@ -916,11 +928,12 @@ def test_post_no_one_of_schema(self, spec): cookies=cookies, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( header={ @@ -935,7 +948,7 @@ def test_post_no_one_of_schema(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -968,11 +981,12 @@ def test_post_cats_only_required_body(self, spec, spec_dict): cookies=cookies, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( header={ @@ -983,9 +997,10 @@ def test_post_cats_only_required_body(self, spec, spec_dict): }, ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] @@ -1020,11 +1035,12 @@ def test_post_pets_raises_invalid_mimetype(self, spec): cookies=cookies, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( header={ @@ -1039,7 +1055,7 @@ def test_post_pets_raises_invalid_mimetype(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is MediaTypeNotFound @@ -1072,12 +1088,13 @@ def test_post_pets_missing_cookie(self, spec, spec_dict): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] @@ -1115,12 +1132,13 @@ def test_post_pets_missing_header(self, spec, spec_dict): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] @@ -1159,14 +1177,14 @@ def test_post_pets_raises_invalid_server_error(self, spec): validate_request( request, spec=spec, - cls=V30RequestParametersUnmarshaller, + cls=V30RequestParametersValidator, ) with pytest.raises(ServerNotFound): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) data_id = 1 @@ -1188,7 +1206,7 @@ def test_post_pets_raises_invalid_server_error(self, spec): request, response, spec=spec, - cls=V30ResponseDataUnmarshaller, + cls=V30ResponseDataValidator, ) def test_get_pet_invalid_security(self, spec): @@ -1210,7 +1228,7 @@ def test_get_pet_invalid_security(self, spec): validate_request( request, spec=spec, - cls=V30RequestSecurityUnmarshaller, + cls=V30RequestSecurityValidator, ) assert exc_info.value.__cause__ == SecurityNotFound( @@ -1236,11 +1254,12 @@ def test_get_pet(self, spec): headers=headers, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( path={ @@ -1248,17 +1267,19 @@ def test_get_pet(self, spec): } ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) assert result.body is None - result = validate_request( - request, - spec=spec, - cls=V30RequestSecurityUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestSecurityUnmarshaller, + ) assert result.security == { "petstore_auth": auth, @@ -1278,7 +1299,7 @@ def test_get_pet(self, spec): data = json.dumps(data_json) response = MockResponse(data) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1300,11 +1321,12 @@ def test_get_pet_not_found(self, spec): view_args=view_args, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( path={ @@ -1312,9 +1334,10 @@ def test_get_pet_not_found(self, spec): } ) - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) assert result.body is None @@ -1329,7 +1352,7 @@ def test_get_pet_not_found(self, spec): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1351,11 +1374,12 @@ def test_get_pet_wildcard(self, spec): view_args=view_args, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters( path={ @@ -1363,7 +1387,7 @@ def test_get_pet_wildcard(self, spec): } ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller, @@ -1375,7 +1399,7 @@ def test_get_pet_wildcard(self, spec): response = MockResponse(data, mimetype="image/png") with pytest.warns(UserWarning): - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert response_result.data == data @@ -1391,7 +1415,7 @@ def test_get_tags(self, spec): path_pattern=path_pattern, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1399,9 +1423,10 @@ def test_get_tags(self, spec): assert result.parameters == Parameters() - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) assert result.body is None @@ -1409,7 +1434,8 @@ def test_get_tags(self, spec): data = json.dumps(data_json) response = MockResponse(data) - response_result = validate_response(request, response, spec=spec) + with pytest.warns(DeprecationWarning): + response_result = validate_response(request, response, spec=spec) assert response_result.errors == [] assert response_result.data == data_json @@ -1433,7 +1459,7 @@ def test_post_tags_extra_body_properties(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1445,7 +1471,7 @@ def test_post_tags_extra_body_properties(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -1463,7 +1489,7 @@ def test_post_tags_empty_body(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1475,7 +1501,7 @@ def test_post_tags_empty_body(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -1493,7 +1519,7 @@ def test_post_tags_wrong_property_type(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1505,7 +1531,7 @@ def test_post_tags_wrong_property_type(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -1526,17 +1552,19 @@ def test_post_tags_additional_properties(self, spec): data=data, ) - result = validate_request( - request, - spec=spec, - cls=V30RequestParametersUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, + spec=spec, + cls=V30RequestParametersUnmarshaller, + ) assert result.parameters == Parameters() - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) assert is_dataclass(result.body) assert result.body.name == pet_name @@ -1554,7 +1582,8 @@ def test_post_tags_additional_properties(self, spec): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = validate_response(request, response, spec=spec) + with pytest.warns(DeprecationWarning): + response_result = validate_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1582,7 +1611,7 @@ def test_post_tags_created_now(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1590,7 +1619,7 @@ def test_post_tags_created_now(self, spec): assert result.parameters == Parameters() - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -1611,7 +1640,7 @@ def test_post_tags_created_now(self, spec): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1639,7 +1668,7 @@ def test_post_tags_created_datetime(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1647,9 +1676,10 @@ def test_post_tags_created_datetime(self, spec): assert result.parameters == Parameters() - result = validate_request( - request, spec=spec, cls=V30RequestBodyUnmarshaller - ) + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec, cls=V30RequestBodyUnmarshaller + ) assert is_dataclass(result.body) assert result.body.created == datetime( @@ -1670,12 +1700,13 @@ def test_post_tags_created_datetime(self, spec): response_data = json.dumps(response_data_json) response = MockResponse(response_data, status_code=404) - result = validate_response( - request, - response, - spec=spec, - cls=V30ResponseDataUnmarshaller, - ) + with pytest.warns(DeprecationWarning): + result = validate_response( + request, + response, + spec=spec, + cls=V30ResponseDataUnmarshaller, + ) assert is_dataclass(result.data) assert result.data.code == code @@ -1683,7 +1714,7 @@ def test_post_tags_created_datetime(self, spec): assert result.data.rootCause == rootCause assert result.data.additionalinfo == additionalinfo - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1711,7 +1742,7 @@ def test_post_tags_created_invalid_type(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1723,7 +1754,7 @@ def test_post_tags_created_invalid_type(self, spec): validate_request( request, spec=spec, - cls=V30RequestBodyUnmarshaller, + cls=V30RequestBodyValidator, ) assert type(exc_info.value.__cause__) is InvalidSchemaValue @@ -1741,7 +1772,7 @@ def test_post_tags_created_invalid_type(self, spec): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = validate_response(request, response, spec=spec) + response_result = unmarshal_response(request, response, spec=spec) assert response_result.errors == [] assert is_dataclass(response_result.data) @@ -1767,7 +1798,7 @@ def test_delete_tags_with_requestbody(self, spec): data=data, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1775,7 +1806,7 @@ def test_delete_tags_with_requestbody(self, spec): assert result.parameters == Parameters() - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -1815,7 +1846,7 @@ def test_delete_tags_no_requestbody(self, spec): path_pattern=path_pattern, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1823,7 +1854,7 @@ def test_delete_tags_no_requestbody(self, spec): assert result.parameters == Parameters() - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) @@ -1841,7 +1872,7 @@ def test_delete_tags_raises_missing_required_response_header( path_pattern=path_pattern, ) - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestParametersUnmarshaller, @@ -1849,7 +1880,7 @@ def test_delete_tags_raises_missing_required_response_header( assert result.parameters == Parameters() - result = validate_request( + result = unmarshal_request( request, spec=spec, cls=V30RequestBodyUnmarshaller ) diff --git a/tests/unit/test_shortcuts.py b/tests/unit/test_shortcuts.py index 796722e3..1c14e694 100644 --- a/tests/unit/test_shortcuts.py +++ b/tests/unit/test_shortcuts.py @@ -4,12 +4,18 @@ from openapi_core import RequestValidator from openapi_core import ResponseValidator +from openapi_core import unmarshal_apicall_request +from openapi_core import unmarshal_apicall_response from openapi_core import unmarshal_request from openapi_core import unmarshal_response from openapi_core import unmarshal_webhook_request from openapi_core import unmarshal_webhook_response +from openapi_core import validate_apicall_request +from openapi_core import validate_apicall_response from openapi_core import validate_request from openapi_core import validate_response +from openapi_core import validate_webhook_request +from openapi_core import validate_webhook_response from openapi_core.exceptions import SpecError from openapi_core.protocols import Request from openapi_core.protocols import Response @@ -22,9 +28,23 @@ from openapi_core.unmarshalling.request.unmarshallers import ( WebhookRequestUnmarshaller, ) +from openapi_core.unmarshalling.response.datatypes import ( + ResponseUnmarshalResult, +) from openapi_core.unmarshalling.response.unmarshallers import ( APICallResponseUnmarshaller, ) +from openapi_core.unmarshalling.response.unmarshallers import ( + WebhookResponseUnmarshaller, +) +from openapi_core.validation.request.validators import APICallRequestValidator +from openapi_core.validation.request.validators import WebhookRequestValidator +from openapi_core.validation.response.validators import ( + APICallResponseValidator, +) +from openapi_core.validation.response.validators import ( + WebhookResponseValidator, +) class MockClass: @@ -32,82 +52,69 @@ class MockClass: schema_unmarshallers_factory = None unmarshal_calls = [] + validate_calls = [] return_unmarshal = None @classmethod - def setUp(cls, return_unmarshal): + def setUp(cls, return_unmarshal=None): cls.unmarshal_calls = [] + cls.validate_calls = [] cls.return_unmarshal = return_unmarshal -class MockReqClass(MockClass): - assert_request = None +class MockReqValidator(MockClass): + def validate(self, req): + self.validate_calls.append((req,)) - @classmethod - def setUp(cls, return_unmarshal, assert_request): - super().setUp(return_unmarshal) - cls.assert_request = assert_request +class MockReqUnmarshaller(MockClass): def unmarshal(self, req): - self.unmarshal_calls.append([req]) - assert req == self.assert_request + self.unmarshal_calls.append((req,)) return self.return_unmarshal -class MockRespClass(MockClass): - assert_request = None - assert_response = None +class MockRespValidator(MockClass): + def validate(self, req, resp): + self.validate_calls.append((req, resp)) - @classmethod - def setUp(cls, return_unmarshal, assert_request, assert_response): - super().setUp(return_unmarshal) - cls.assert_request = assert_request - cls.assert_response = assert_response +class MockRespUnmarshaller(MockClass): def unmarshal(self, req, resp): - self.unmarshal_calls.append([req, resp]) - assert req == self.assert_request - assert resp == self.assert_response + self.unmarshal_calls.append((req, resp)) return self.return_unmarshal -class TestUnmarshalRequest: +@pytest.fixture(autouse=True) +def setup(): + MockClass.setUp() + yield + + +class TestUnmarshalAPICallRequest: def test_spec_not_detected(self, spec_invalid): request = mock.Mock(spec=Request) with pytest.raises(SpecError): - unmarshal_request(request, spec=spec_invalid) + unmarshal_apicall_request(request, spec=spec_invalid) def test_request_type_invalid(self, spec_v31): request = mock.sentinel.request with pytest.raises(TypeError): - unmarshal_request(request, spec=spec_v31) + unmarshal_apicall_request(request, spec=spec_v31) def test_spec_type_invalid(self): request = mock.Mock(spec=Request) spec = mock.sentinel.spec with pytest.raises(TypeError): - unmarshal_request(request, spec=spec) + unmarshal_apicall_request(request, spec=spec) def test_cls_type_invalid(self, spec_v31): request = mock.Mock(spec=Request) with pytest.raises(TypeError): - unmarshal_request(request, spec=spec_v31, cls=Exception) - - @mock.patch( - "openapi_core.unmarshalling.request.unmarshallers.APICallRequestUnmarshaller." - "unmarshal", - ) - def test_request(self, mock_unmarshal, spec_v31): - request = mock.Mock(spec=Request) - - result = unmarshal_request(request, spec=spec_v31) - - assert result == mock_unmarshal.return_value - mock_unmarshal.assert_called_once_with(request) + unmarshal_apicall_request(request, spec=spec_v31, cls=Exception) class TestUnmarshalWebhookRequest: @@ -136,6 +143,12 @@ def test_cls_type_invalid(self, spec_v31): with pytest.raises(TypeError): unmarshal_webhook_request(request, spec=spec_v31, cls=Exception) + def test_spec_oas30_validator_not_found(self, spec_v30): + request = mock.Mock(spec=WebhookRequest) + + with pytest.raises(SpecError): + unmarshal_webhook_request(request, spec=spec_v30) + @mock.patch( "openapi_core.unmarshalling.request.unmarshallers.WebhookRequestUnmarshaller." "unmarshal", @@ -149,6 +162,86 @@ def test_request(self, mock_unmarshal, spec_v31): mock_unmarshal.assert_called_once_with(request) +class TestUnmarshalRequest: + def test_spec_not_detected(self, spec_invalid): + request = mock.Mock(spec=Request) + + with pytest.raises(SpecError): + unmarshal_request(request, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + + with pytest.raises(TypeError): + unmarshal_request(request, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=Request) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + unmarshal_request(request, spec=spec) + + @pytest.mark.parametrize("req", [Request, WebhookRequest]) + def test_cls_type_invalid(self, spec_v31, req): + request = mock.Mock(spec=req) + + with pytest.raises(TypeError): + unmarshal_request(request, spec=spec_v31, cls=Exception) + + @mock.patch( + "openapi_core.unmarshalling.request.unmarshallers.APICallRequestUnmarshaller." + "unmarshal", + ) + def test_request(self, mock_unmarshal, spec_v31): + request = mock.Mock(spec=Request) + + result = unmarshal_request(request, spec=spec_v31) + + assert result == mock_unmarshal.return_value + mock_unmarshal.assert_called_once_with(request) + + +class TestUnmarshalAPICallResponse: + def test_spec_not_detected(self, spec_invalid): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + + with pytest.raises(SpecError): + unmarshal_apicall_response(request, response, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + response = mock.Mock(spec=Response) + + with pytest.raises(TypeError): + unmarshal_apicall_response(request, response, spec=spec_v31) + + def test_response_type_invalid(self, spec_v31): + request = mock.Mock(spec=Request) + response = mock.sentinel.response + + with pytest.raises(TypeError): + unmarshal_apicall_response(request, response, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + unmarshal_apicall_response(request, response, spec=spec) + + def test_cls_type_invalid(self, spec_v31): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + + with pytest.raises(TypeError): + unmarshal_apicall_response( + request, response, spec=spec_v31, cls=Exception + ) + + class TestUnmarshalResponse: def test_spec_not_detected(self, spec_invalid): request = mock.Mock(spec=Request) @@ -179,8 +272,9 @@ def test_spec_type_invalid(self): with pytest.raises(TypeError): unmarshal_response(request, response, spec=spec) - def test_cls_type_invalid(self, spec_v31): - request = mock.Mock(spec=Request) + @pytest.mark.parametrize("req", [Request, WebhookRequest]) + def test_cls_type_invalid(self, spec_v31, req): + request = mock.Mock(spec=req) response = mock.Mock(spec=Response) with pytest.raises(TypeError): @@ -239,6 +333,13 @@ def test_cls_type_invalid(self, spec_v31): request, response, spec=spec_v31, cls=Exception ) + def test_spec_oas30_validator_not_found(self, spec_v30): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + + with pytest.raises(SpecError): + unmarshal_webhook_response(request, response, spec=spec_v30) + @mock.patch( "openapi_core.unmarshalling.response.unmarshallers.WebhookResponseUnmarshaller." "unmarshal", @@ -253,12 +354,97 @@ def test_request_response(self, mock_unmarshal, spec_v31): mock_unmarshal.assert_called_once_with(request, response) +class TestValidateAPICallRequest: + def test_spec_not_detected(self, spec_invalid): + request = mock.Mock(spec=Request) + + with pytest.raises(SpecError): + validate_apicall_request(request, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + + with pytest.raises(TypeError): + validate_apicall_request(request, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=Request) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + validate_apicall_request(request, spec=spec) + + def test_cls_type_invalid(self, spec_v31): + request = mock.Mock(spec=Request) + + with pytest.raises(TypeError): + validate_apicall_request(request, spec=spec_v31, cls=Exception) + + @mock.patch( + "openapi_core.validation.request.validators.APICallRequestValidator." + "validate", + ) + def test_request(self, mock_validate, spec_v31): + request = mock.Mock(spec=Request) + + result = validate_apicall_request(request, spec=spec_v31) + + assert result == mock_validate.return_value + mock_validate.assert_called_once_with(request) + + +class TestValidateWebhookRequest: + def test_spec_not_detected(self, spec_invalid): + request = mock.Mock(spec=WebhookRequest) + + with pytest.raises(SpecError): + validate_webhook_request(request, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + + with pytest.raises(TypeError): + validate_webhook_request(request, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=WebhookRequest) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + validate_webhook_request(request, spec=spec) + + def test_cls_type_invalid(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + + with pytest.raises(TypeError): + validate_webhook_request(request, spec=spec_v31, cls=Exception) + + def test_spec_oas30_validator_not_found(self, spec_v30): + request = mock.Mock(spec=WebhookRequest) + + with pytest.raises(SpecError): + validate_webhook_request(request, spec=spec_v30) + + @mock.patch( + "openapi_core.validation.request.validators.WebhookRequestValidator." + "validate", + ) + def test_request(self, mock_validate, spec_v31): + request = mock.Mock(spec=WebhookRequest) + + result = validate_webhook_request(request, spec=spec_v31) + + assert result == mock_validate.return_value + mock_validate.assert_called_once_with(request) + + class TestValidateRequest: def test_spec_not_detected(self, spec_invalid): request = mock.Mock(spec=Request) with pytest.raises(SpecError): - validate_request(request, spec=spec_invalid) + with pytest.warns(DeprecationWarning): + validate_request(request, spec=spec_invalid) def test_request_type_invalid(self, spec_v31): request = mock.sentinel.request @@ -280,7 +466,8 @@ def test_spec_type_invalid(self): def test_request(self, mock_unmarshal, spec_v31): request = mock.Mock(spec=Request) - result = validate_request(request, spec=spec_v31) + with pytest.warns(DeprecationWarning): + result = validate_request(request, spec=spec_v31) assert result == mock_unmarshal.return_value mock_unmarshal.assert_called_once_with(request) @@ -307,7 +494,8 @@ def test_request_error(self, mock_unmarshal, spec_v31): mock_unmarshal.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - validate_request(request, spec=spec_v31) + with pytest.warns(DeprecationWarning): + validate_request(request, spec=spec_v31) mock_unmarshal.assert_called_once_with(request) @@ -325,20 +513,90 @@ def test_validator(self, spec_v31): spec_v31, request, base_url=None ) - def test_cls(self, spec_v31): + def test_cls_apicall(self, spec_v31): request = mock.Mock(spec=Request) - unmarshal = mock.Mock(spec=RequestUnmarshalResult) TestAPICallReq = type( "TestAPICallReq", - (MockReqClass, APICallRequestUnmarshaller), + (MockReqValidator, APICallRequestValidator), {}, ) - TestAPICallReq.setUp(unmarshal, request) result = validate_request(request, spec=spec_v31, cls=TestAPICallReq) + assert result is None + assert TestAPICallReq.validate_calls == [ + (request,), + ] + + def test_cls_apicall_unmarshaller(self, spec_v31): + request = mock.Mock(spec=Request) + unmarshal = mock.Mock(spec=RequestUnmarshalResult) + TestAPICallReq = type( + "TestAPICallReq", + (MockReqUnmarshaller, APICallRequestUnmarshaller), + {}, + ) + TestAPICallReq.setUp(unmarshal) + + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec_v31, cls=TestAPICallReq + ) + + assert result == unmarshal + assert TestAPICallReq.unmarshal_calls == [ + (request,), + ] + + def test_cls_webhook(self, spec_v31): + request = mock.Mock(spec=Request) + TestWebhookReq = type( + "TestWebhookReq", + (MockReqValidator, WebhookRequestValidator), + {}, + ) + + result = validate_request(request, spec=spec_v31, cls=TestWebhookReq) + + assert result is None + assert TestWebhookReq.validate_calls == [ + (request,), + ] + + def test_webhook_cls(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + TestWebhookReq = type( + "TestWebhookReq", + (MockReqValidator, WebhookRequestValidator), + {}, + ) + + result = validate_request(request, spec=spec_v31, cls=TestWebhookReq) + + assert result is None + assert TestWebhookReq.validate_calls == [ + (request,), + ] + + def test_cls_webhook_unmarshaller(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + unmarshal = mock.Mock(spec=RequestUnmarshalResult) + TestWebhookReq = type( + "TestWebhookReq", + (MockReqUnmarshaller, WebhookRequestUnmarshaller), + {}, + ) + TestWebhookReq.setUp(unmarshal) + + with pytest.warns(DeprecationWarning): + result = validate_request( + request, spec=spec_v31, cls=TestWebhookReq + ) + assert result == unmarshal - assert len(TestAPICallReq.unmarshal_calls) == 1 + assert TestWebhookReq.unmarshal_calls == [ + (request,), + ] def test_cls_invalid(self, spec_v31): request = mock.Mock(spec=Request) @@ -353,7 +611,8 @@ def test_cls_invalid(self, spec_v31): def test_webhook_request(self, mock_unmarshal, spec_v31): request = mock.Mock(spec=WebhookRequest) - result = validate_request(request, spec=spec_v31) + with pytest.warns(DeprecationWarning): + result = validate_request(request, spec=spec_v31) assert result == mock_unmarshal.return_value mock_unmarshal.assert_called_once_with(request) @@ -362,7 +621,8 @@ def test_webhook_request_validator_not_found(self, spec_v30): request = mock.Mock(spec=WebhookRequest) with pytest.raises(SpecError): - validate_request(request, spec=spec_v30) + with pytest.warns(DeprecationWarning): + validate_request(request, spec=spec_v30) @mock.patch( "openapi_core.unmarshalling.request.unmarshallers.WebhookRequestUnmarshaller." @@ -373,30 +633,129 @@ def test_webhook_request_error(self, mock_unmarshal, spec_v31): mock_unmarshal.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - validate_request(request, spec=spec_v31) + with pytest.warns(DeprecationWarning): + validate_request(request, spec=spec_v31) mock_unmarshal.assert_called_once_with(request) - def test_webhook_cls(self, spec_v31): + def test_webhook_cls_invalid(self, spec_v31): request = mock.Mock(spec=WebhookRequest) - unmarshal = mock.Mock(spec=RequestUnmarshalResult) - TestWebhookReq = type( - "TestWebhookReq", - (MockReqClass, WebhookRequestUnmarshaller), - {}, - ) - TestWebhookReq.setUp(unmarshal, request) - result = validate_request(request, spec=spec_v31, cls=TestWebhookReq) + with pytest.raises(TypeError): + validate_request(request, spec=spec_v31, cls=Exception) - assert result == unmarshal - assert len(TestWebhookReq.unmarshal_calls) == 1 - def test_webhook_cls_invalid(self, spec_v31): +class TestValidateAPICallResponse: + def test_spec_not_detected(self, spec_invalid): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + + with pytest.raises(SpecError): + validate_apicall_response(request, response, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + response = mock.Mock(spec=Response) + + with pytest.raises(TypeError): + validate_apicall_response(request, response, spec=spec_v31) + + def test_response_type_invalid(self, spec_v31): + request = mock.Mock(spec=Request) + response = mock.sentinel.response + + with pytest.raises(TypeError): + validate_apicall_response(request, response, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + validate_apicall_response(request, response, spec=spec) + + def test_cls_type_invalid(self, spec_v31): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + + with pytest.raises(TypeError): + validate_apicall_response( + request, response, spec=spec_v31, cls=Exception + ) + + @mock.patch( + "openapi_core.validation.response.validators.APICallResponseValidator." + "validate", + ) + def test_request_response(self, mock_validate, spec_v31): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + + result = validate_apicall_response(request, response, spec=spec_v31) + + assert result == mock_validate.return_value + mock_validate.assert_called_once_with(request, response) + + +class TestValidateWebhookResponse: + def test_spec_not_detected(self, spec_invalid): request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + + with pytest.raises(SpecError): + validate_webhook_response(request, response, spec=spec_invalid) + + def test_request_type_invalid(self, spec_v31): + request = mock.sentinel.request + response = mock.Mock(spec=Response) with pytest.raises(TypeError): - validate_request(request, spec=spec_v31, cls=Exception) + validate_webhook_response(request, response, spec=spec_v31) + + def test_response_type_invalid(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + response = mock.sentinel.response + + with pytest.raises(TypeError): + validate_webhook_response(request, response, spec=spec_v31) + + def test_spec_type_invalid(self): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + spec = mock.sentinel.spec + + with pytest.raises(TypeError): + validate_webhook_response(request, response, spec=spec) + + def test_cls_type_invalid(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + + with pytest.raises(TypeError): + validate_webhook_response( + request, response, spec=spec_v31, cls=Exception + ) + + def test_spec_oas30_validator_not_found(self, spec_v30): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + + with pytest.raises(SpecError): + validate_webhook_response(request, response, spec=spec_v30) + + @mock.patch( + "openapi_core.validation.response.validators.WebhookResponseValidator." + "validate", + ) + def test_request_response(self, mock_validate, spec_v31): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + + result = validate_webhook_response(request, response, spec=spec_v31) + + assert result == mock_validate.return_value + mock_validate.assert_called_once_with(request, response) class TestValidateResponse: @@ -405,7 +764,8 @@ def test_spec_not_detected(self, spec_invalid): response = mock.Mock(spec=Response) with pytest.raises(SpecError): - validate_response(request, response, spec=spec_invalid) + with pytest.warns(DeprecationWarning): + validate_response(request, response, spec=spec_invalid) def test_request_type_invalid(self, spec_v31): request = mock.sentinel.request @@ -437,7 +797,8 @@ def test_request_response(self, mock_unmarshal, spec_v31): request = mock.Mock(spec=Request) response = mock.Mock(spec=Response) - result = validate_response(request, response, spec=spec_v31) + with pytest.warns(DeprecationWarning): + result = validate_response(request, response, spec=spec_v31) assert result == mock_unmarshal.return_value mock_unmarshal.assert_called_once_with(request, response) @@ -466,7 +827,8 @@ def test_request_response_error(self, mock_unmarshal, spec_v31): mock_unmarshal.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - validate_response(request, response, spec=spec_v31) + with pytest.warns(DeprecationWarning): + validate_response(request, response, spec=spec_v31) mock_unmarshal.assert_called_once_with(request, response) @@ -485,23 +847,65 @@ def test_validator(self, spec_v31): spec_v31, request, response, base_url=None ) - def test_cls(self, spec_v31): + def test_cls_apicall(self, spec_v31): request = mock.Mock(spec=Request) response = mock.Mock(spec=Response) - unmarshal = mock.Mock(spec=RequestUnmarshalResult) TestAPICallResp = type( "TestAPICallResp", - (MockRespClass, APICallResponseUnmarshaller), + (MockRespValidator, APICallResponseValidator), {}, ) - TestAPICallResp.setUp(unmarshal, request, response) result = validate_response( request, response, spec=spec_v31, cls=TestAPICallResp ) + assert result is None + assert TestAPICallResp.validate_calls == [ + (request, response), + ] + + def test_cls_apicall_unmarshaller(self, spec_v31): + request = mock.Mock(spec=Request) + response = mock.Mock(spec=Response) + unmarshal = mock.Mock(spec=ResponseUnmarshalResult) + TestAPICallReq = type( + "TestAPICallReq", + (MockRespUnmarshaller, APICallResponseUnmarshaller), + {}, + ) + TestAPICallReq.setUp(unmarshal) + + with pytest.warns(DeprecationWarning): + result = validate_response( + request, response, spec=spec_v31, cls=TestAPICallReq + ) + assert result == unmarshal - assert len(TestAPICallResp.unmarshal_calls) == 1 + assert TestAPICallReq.unmarshal_calls == [ + (request, response), + ] + + def test_cls_webhook_unmarshaller(self, spec_v31): + request = mock.Mock(spec=WebhookRequest) + response = mock.Mock(spec=Response) + unmarshal = mock.Mock(spec=ResponseUnmarshalResult) + TestWebhookReq = type( + "TestWebhookReq", + (MockRespUnmarshaller, WebhookResponseUnmarshaller), + {}, + ) + TestWebhookReq.setUp(unmarshal) + + with pytest.warns(DeprecationWarning): + result = validate_response( + request, response, spec=spec_v31, cls=TestWebhookReq + ) + + assert result == unmarshal + assert TestWebhookReq.unmarshal_calls == [ + (request, response), + ] def test_cls_type_invalid(self, spec_v31): request = mock.Mock(spec=Request) @@ -515,7 +919,8 @@ def test_webhook_response_validator_not_found(self, spec_v30): response = mock.Mock(spec=Response) with pytest.raises(SpecError): - validate_response(request, response, spec=spec_v30) + with pytest.warns(DeprecationWarning): + validate_response(request, response, spec=spec_v30) @mock.patch( "openapi_core.unmarshalling.response.unmarshallers.WebhookResponseUnmarshaller." @@ -525,7 +930,8 @@ def test_webhook_request(self, mock_unmarshal, spec_v31): request = mock.Mock(spec=WebhookRequest) response = mock.Mock(spec=Response) - result = validate_response(request, response, spec=spec_v31) + with pytest.warns(DeprecationWarning): + result = validate_response(request, response, spec=spec_v31) assert result == mock_unmarshal.return_value mock_unmarshal.assert_called_once_with(request, response) @@ -540,27 +946,28 @@ def test_webhook_request_error(self, mock_unmarshal, spec_v31): mock_unmarshal.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - validate_response(request, response, spec=spec_v31) + with pytest.warns(DeprecationWarning): + validate_response(request, response, spec=spec_v31) mock_unmarshal.assert_called_once_with(request, response) def test_webhook_cls(self, spec_v31): request = mock.Mock(spec=WebhookRequest) response = mock.Mock(spec=Response) - unmarshal = mock.Mock(spec=RequestUnmarshalResult) TestWebhookResp = type( "TestWebhookResp", - (MockRespClass, APICallResponseUnmarshaller), + (MockRespValidator, WebhookResponseValidator), {}, ) - TestWebhookResp.setUp(unmarshal, request, response) result = validate_response( request, response, spec=spec_v31, cls=TestWebhookResp ) - assert result == unmarshal - assert len(TestWebhookResp.unmarshal_calls) == 1 + assert result is None + assert TestWebhookResp.validate_calls == [ + (request, response), + ] def test_webhook_cls_type_invalid(self, spec_v31): request = mock.Mock(spec=WebhookRequest)