"
+ ),
+ "status": 405,
+ "title": (
+ "Operation delete not found for "
+ "http://petstore.swagger.io/v1/pets/12"
+ ),
+ }
+ ]
+ }
+ assert response.status_code == 405
+ assert response.json() == expected_data
+
+ def test_get_valid(self, client):
+ headers = {
+ "Authorization": "Basic testuser",
+ }
+ response = client.get("/v1/pets/12", headers=headers)
+
+ expected_data = {
+ "data": {
+ "id": 12,
+ "name": "Cat",
+ "ears": {
+ "healthy": True,
+ },
+ },
+ }
+ assert response.status_code == 200
+ assert response.json() == expected_data
+
+
+class TestPetPhotoEndpoint(BaseTestPetstore):
+ def test_get_valid(self, client, data_gif):
+ client.cookies.set("user", "1")
+ headers = {
+ "Authorization": "Basic testuser",
+ "Api-Key": self.api_key_encoded,
+ }
+
+ response = client.get(
+ "/v1/pets/1/photo",
+ headers=headers,
+ )
+
+ assert response.content == data_gif
+ assert response.status_code == 200
+
+ def test_post_valid(self, client, data_gif):
+ client.cookies.set("user", "1")
+ content_type = "image/gif"
+ headers = {
+ "Authorization": "Basic testuser",
+ "Api-Key": self.api_key_encoded,
+ "Content-Type": content_type,
+ }
+
+ response = client.post(
+ "/v1/pets/1/photo",
+ headers=headers,
+ content=data_gif,
+ )
+
+ assert not response.text
+ assert response.status_code == 201
diff --git a/tests/integration/contrib/flask/conftest.py b/tests/integration/contrib/flask/conftest.py
index 80e8579c..a89e729a 100644
--- a/tests/integration/contrib/flask/conftest.py
+++ b/tests/integration/contrib/flask/conftest.py
@@ -1,6 +1,5 @@
import pytest
from flask import Flask
-from jsonschema_path import SchemaPath
@pytest.fixture(scope="session")
diff --git a/tests/integration/contrib/flask/data/v3.0/flaskproject/pets/views.py b/tests/integration/contrib/flask/data/v3.0/flaskproject/pets/views.py
index 091b942e..f9b55a03 100644
--- a/tests/integration/contrib/flask/data/v3.0/flaskproject/pets/views.py
+++ b/tests/integration/contrib/flask/data/v3.0/flaskproject/pets/views.py
@@ -24,5 +24,5 @@ def get(self, petId):
return send_file(fp, mimetype="image/gif")
def post(self, petId):
- data = request.stream.read()
+ assert request.data == self.OPENID_LOGO
return Response(status=201)
diff --git a/tests/integration/contrib/flask/test_flask_decorator.py b/tests/integration/contrib/flask/test_flask_decorator.py
index cda6cd09..91637b94 100644
--- a/tests/integration/contrib/flask/test_flask_decorator.py
+++ b/tests/integration/contrib/flask/test_flask_decorator.py
@@ -1,5 +1,4 @@
import pytest
-from flask import Flask
from flask import jsonify
from flask import make_response
diff --git a/tests/integration/contrib/flask/test_flask_validator.py b/tests/integration/contrib/flask/test_flask_validator.py
index 45773c39..4e24e848 100644
--- a/tests/integration/contrib/flask/test_flask_validator.py
+++ b/tests/integration/contrib/flask/test_flask_validator.py
@@ -1,7 +1,5 @@
from json import dumps
-import pytest
-from flask import Flask
from flask.testing import FlaskClient
from flask.wrappers import Response
diff --git a/tests/integration/contrib/flask/test_flask_views.py b/tests/integration/contrib/flask/test_flask_views.py
index a1caa2c7..fa00c198 100644
--- a/tests/integration/contrib/flask/test_flask_views.py
+++ b/tests/integration/contrib/flask/test_flask_views.py
@@ -1,5 +1,4 @@
import pytest
-from flask import Flask
from flask import jsonify
from flask import make_response
diff --git a/tests/integration/contrib/requests/test_requests_validation.py b/tests/integration/contrib/requests/test_requests_validation.py
index 69aa1c34..b989ee37 100644
--- a/tests/integration/contrib/requests/test_requests_validation.py
+++ b/tests/integration/contrib/requests/test_requests_validation.py
@@ -207,7 +207,7 @@ def test_request_binary_valid(self, request_unmarshaller, data_gif):
data=data_gif,
)
request_prepared = request.prepare()
- openapi_request = RequestsOpenAPIRequest(request)
+ openapi_request = RequestsOpenAPIRequest(request_prepared)
result = request_unmarshaller.unmarshal(openapi_request)
assert not result.errors
assert result.body == data_gif
diff --git a/tests/integration/contrib/starlette/data/v3.0/starletteproject/pets/endpoints.py b/tests/integration/contrib/starlette/data/v3.0/starletteproject/pets/endpoints.py
index 1ec8e17b..b17b3029 100644
--- a/tests/integration/contrib/starlette/data/v3.0/starletteproject/pets/endpoints.py
+++ b/tests/integration/contrib/starlette/data/v3.0/starletteproject/pets/endpoints.py
@@ -4,11 +4,6 @@
from starlette.responses import Response
from starlette.responses import StreamingResponse
-from openapi_core import unmarshal_request
-from openapi_core import unmarshal_response
-from openapi_core.contrib.starlette import StarletteOpenAPIRequest
-from openapi_core.contrib.starlette import StarletteOpenAPIResponse
-
OPENID_LOGO = b64decode(
"""
R0lGODlhEAAQAMQAAO3t7eHh4srKyvz8/P5pDP9rENLS0v/28P/17tXV1dHEvPDw8M3Nzfn5+d3d
@@ -96,9 +91,10 @@ async def pet_detail_endpoint(request):
async def pet_photo_endpoint(request):
- body = await request.body()
if request.method == "GET":
contents = iter([OPENID_LOGO])
return StreamingResponse(contents, media_type="image/gif")
elif request.method == "POST":
+ body = await request.body()
+ assert body == OPENID_LOGO
return Response(status_code=201)
diff --git a/tests/integration/contrib/starlette/test_starlette_project.py b/tests/integration/contrib/starlette/test_starlette_project.py
index fc799a30..d1e8ed54 100644
--- a/tests/integration/contrib/starlette/test_starlette_project.py
+++ b/tests/integration/contrib/starlette/test_starlette_project.py
@@ -183,7 +183,7 @@ def test_post_media_type_invalid(self, client):
"title": (
"Content for the following mimetype not found: "
"text/html. "
- "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
+ "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
),
}
]
diff --git a/tests/integration/data/v3.0/parent-reference/openapi.yaml b/tests/integration/data/v3.0/parent-reference/openapi.yaml
new file mode 100644
index 00000000..51150416
--- /dev/null
+++ b/tests/integration/data/v3.0/parent-reference/openapi.yaml
@@ -0,0 +1,7 @@
+openapi: "3.0.0"
+info:
+ title: sample
+ version: "0.1"
+paths:
+ /books:
+ $ref: "./paths/books.yaml"
\ No newline at end of file
diff --git a/tests/integration/data/v3.0/parent-reference/paths/books.yaml b/tests/integration/data/v3.0/parent-reference/paths/books.yaml
new file mode 100644
index 00000000..d625f4f5
--- /dev/null
+++ b/tests/integration/data/v3.0/parent-reference/paths/books.yaml
@@ -0,0 +1,10 @@
+get:
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "../schemas/book.yaml#/Book"
\ No newline at end of file
diff --git a/tests/integration/data/v3.0/parent-reference/schemas/book.yaml b/tests/integration/data/v3.0/parent-reference/schemas/book.yaml
new file mode 100644
index 00000000..1bf35402
--- /dev/null
+++ b/tests/integration/data/v3.0/parent-reference/schemas/book.yaml
@@ -0,0 +1,9 @@
+Book:
+ type: object
+ properties:
+ id:
+ $ref: "#/BookId"
+ title:
+ type: string
+BookId:
+ type: string
\ No newline at end of file
diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml
index d26816ac..735fd96c 100644
--- a/tests/integration/data/v3.0/petstore.yaml
+++ b/tests/integration/data/v3.0/petstore.yaml
@@ -150,6 +150,9 @@ paths:
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/PetCreate'
+ multipart/form-data:
+ schema:
+ $ref: '#/components/schemas/PetWithPhotoCreate'
text/plain: {}
responses:
'201':
@@ -375,6 +378,16 @@ components:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Bird"
+ PetWithPhotoCreate:
+ type: object
+ x-model: PetWithPhotoCreate
+ allOf:
+ - $ref: "#/components/schemas/PetCreatePartOne"
+ - $ref: "#/components/schemas/PetCreatePartTwo"
+ - $ref: "#/components/schemas/PetCreatePartPhoto"
+ oneOf:
+ - $ref: "#/components/schemas/Cat"
+ - $ref: "#/components/schemas/Bird"
PetCreatePartOne:
type: object
x-model: PetCreatePartOne
@@ -395,6 +408,15 @@ components:
$ref: "#/components/schemas/Position"
healthy:
type: boolean
+ PetCreatePartPhoto:
+ type: object
+ x-model: PetCreatePartPhoto
+ properties:
+ photo:
+ $ref: "#/components/schemas/PetPhoto"
+ PetPhoto:
+ type: string
+ format: binary
Bird:
type: object
x-model: Bird
diff --git a/tests/integration/schema/test_spec.py b/tests/integration/schema/test_spec.py
index 56f14c29..d8191f3e 100644
--- a/tests/integration/schema/test_spec.py
+++ b/tests/integration/schema/test_spec.py
@@ -3,8 +3,6 @@
import pytest
from jsonschema_path import SchemaPath
-from openapi_core import V30RequestValidator
-from openapi_core import V30ResponseValidator
from openapi_core.schema.servers import get_server_url
from openapi_core.schema.specs import get_spec_url
@@ -31,14 +29,6 @@ def spec_dict(self, content_factory):
def schema_path(self, spec_dict, base_uri):
return SchemaPath.from_dict(spec_dict, base_uri=base_uri)
- @pytest.fixture
- def request_validator(self, schema_path):
- return V30RequestValidator(schema_path)
-
- @pytest.fixture
- def response_validator(self, schema_path):
- return V30ResponseValidator(schema_path)
-
def test_spec(self, schema_path, spec_dict):
url = "http://petstore.swagger.io/v1"
@@ -325,14 +315,6 @@ def schema_path(self, spec_dict, base_uri):
base_uri=base_uri,
)
- @pytest.fixture
- def request_validator(self, spec):
- return RequestValidator(spec)
-
- @pytest.fixture
- def response_validator(self, spec):
- return ResponseValidator(spec)
-
def test_spec(self, schema_path, spec_dict):
info = schema_path / "info"
info_spec = spec_dict["info"]
diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py
index cf79321f..58fbb760 100644
--- a/tests/integration/test_petstore.py
+++ b/tests/integration/test_petstore.py
@@ -14,7 +14,6 @@
from openapi_core import validate_response
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.datatypes import Parameters
-from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.deserializing.styles.exceptions import (
EmptyQueryParameterValue,
)
@@ -101,11 +100,15 @@ def test_get_pets(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -160,11 +163,15 @@ def test_get_pets_response(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -220,11 +227,15 @@ def test_get_pets_response_media_type(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -268,11 +279,15 @@ def test_get_pets_invalid_response(self, spec, response_unmarshaller):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -340,11 +355,15 @@ def test_get_pets_ids_param(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -392,11 +411,15 @@ def test_get_pets_tags_param(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -444,12 +467,16 @@ def test_get_pets_parameter_schema_error(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- with pytest.raises(ParameterValidationError) as exc_info:
- validate_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ with pytest.raises(ParameterValidationError) as exc_info:
+ validate_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert type(exc_info.value.__cause__) is InvalidSchemaValue
result = unmarshal_request(
@@ -476,12 +503,16 @@ def test_get_pets_wrong_parameter_type(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- with pytest.raises(ParameterValidationError) as exc_info:
- validate_request(
- request,
- spec=spec,
- cls=V30RequestParametersValidator,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ with pytest.raises(ParameterValidationError) as exc_info:
+ validate_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersValidator,
+ )
assert type(exc_info.value.__cause__) is CastError
result = unmarshal_request(
@@ -503,12 +534,16 @@ def test_get_pets_raises_missing_required_param(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- with pytest.raises(MissingRequiredParameter):
- validate_request(
- request,
- spec=spec,
- cls=V30RequestParametersValidator,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ with pytest.raises(MissingRequiredParameter):
+ validate_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersValidator,
+ )
result = unmarshal_request(
request, spec=spec, cls=V30RequestBodyUnmarshaller
@@ -535,12 +570,16 @@ def test_get_pets_empty_value(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- with pytest.raises(ParameterValidationError) as exc_info:
- validate_request(
- request,
- spec=spec,
- cls=V30RequestParametersValidator,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ with pytest.raises(ParameterValidationError) as exc_info:
+ validate_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersValidator,
+ )
assert type(exc_info.value.__cause__) is EmptyQueryParameterValue
result = unmarshal_request(
@@ -568,11 +607,15 @@ def test_get_pets_allow_empty_value(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -606,11 +649,15 @@ def test_get_pets_none_value(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -645,11 +692,15 @@ def test_get_pets_param_order(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert result.parameters == Parameters(
query={
@@ -689,11 +740,15 @@ def test_get_pets_param_coordinates(self, spec):
with pytest.warns(
DeprecationWarning, match="limit parameter is deprecated"
):
- result = unmarshal_request(
- request,
- spec=spec,
- cls=V30RequestParametersUnmarshaller,
- )
+ with pytest.warns(
+ DeprecationWarning,
+ match="Use of allowEmptyValue property is deprecated",
+ ):
+ result = unmarshal_request(
+ request,
+ spec=spec,
+ cls=V30RequestParametersUnmarshaller,
+ )
assert is_dataclass(result.parameters.query["coordinates"])
assert (
@@ -1326,7 +1381,6 @@ def test_get_pet_invalid_security(self, spec):
view_args = {
"petId": "1",
}
- auth = "authuser"
request = MockRequest(
host_url,
"GET",
diff --git a/tests/integration/unmarshalling/test_request_unmarshaller.py b/tests/integration/unmarshalling/test_request_unmarshaller.py
index 2993275b..0eefa3f0 100644
--- a/tests/integration/unmarshalling/test_request_unmarshaller.py
+++ b/tests/integration/unmarshalling/test_request_unmarshaller.py
@@ -201,6 +201,7 @@ def test_invalid_content_type(self, request_unmarshaller):
availableMimetypes=[
"application/json",
"application/x-www-form-urlencoded",
+ "multipart/form-data",
"text/plain",
],
)
diff --git a/tests/integration/unmarshalling/test_response_unmarshaller.py b/tests/integration/unmarshalling/test_response_unmarshaller.py
index 515696a0..3c67cf60 100644
--- a/tests/integration/unmarshalling/test_response_unmarshaller.py
+++ b/tests/integration/unmarshalling/test_response_unmarshaller.py
@@ -137,11 +137,16 @@ def test_invalid_header(self, response_unmarshaller):
"name": 1,
}
userdata_json = json.dumps(userdata)
+ cookies = {
+ "user": "123",
+ "userdata": userdata_json,
+ }
request = MockRequest(
self.host_url,
"delete",
"/v1/tags",
path_pattern="/v1/tags",
+ cookies=cookies,
)
response_json = {
"data": [
diff --git a/tests/integration/unmarshalling/test_unmarshallers.py b/tests/integration/unmarshalling/test_unmarshallers.py
index 7efb8ed9..54e944a3 100644
--- a/tests/integration/unmarshalling/test_unmarshallers.py
+++ b/tests/integration/unmarshalling/test_unmarshallers.py
@@ -355,7 +355,7 @@ def test_string_datetime_invalid(self, unmarshallers_factory):
unmarshaller.unmarshal(value)
assert len(exc_info.value.schema_errors) == 1
assert (
- f"is not a 'date-time'" in exc_info.value.schema_errors[0].message
+ "is not a 'date-time'" in exc_info.value.schema_errors[0].message
)
def test_string_password(self, unmarshallers_factory):
@@ -396,7 +396,7 @@ def test_string_uuid_invalid(self, unmarshallers_factory):
with pytest.raises(InvalidSchemaValue) as exc_info:
unmarshaller.unmarshal(value)
assert len(exc_info.value.schema_errors) == 1
- assert f"is not a 'uuid'" in exc_info.value.schema_errors[0].message
+ assert "is not a 'uuid'" in exc_info.value.schema_errors[0].message
@pytest.mark.parametrize(
"type,format,value,expected",
@@ -1840,6 +1840,25 @@ def test_object_property_nullable(self, unmarshallers_factory):
assert result == value
+ def test_subschema_nullable(self, unmarshallers_factory):
+ schema = {
+ "oneOf": [
+ {
+ "type": "integer",
+ },
+ {
+ "nullable": True,
+ },
+ ]
+ }
+ spec = SchemaPath.from_dict(schema)
+ unmarshaller = unmarshallers_factory.create(spec)
+ value = None
+
+ result = unmarshaller.unmarshal(value)
+
+ assert result is None
+
class TestOAS30RequestSchemaUnmarshallersFactory(
BaseTestOASSchemaUnmarshallersFactoryCall,
@@ -2057,6 +2076,27 @@ def test_nultiple_types_invalid(self, unmarshallers_factory, types, value):
assert len(exc_info.value.schema_errors) == 1
assert "is not of type" in exc_info.value.schema_errors[0].message
+ @pytest.mark.parametrize(
+ "types,format,value,expected",
+ [
+ (["string", "null"], "date", None, None),
+ (["string", "null"], "date", "2018-12-13", date(2018, 12, 13)),
+ ],
+ )
+ def test_multiple_types_format_valid_or_ignored(
+ self, unmarshallers_factory, types, format, value, expected
+ ):
+ schema = {
+ "type": types,
+ "format": format,
+ }
+ spec = SchemaPath.from_dict(schema)
+ unmarshaller = unmarshallers_factory.create(spec)
+
+ result = unmarshaller.unmarshal(value)
+
+ assert result == expected
+
def test_any_null(self, unmarshallers_factory):
schema = {}
spec = SchemaPath.from_dict(schema)
@@ -2065,3 +2105,22 @@ def test_any_null(self, unmarshallers_factory):
result = unmarshaller.unmarshal(None)
assert result is None
+
+ def test_subschema_null(self, unmarshallers_factory):
+ schema = {
+ "oneOf": [
+ {
+ "type": "integer",
+ },
+ {
+ "type": "null",
+ },
+ ]
+ }
+ spec = SchemaPath.from_dict(schema)
+ unmarshaller = unmarshallers_factory.create(spec)
+ value = None
+
+ result = unmarshaller.unmarshal(value)
+
+ assert result is None
diff --git a/tests/integration/validation/test_parent_reference.py b/tests/integration/validation/test_parent_reference.py
new file mode 100644
index 00000000..21e37351
--- /dev/null
+++ b/tests/integration/validation/test_parent_reference.py
@@ -0,0 +1,45 @@
+import json
+
+import pytest
+from jsonschema_path import SchemaPath
+
+from openapi_core import Config
+from openapi_core import OpenAPI
+from openapi_core import V30ResponseUnmarshaller
+from openapi_core.testing import MockRequest
+from openapi_core.testing import MockResponse
+
+
+class TestParentReference:
+
+ spec_path = "data/v3.0/parent-reference/openapi.yaml"
+
+ @pytest.fixture
+ def unmarshaller(self, content_factory):
+ content, base_uri = content_factory.from_file(self.spec_path)
+ return V30ResponseUnmarshaller(
+ spec=SchemaPath.from_dict(content, base_uri=base_uri)
+ )
+
+ @pytest.fixture
+ def openapi(self, content_factory):
+ content, base_uri = content_factory.from_file(self.spec_path)
+ spec = SchemaPath.from_dict(content, base_uri=base_uri)
+ config = Config(spec_base_uri=base_uri)
+ return OpenAPI(spec, config=config)
+
+ def test_valid(self, openapi):
+ request = MockRequest(host_url="", method="GET", path="/books")
+ response = MockResponse(
+ data=json.dumps([{"id": "BOOK:01", "title": "Test Book"}]).encode()
+ )
+
+ openapi.validate_response(request, response)
+
+ def test_unmarshal(self, unmarshaller):
+ request = MockRequest(host_url="", method="GET", path="/books")
+ response = MockResponse(
+ data=json.dumps([{"id": "BOOK:01", "title": "Test Book"}]).encode()
+ )
+
+ unmarshaller.unmarshal(request, response)
diff --git a/tests/integration/validation/test_request_validators.py b/tests/integration/validation/test_request_validators.py
index 5cae21a9..eaac8dbf 100644
--- a/tests/integration/validation/test_request_validators.py
+++ b/tests/integration/validation/test_request_validators.py
@@ -1,23 +1,14 @@
-import json
from base64 import b64encode
import pytest
from openapi_core import V30RequestValidator
-from openapi_core.datatypes import Parameters
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import OperationNotFound
from openapi_core.templating.paths.exceptions import PathNotFound
from openapi_core.templating.security.exceptions import SecurityNotFound
from openapi_core.testing import MockRequest
-from openapi_core.unmarshalling.request.unmarshallers import (
- V30RequestUnmarshaller,
-)
-from openapi_core.validation.request.exceptions import InvalidParameter
from openapi_core.validation.request.exceptions import MissingRequiredParameter
-from openapi_core.validation.request.exceptions import (
- MissingRequiredRequestBody,
-)
from openapi_core.validation.request.exceptions import (
RequestBodyValidationError,
)
@@ -115,6 +106,7 @@ def test_media_type_not_found(self, request_validator):
availableMimetypes=[
"application/json",
"application/x-www-form-urlencoded",
+ "multipart/form-data",
"text/plain",
],
)
diff --git a/tests/integration/validation/test_response_validators.py b/tests/integration/validation/test_response_validators.py
index 807aa13e..dcc1c0a3 100644
--- a/tests/integration/validation/test_response_validators.py
+++ b/tests/integration/validation/test_response_validators.py
@@ -1,5 +1,4 @@
import json
-from dataclasses import is_dataclass
import pytest
@@ -13,9 +12,6 @@
from openapi_core.templating.responses.exceptions import ResponseNotFound
from openapi_core.testing import MockRequest
from openapi_core.testing import MockResponse
-from openapi_core.unmarshalling.response.unmarshallers import (
- V30ResponseUnmarshaller,
-)
from openapi_core.validation.response.exceptions import DataValidationError
from openapi_core.validation.response.exceptions import InvalidData
from openapi_core.validation.response.exceptions import InvalidHeader
diff --git a/tests/unit/contrib/flask/test_flask_requests.py b/tests/unit/contrib/flask/test_flask_requests.py
index 63e51abf..48209cc6 100644
--- a/tests/unit/contrib/flask/test_flask_requests.py
+++ b/tests/unit/contrib/flask/test_flask_requests.py
@@ -1,5 +1,3 @@
-from urllib.parse import urljoin
-
import pytest
from werkzeug.datastructures import Headers
from werkzeug.datastructures import ImmutableMultiDict
diff --git a/tests/unit/deserializing/test_styles_deserializers.py b/tests/unit/deserializing/test_styles_deserializers.py
index 3c516143..29e52d25 100644
--- a/tests/unit/deserializing/test_styles_deserializers.py
+++ b/tests/unit/deserializing/test_styles_deserializers.py
@@ -4,9 +4,6 @@
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.deserializing.styles import style_deserializers_factory
-from openapi_core.deserializing.styles.exceptions import (
- EmptyQueryParameterValue,
-)
from openapi_core.schema.parameters import get_style_and_explode
diff --git a/tests/unit/templating/test_media_types_finders.py b/tests/unit/templating/test_media_types_finders.py
index 9580c30c..d83cc1f1 100644
--- a/tests/unit/templating/test_media_types_finders.py
+++ b/tests/unit/templating/test_media_types_finders.py
@@ -3,7 +3,6 @@
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.media_types.finders import MediaTypeFinder
-from openapi_core.testing import MockResponse
class TestMediaTypes:
@@ -22,10 +21,19 @@ def content(self, spec):
def finder(self, content):
return MediaTypeFinder(content)
- def test_charset(self, finder, content):
- mimetype = "text/html; charset=utf-8"
-
- mimetype, parameters, _ = finder.find(mimetype)
+ @pytest.mark.parametrize(
+ "media_type",
+ [
+ # equivalent according to RFC 9110
+ "text/html;charset=utf-8",
+ 'Text/HTML;Charset="utf-8"',
+ 'text/html; charset="utf-8"',
+ "text/html;charset=UTF-8",
+ "text/html ; charset=utf-8",
+ ],
+ )
+ def test_charset(self, finder, content, media_type):
+ mimetype, parameters, _ = finder.find(media_type)
assert mimetype == "text/*"
assert parameters == {"charset": "utf-8"}
diff --git a/tests/unit/templating/test_paths_finders.py b/tests/unit/templating/test_paths_finders.py
index cb0821ee..63505a48 100644
--- a/tests/unit/templating/test_paths_finders.py
+++ b/tests/unit/templating/test_paths_finders.py
@@ -7,7 +7,6 @@
from openapi_core.templating.paths.exceptions import PathsNotFound
from openapi_core.templating.paths.exceptions import ServerNotFound
from openapi_core.templating.paths.finders import APICallPathFinder
-from openapi_core.testing import MockRequest
class BaseTestSimpleServer:
@@ -171,19 +170,45 @@ def spec(self, info, paths):
class BaseTestServerNotFound:
@pytest.fixture
def servers(self):
- return []
+ return [
+ SchemaPath.from_dict(
+ {"url": "http://petstore.swagger.io/resource"}
+ )
+ ]
- @pytest.mark.xfail(
- reason="returns default server",
- )
def test_raises(self, finder):
method = "get"
- full_url = "http://petstore.swagger.io/resource"
+ full_url = "http://invalidserver/resource"
with pytest.raises(ServerNotFound):
finder.find(method, full_url)
+class BaseTestDefaultServer:
+ @pytest.fixture
+ def servers(self):
+ return []
+
+ def test_returns_default_server(self, finder, spec):
+ method = "get"
+ full_url = "http://petstore.swagger.io/resource"
+
+ result = finder.find(method, full_url)
+
+ path = spec / "paths" / self.path_name
+ operation = spec / "paths" / self.path_name / method
+ server = SchemaPath.from_dict({"url": "/"})
+ path_result = TemplateResult(self.path_name, {})
+ server_result = TemplateResult("/", {})
+ assert result == (
+ path,
+ operation,
+ server,
+ path_result,
+ server_result,
+ )
+
+
class BaseTestOperationNotFound:
@pytest.fixture
def operations(self):
@@ -291,6 +316,15 @@ def test_raises(self, finder):
finder.find(method, full_url)
+class TestSpecSimpleServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestSpecServer,
+ BaseTestSimplePath,
+ BaseTestSimpleServer,
+):
+ pass
+
+
class TestSpecSimpleServerServerNotFound(
BaseTestServerNotFound,
BaseTestSpecServer,
@@ -326,6 +360,15 @@ class TestSpecSimpleServerPathsNotFound(
pass
+class TestOperationSimpleServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestOperationServer,
+ BaseTestSimplePath,
+ BaseTestSimpleServer,
+):
+ pass
+
+
class TestOperationSimpleServerServerNotFound(
BaseTestServerNotFound,
BaseTestOperationServer,
@@ -361,6 +404,15 @@ class TestOperationSimpleServerPathsNotFound(
pass
+class TestPathSimpleServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestPathServer,
+ BaseTestSimplePath,
+ BaseTestSimpleServer,
+):
+ pass
+
+
class TestPathSimpleServerServerNotFound(
BaseTestServerNotFound,
BaseTestPathServer,
@@ -444,6 +496,15 @@ class TestPathSimpleServerVariablePathValid(
pass
+class TestSpecVariableServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestSpecServer,
+ BaseTestSimplePath,
+ BaseTestVariableServer,
+):
+ pass
+
+
class TestSpecVariableServerServerNotFound(
BaseTestServerNotFound,
BaseTestSpecServer,
@@ -479,6 +540,15 @@ class TestSpecVariableServerPathsNotFound(
pass
+class TestOperationVariableServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestOperationServer,
+ BaseTestSimplePath,
+ BaseTestVariableServer,
+):
+ pass
+
+
class TestOperationVariableServerServerNotFound(
BaseTestServerNotFound,
BaseTestOperationServer,
@@ -514,6 +584,15 @@ class TestOperationVariableServerPathsNotFound(
pass
+class TestPathVariableServerDefaultServer(
+ BaseTestDefaultServer,
+ BaseTestPathServer,
+ BaseTestSimplePath,
+ BaseTestVariableServer,
+):
+ pass
+
+
class TestPathVariableServerServerNotFound(
BaseTestServerNotFound,
BaseTestPathServer,
diff --git a/tests/unit/templating/test_templating_util.py b/tests/unit/templating/test_templating_util.py
index b268e4f0..815f6cb0 100644
--- a/tests/unit/templating/test_templating_util.py
+++ b/tests/unit/templating/test_templating_util.py
@@ -29,6 +29,7 @@ def test_exact(self):
[
("/{test_id}/test", {"test_id": "test"}),
("/{test.id}/test", {"test.id": "test"}),
+ ("/{test-id}/test", {"test-id": "test"}),
],
)
def test_chars_valid(self, path_pattern, expected):
@@ -49,7 +50,6 @@ def test_chars_valid(self, path_pattern, expected):
"path_pattern,expected",
[
("/{test~id}/test", {"test~id": "test"}),
- ("/{test-id}/test", {"test-id": "test"}),
],
)
def test_special_chars_valid(self, path_pattern, expected):
diff --git a/tests/unit/test_shortcuts.py b/tests/unit/test_shortcuts.py
index 963c4658..9a3f36c9 100644
--- a/tests/unit/test_shortcuts.py
+++ b/tests/unit/test_shortcuts.py
@@ -423,8 +423,7 @@ def test_request_response_error(self, mock_unmarshal, spec_v31):
mock_unmarshal.return_value = ResultMock(error_to_raise=ValueError)
with pytest.raises(ValueError):
- with pytest.warns(DeprecationWarning):
- unmarshal_response(request, response, spec=spec_v31)
+ unmarshal_response(request, response, spec=spec_v31)
mock_unmarshal.assert_called_once_with(request, response)
@@ -593,19 +592,17 @@ def test_request(self, mock_validate, spec_v31):
class TestValidateRequest:
- def test_spec_not_detected(self, spec_invalid):
+ def test_spec_invalid(self, spec_invalid):
request = mock.Mock(spec=Request)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_request(request, spec=spec_invalid)
+ validate_request(request, spec=spec_invalid)
def test_spec_not_detected(self, spec_v20):
request = mock.Mock(spec=Request)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_request(request, spec=spec_v20)
+ validate_request(request, spec=spec_v20)
def test_request_type_invalid(self, spec_v31):
request = mock.sentinel.request
@@ -733,8 +730,7 @@ def test_webhook_request_validator_not_found(self, spec_v30):
request = mock.Mock(spec=WebhookRequest)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_request(request, spec=spec_v30)
+ validate_request(request, spec=spec_v30)
@mock.patch(
"openapi_core.validation.request.validators.V31WebhookRequestValidator."
@@ -889,16 +885,14 @@ def test_spec_not_detected(self, spec_invalid):
response = mock.Mock(spec=Response)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_response(request, response, spec=spec_invalid)
+ validate_response(request, response, spec=spec_invalid)
def test_spec_not_supported(self, spec_v20):
request = mock.Mock(spec=Request)
response = mock.Mock(spec=Response)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_response(request, response, spec=spec_v20)
+ validate_response(request, response, spec=spec_v20)
def test_request_type_invalid(self, spec_v31):
request = mock.sentinel.request
@@ -965,8 +959,7 @@ def test_webhook_response_validator_not_found(self, spec_v30):
response = mock.Mock(spec=Response)
with pytest.raises(SpecError):
- with pytest.warns(DeprecationWarning):
- validate_response(request, response, spec=spec_v30)
+ validate_response(request, response, spec=spec_v30)
@mock.patch(
"openapi_core.validation.response.validators.V31WebhookResponseValidator."
diff --git a/tests/unit/unmarshalling/test_request_unmarshallers.py b/tests/unit/unmarshalling/test_request_unmarshallers.py
new file mode 100644
index 00000000..a407d567
--- /dev/null
+++ b/tests/unit/unmarshalling/test_request_unmarshallers.py
@@ -0,0 +1,136 @@
+import enum
+
+import pytest
+from jsonschema_path import SchemaPath
+
+from openapi_core import V30RequestUnmarshaller
+from openapi_core import 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))
diff --git a/tests/unit/unmarshalling/test_schema_unmarshallers.py b/tests/unit/unmarshalling/test_schema_unmarshallers.py
index 3373a34f..5a8fe12e 100644
--- a/tests/unit/unmarshalling/test_schema_unmarshallers.py
+++ b/tests/unit/unmarshalling/test_schema_unmarshallers.py
@@ -8,7 +8,6 @@
from openapi_core.unmarshalling.schemas.exceptions import (
FormatterNotFoundError,
)
-from openapi_core.unmarshalling.schemas.exceptions import FormatUnmarshalError
from openapi_core.unmarshalling.schemas.factories import (
SchemaUnmarshallersFactory,
)
@@ -102,8 +101,9 @@ def custom_format_unmarshaller(value):
extra_format_unmarshallers=extra_format_unmarshallers,
)
- with pytest.raises(FormatUnmarshalError):
- unmarshaller.unmarshal(value)
+ result = unmarshaller.unmarshal(value)
+
+ assert result == value
def test_schema_extra_format_unmarshaller_format_custom(
self, schema_unmarshaller_factory
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy