Skip to content

Commit 6e7dd39

Browse files
Return NoContent when mocking HTTP 204 operations (#2000)
There's never a need to return a response for HTTP 204 - and these code are unlikely to have any "example" response documented in the spec. Avoids a uvicorn RuntimeError `"Response content longer than Content-Length"` when mocking a (3.0) spec containing: ``` ... responses: '204': description: No Content '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ProblemDetails' ... ```
1 parent 087f05f commit 6e7dd39

File tree

4 files changed

+55
-1
lines changed

4 files changed

+55
-1
lines changed

connexion/operations/openapi.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
import logging
66
import typing as t
7+
from http import HTTPStatus
78

8-
from connexion.datastructures import MediaTypeDict
9+
from connexion.datastructures import MediaTypeDict, NoContent
910
from connexion.operations.abstract import AbstractOperation
1011
from connexion.uri_parsing import OpenAPIURIParser
1112
from connexion.utils import build_example_from_schema, deep_get
@@ -170,6 +171,10 @@ def example_response(self, status_code=None, content_type=None):
170171
status_code = int(status_code)
171172
except ValueError:
172173
status_code = 200
174+
175+
if status_code == HTTPStatus.NO_CONTENT:
176+
return NoContent, status_code
177+
173178
try:
174179
# TODO also use example header?
175180
return (

connexion/operations/swagger2.py

+6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
import logging
66
import typing as t
7+
from http import HTTPStatus
78

9+
from connexion.datastructures import NoContent
810
from connexion.exceptions import InvalidSpecification
911
from connexion.operations.abstract import AbstractOperation
1012
from connexion.uri_parsing import Swagger2URIParser
@@ -196,6 +198,10 @@ def example_response(self, status_code=None, *args, **kwargs):
196198
status_code = int(status_code)
197199
except ValueError:
198200
status_code = 200
201+
202+
if status_code == HTTPStatus.NO_CONTENT:
203+
return NoContent, status_code
204+
199205
try:
200206
return (
201207
list(deep_get(self._responses, examples_path).values())[0],

tests/test_mock.py

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from connexion.datastructures import NoContent
12
from connexion.mock import MockResolver
23
from connexion.operations import OpenAPIOperation, Swagger2Operation
34

@@ -261,6 +262,28 @@ def test_mock_resolver_no_example_nested_in_list_openapi():
261262
assert all(isinstance(c, str) for c in response)
262263

263264

265+
def test_mock_resolver_no_content():
266+
resolver = MockResolver(mock_all=True)
267+
268+
responses = {"204": {}}
269+
270+
operation = Swagger2Operation(
271+
method="GET",
272+
path="endpoint",
273+
path_parameters=[],
274+
operation={"responses": responses},
275+
app_produces=["application/json"],
276+
app_consumes=["application/json"],
277+
definitions={},
278+
resolver=resolver,
279+
)
280+
assert operation.operation_id == "mock-1"
281+
282+
response, status_code = resolver.mock_operation(operation)
283+
assert status_code == 204
284+
assert response == NoContent
285+
286+
264287
def test_mock_resolver_no_examples():
265288
resolver = MockResolver(mock_all=True)
266289

tests/test_mock3.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from connexion.datastructures import NoContent
12
from connexion.mock import MockResolver
23
from connexion.operations import OpenAPIOperation
34

@@ -87,6 +88,25 @@ def test_mock_resolver_inline_schema_example():
8788
assert response == {"foo": "bar"}
8889

8990

91+
def test_mock_resolver_no_content():
92+
resolver = MockResolver(mock_all=True)
93+
94+
responses = {"204": {}}
95+
96+
operation = OpenAPIOperation(
97+
method="GET",
98+
path="endpoint",
99+
path_parameters=[],
100+
operation={"responses": responses},
101+
resolver=resolver,
102+
)
103+
assert operation.operation_id == "mock-1"
104+
105+
response, status_code = resolver.mock_operation(operation)
106+
assert status_code == 204
107+
assert response == NoContent
108+
109+
90110
def test_mock_resolver_no_examples():
91111
resolver = MockResolver(mock_all=True)
92112

0 commit comments

Comments
 (0)