From fe55453676ad95dbba61ab41d56ef3e05a68805d Mon Sep 17 00:00:00 2001 From: Sergii Kozak Date: Sat, 2 Mar 2024 16:00:24 +0100 Subject: [PATCH 1/2] - invalid usage of super() when having multi-baseclass inheritance: use direct basecls.__init__() to avoid nasty side effect of calling 'wrong' base class __init__(). Particularly in this case, openapi_core.validation.request.BaseRequestUnmarshaller got 'extra_format_unmarshallers' property reset to None after BaseRequestValidator.__init__() call --- openapi_core/unmarshalling/unmarshallers.py | 3 +- openapi_core/validation/request/validators.py | 4 +- .../test_request_unmarshallers.py | 128 ++++++++++++++++++ 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 tests/unit/unmarshalling/test_request_unmarshallers.py diff --git a/openapi_core/unmarshalling/unmarshallers.py b/openapi_core/unmarshalling/unmarshallers.py index ddc8b891..984b9ea1 100644 --- a/openapi_core/unmarshalling/unmarshallers.py +++ b/openapi_core/unmarshalling/unmarshallers.py @@ -60,7 +60,8 @@ def __init__( schema_validators_factory = ( schema_unmarshallers_factory.schema_validators_factory ) - super().__init__( + BaseValidator.__init__( + self, spec, base_url=base_url, style_deserializers_factory=style_deserializers_factory, diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index 34e23ecd..f2e1ae95 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -84,7 +84,9 @@ def __init__( ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, ): - super().__init__( + + BaseValidator.__init__( + self, spec, base_url=base_url, style_deserializers_factory=style_deserializers_factory, diff --git a/tests/unit/unmarshalling/test_request_unmarshallers.py b/tests/unit/unmarshalling/test_request_unmarshallers.py new file mode 100644 index 00000000..a9696943 --- /dev/null +++ b/tests/unit/unmarshalling/test_request_unmarshallers.py @@ -0,0 +1,128 @@ +import enum +import pytest +from jsonschema_path import SchemaPath + +from openapi_core import V30RequestUnmarshaller, V31RequestUnmarshaller +from openapi_core.datatypes import Parameters +from openapi_core.testing import MockRequest + + +class Colors(enum.Enum): + + YELLOW = "yellow" + BLUE = "blue" + RED = "red" + + @classmethod + def of(cls, v: str): + for it in cls: + if it.value == v: + return it + raise ValueError(f"Invalid value: {v}") + + +class TestRequestUnmarshaller: + + @pytest.fixture(scope="session") + def spec_dict(self): + return { + "openapi": "3.1.0", + "info": { + "title": "Test request body unmarshaller", + "version": "0.1", + }, + "paths": { + "/resources": { + "post": { + "description": "POST resources test request", + "requestBody": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createResource" + } + } + } + }, + "responses": { + "201": { + "description": "Resource was created." + } + } + }, + "get": { + "description": "POST resources test request", + "parameters": [ + { + "name": "color", + "in": "query", + "required": False, + "schema": { + "$ref": "#/components/schemas/colors" + }, + }, + ], + "responses": { + "default": { + "description": "Returned resources matching request." + } + } + } + } + }, + "components": { + "schemas": { + "colors": { + "type": "string", + "enum": ["yellow", "blue", "red"], + "format": "enum_Colors" + }, + "createResource": { + "type": "object", + "properties": { + "resId": { + "type": "integer" + }, + "color": { + "$ref": "#/components/schemas/colors" + } + }, + "required": ["resId", "color"] + } + } + } + } + + @pytest.fixture(scope="session") + def spec(self, spec_dict): + return SchemaPath.from_dict(spec_dict) + + @pytest.mark.parametrize("req_unmarshaller_cls", [V30RequestUnmarshaller, V31RequestUnmarshaller]) + def test_request_body_extra_unmarshaller(self, spec, req_unmarshaller_cls): + ru = req_unmarshaller_cls(spec=spec, extra_format_unmarshallers={ + "enum_Colors": Colors.of + }) + request = MockRequest(host_url="http://example.com", + method="post", + path="/resources", + data=b'{"resId": 23498572, "color": "blue"}') + result = ru.unmarshal(request) + + assert not result.errors + assert result.body == {"resId": 23498572, "color": Colors.BLUE} + assert result.parameters == Parameters() + + @pytest.mark.parametrize("req_unmarshaller_cls", [V30RequestUnmarshaller, V31RequestUnmarshaller]) + def test_request_param_extra_unmarshaller(self, spec, req_unmarshaller_cls): + ru = req_unmarshaller_cls(spec=spec, extra_format_unmarshallers={ + "enum_Colors": Colors.of + }) + request = MockRequest(host_url="http://example.com", + method="get", + path="/resources", + args={"color": "blue"}) + result = ru.unmarshal(request) + + assert not result.errors + assert result.parameters == Parameters(query=dict(color=Colors.BLUE)) From 0a0565dc06273ed427801529ab737c31263f2cd4 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Tue, 5 Mar 2024 22:26:40 +0000 Subject: [PATCH 2/2] Request unmarshaller test formatting fix --- .../test_request_unmarshallers.py | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/tests/unit/unmarshalling/test_request_unmarshallers.py b/tests/unit/unmarshalling/test_request_unmarshallers.py index a9696943..a407d567 100644 --- a/tests/unit/unmarshalling/test_request_unmarshallers.py +++ b/tests/unit/unmarshalling/test_request_unmarshallers.py @@ -1,8 +1,10 @@ import enum + import pytest from jsonschema_path import SchemaPath -from openapi_core import V30RequestUnmarshaller, V31RequestUnmarshaller +from openapi_core import V30RequestUnmarshaller +from openapi_core import V31RequestUnmarshaller from openapi_core.datatypes import Parameters from openapi_core.testing import MockRequest @@ -43,13 +45,11 @@ def spec_dict(self): "$ref": "#/components/schemas/createResource" } } - } + }, }, "responses": { - "201": { - "description": "Resource was created." - } - } + "201": {"description": "Resource was created."} + }, }, "get": { "description": "POST resources test request", @@ -67,8 +67,8 @@ def spec_dict(self): "default": { "description": "Returned resources matching request." } - } - } + }, + }, } }, "components": { @@ -76,52 +76,60 @@ def spec_dict(self): "colors": { "type": "string", "enum": ["yellow", "blue", "red"], - "format": "enum_Colors" + "format": "enum_Colors", }, "createResource": { "type": "object", "properties": { - "resId": { - "type": "integer" - }, - "color": { - "$ref": "#/components/schemas/colors" - } + "resId": {"type": "integer"}, + "color": {"$ref": "#/components/schemas/colors"}, }, - "required": ["resId", "color"] - } + "required": ["resId", "color"], + }, } - } + }, } @pytest.fixture(scope="session") def spec(self, spec_dict): return SchemaPath.from_dict(spec_dict) - @pytest.mark.parametrize("req_unmarshaller_cls", [V30RequestUnmarshaller, V31RequestUnmarshaller]) + @pytest.mark.parametrize( + "req_unmarshaller_cls", + [V30RequestUnmarshaller, V31RequestUnmarshaller], + ) def test_request_body_extra_unmarshaller(self, spec, req_unmarshaller_cls): - ru = req_unmarshaller_cls(spec=spec, extra_format_unmarshallers={ - "enum_Colors": Colors.of - }) - request = MockRequest(host_url="http://example.com", - method="post", - path="/resources", - data=b'{"resId": 23498572, "color": "blue"}') + ru = req_unmarshaller_cls( + spec=spec, extra_format_unmarshallers={"enum_Colors": Colors.of} + ) + request = MockRequest( + host_url="http://example.com", + method="post", + path="/resources", + data=b'{"resId": 23498572, "color": "blue"}', + ) result = ru.unmarshal(request) assert not result.errors assert result.body == {"resId": 23498572, "color": Colors.BLUE} assert result.parameters == Parameters() - @pytest.mark.parametrize("req_unmarshaller_cls", [V30RequestUnmarshaller, V31RequestUnmarshaller]) - def test_request_param_extra_unmarshaller(self, spec, req_unmarshaller_cls): - ru = req_unmarshaller_cls(spec=spec, extra_format_unmarshallers={ - "enum_Colors": Colors.of - }) - request = MockRequest(host_url="http://example.com", - method="get", - path="/resources", - args={"color": "blue"}) + @pytest.mark.parametrize( + "req_unmarshaller_cls", + [V30RequestUnmarshaller, V31RequestUnmarshaller], + ) + def test_request_param_extra_unmarshaller( + self, spec, req_unmarshaller_cls + ): + ru = req_unmarshaller_cls( + spec=spec, extra_format_unmarshallers={"enum_Colors": Colors.of} + ) + request = MockRequest( + host_url="http://example.com", + method="get", + path="/resources", + args={"color": "blue"}, + ) result = ru.unmarshal(request) assert not result.errors