From ef1f03c0ac68e1197055e5cab729c4e5b0d0f89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Fri, 29 Nov 2024 13:06:17 +0100 Subject: [PATCH] v20 - glue empty responses are allowed --- aiopenapi3/v20/glue.py | 6 +++- tests/conftest.py | 5 +++ .../paths-response-content-empty-v20.yaml | 33 +++++++++++++++++++ .../paths-response-content-empty.yaml | 28 ++++++++++++++++ tests/path_test.py | 12 +++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/paths-response-content-empty-v20.yaml create mode 100644 tests/fixtures/paths-response-content-empty.yaml diff --git a/aiopenapi3/v20/glue.py b/aiopenapi3/v20/glue.py index 5dd8b97f..6ae1798d 100644 --- a/aiopenapi3/v20/glue.py +++ b/aiopenapi3/v20/glue.py @@ -295,7 +295,7 @@ def _process_stream(self, result: httpx.Response) -> tuple["ResponseHeadersType" return headers, expected_response.schema_ def _process_request(self, result: httpx.Response) -> tuple["ResponseHeadersType", "ResponseDataType"]: - rheaders: "ResponseHeadersType" = dict() + rheaders: "ResponseHeadersType" # spec enforces these are strings status_code = str(result.status_code) content_type = result.headers.get("Content-Type", None) @@ -316,6 +316,10 @@ def _process_request(self, result: httpx.Response) -> tuple["ResponseHeadersType rheaders = self._process__headers(result, headers, expected_response) + if expected_response.schema_ is None: + """Swagger treats no schema as a response without a body.""" + return rheaders, None + if status_code == "204": return rheaders, None diff --git a/tests/conftest.py b/tests/conftest.py index 239b1077..7502fe9f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -272,6 +272,11 @@ def with_paths_response_header(openapi_version): yield _get_parsed_yaml("paths-response-header.yaml", openapi_version) +@pytest.fixture(params=["", "-v20"], ids=["v3x", "v20"]) +def with_paths_response_content_empty_vXX(request): + return _get_parsed_yaml(f"paths-response-content-empty{request.param}.yaml") + + @pytest.fixture def with_paths_response_content_type_octet(openapi_version): yield _get_parsed_yaml("paths-response-content-type-octet.yaml", openapi_version) diff --git a/tests/fixtures/paths-response-content-empty-v20.yaml b/tests/fixtures/paths-response-content-empty-v20.yaml new file mode 100644 index 00000000..f453cdd3 --- /dev/null +++ b/tests/fixtures/paths-response-content-empty-v20.yaml @@ -0,0 +1,33 @@ +swagger: "2.0" +info: + title: with empty response + description: with empty response + version: 1.0.0 +host: api.example.com +basePath: /v1 +schemes: + - https + +consumes: + - application/json +produces: + - application/json + +definitions: {} + +paths: + /empty: + get: + operationId: empty + responses: + "200": + description: OK + /headers: + get: + operationId: headers + responses: + "200": + description: OK + headers: + X-required: + type: string diff --git a/tests/fixtures/paths-response-content-empty.yaml b/tests/fixtures/paths-response-content-empty.yaml new file mode 100644 index 00000000..7c5e9224 --- /dev/null +++ b/tests/fixtures/paths-response-content-empty.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: 'with empty response' + version: 0.0.0 +servers: + - url: http://127.0.0.1/api + +security: + - {} + +paths: + /empty: + get: + operationId: empty + responses: + "200": + description: "ok" + /headers: + get: + operationId: headers + responses: + "200": + description: "ok" + headers: + X-required: + schema: + type: string + required: true diff --git a/tests/path_test.py b/tests/path_test.py index 1d764658..ad86bde0 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -427,6 +427,18 @@ def test_paths_response_header(httpx_mock, with_paths_response_header): return +@pytest.mark.httpx_mock(can_send_already_matched_responses=True) +def test_paths_response_content_empty(httpx_mock, with_paths_response_content_empty_vXX): + httpx_mock.add_response(status_code=200) + api = OpenAPI(URLBASE, with_paths_response_content_empty_vXX, session_factory=httpx.Client) + h, b = api._.empty(return_headers=True) + assert b is None and h == {} + + httpx_mock.add_response(status_code=200, headers={"X-required": "1"}) + h, b = api._.headers(return_headers=True) + assert b is None and h["X-required"] == "1" + + @pytest.mark.httpx_mock(can_send_already_matched_responses=True) def test_paths_response_content_type_octet(httpx_mock, with_paths_response_content_type_octet): CONTENT = b"\x00\x11"