Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(testing): add msgpack support #2394

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9e9e3cd
tried to add support for messagepack
arthurprioli Oct 18, 2024
8deb751
added documentation
arthurprioli Oct 20, 2024
97a5a2a
fixed ruff formatting
arthurprioli Oct 20, 2024
58e251d
Merge branch 'master' into 1026-add-messsagepack-support
arthurprioli Oct 28, 2024
2cea4f6
tests added
arthurprioli Oct 30, 2024
f911793
inserted changes asked by code review
arthurprioli Oct 30, 2024
aa6edf2
fixed overidented docstrings
arthurprioli Oct 30, 2024
37d702e
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Nov 6, 2024
4db15eb
Merge branch 'master' into 1026-add-messsagepack-support
arthurprioli Nov 10, 2024
918ba30
added more tests, fixed documentation and wrote better client.py
arthurprioli Nov 20, 2024
ef7a39d
Merge branch '1026-add-messsagepack-support' of https://github.com/ar…
arthurprioli Nov 20, 2024
f226b5b
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Nov 22, 2024
f562383
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Dec 30, 2024
41965f6
removed diffs from newsfragment
arthurprioli Jan 3, 2025
eab724a
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Jan 4, 2025
9a4aded
added new test function for msgpack parameters and fixed small bugs
arthurprioli Jan 7, 2025
03bb7c0
added and rendered towncrier docs
arthurprioli Jan 7, 2025
8fdf4aa
added tests on content body
arthurprioli Jan 8, 2025
bdb36e2
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Feb 8, 2025
0f5bb82
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Mar 14, 2025
87e8e2d
Merge branch 'master' into 1026-add-messsagepack-support
vytas7 Mar 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/_newsfragments/1026.newandimproved.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :func:`~falcon.testing.simulate_request` now suports ``msgpack``
and returns Content-Type as ``MEDIA_MSGPACK`` in a similar way that
was made to JSON parameters.
50 changes: 50 additions & 0 deletions falcon/testing/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@
from falcon.asgi_spec import ScopeType
from falcon.constants import COMBINED_METHODS
from falcon.constants import MEDIA_JSON
from falcon.constants import MEDIA_MSGPACK
from falcon.errors import CompatibilityError
from falcon.media import MessagePackHandler
from falcon.testing import helpers
from falcon.testing.srmock import StartResponseMock
from falcon.typing import Headers
Expand Down Expand Up @@ -455,6 +457,7 @@
content_type: Optional[str] = None,
body: Optional[Union[str, bytes]] = None,
json: Optional[Any] = None,
msgpack: Optional[Any] = None,
file_wrapper: Optional[Callable[..., Any]] = None,
wsgierrors: Optional[TextIO] = None,
params: Optional[Mapping[str, Any]] = None,
Expand Down Expand Up @@ -592,6 +595,7 @@
content_type=content_type,
body=body,
json=json,
msgpack=msgpack,
params=params,
params_csv=params_csv,
protocol=protocol,
Expand All @@ -615,6 +619,7 @@
headers,
body,
json,
msgpack,
extras,
)

Expand Down Expand Up @@ -667,6 +672,7 @@
content_type: Optional[str] = ...,
body: Optional[Union[str, bytes]] = ...,
json: Optional[Any] = ...,
msgpack: Optional[Any] = ...,
params: Optional[Mapping[str, Any]] = ...,
params_csv: bool = ...,
protocol: str = ...,
Expand Down Expand Up @@ -694,6 +700,7 @@
content_type: Optional[str] = ...,
body: Optional[Union[str, bytes]] = ...,
json: Optional[Any] = ...,
msgpack: Optional[Any] = ...,
params: Optional[Mapping[str, Any]] = ...,
params_csv: bool = ...,
protocol: str = ...,
Expand Down Expand Up @@ -724,6 +731,7 @@
content_type: Optional[str] = None,
body: Optional[Union[str, bytes]] = None,
json: Optional[Any] = None,
msgpack: Optional[Any] = None,
params: Optional[Mapping[str, Any]] = None,
params_csv: bool = False,
protocol: str = 'http',
Expand Down Expand Up @@ -808,6 +816,13 @@
overrides `body` and sets the Content-Type header to
``'application/json'``, overriding any value specified by either
the `content_type` or `headers` arguments.
msgpack(Msgpack serializable): A Msgpack document to serialize as the
body of the request (default: ``None``). If specified,
overrides `body` and sets the Content-Type header to
``'application/msgpack'``, overriding any value specified by
either the `content_type` or `headers` arguments. If msgpack and json
are both specified, the Content-Type header will be set as `
`'application/msgpack'``.
host(str): A string to use for the hostname part of the fully
qualified request URL (default: 'falconframework.org')
remote_addr (str): A string to use as the remote IP address for the
Expand Down Expand Up @@ -846,6 +861,7 @@
headers,
body,
json,
msgpack,
extras,
)

Expand Down Expand Up @@ -1551,6 +1567,13 @@
overrides `body` and sets the Content-Type header to
``'application/json'``, overriding any value specified by either
the `content_type` or `headers` arguments.
msgpack(Msgpack serializable): A Msgpack document to serialize as the
body of the request (default: ``None``). If specified,
overrides `body` and sets the Content-Type header to
``'application/msgpack'``, overriding any value specified by
either the `content_type` or `headers` arguments. If msgpack and json
are both specified, the Content-Type header will be set as
``'application/msgpack'``.
file_wrapper (callable): Callable that returns an iterable,
to be used as the value for *wsgi.file_wrapper* in the
WSGI environ (default: ``None``). This can be used to test
Expand Down Expand Up @@ -1662,6 +1685,13 @@
overrides `body` and sets the Content-Type header to
``'application/json'``, overriding any value specified by either
the `content_type` or `headers` arguments.
msgpack(Msgpack serializable): A Msgpack document to serialize as the
body of the request (default: ``None``). If specified,
overrides `body` and sets the Content-Type header to
``'application/msgpack'``, overriding any value specified by
either the `content_type` or `headers` arguments. If msgpack and json
are both specified, the Content-Type header will be set as
``'application/msgpack'``.
file_wrapper (callable): Callable that returns an iterable,
to be used as the value for *wsgi.file_wrapper* in the
WSGI environ (default: ``None``). This can be used to test
Expand Down Expand Up @@ -1862,6 +1892,13 @@
overrides `body` and sets the Content-Type header to
``'application/json'``, overriding any value specified by either
the `content_type` or `headers` arguments.
msgpack(Msgpack serializable): A Msgpack document to serialize as the
body of the request (default: ``None``). If specified,
overrides `body` and sets the Content-Type header to
``'application/msgpack'``, overriding any value specified by
either the `content_type` or `headers` arguments. If msgpack and json
are both specified, the Content-Type header will be set as
``'application/msgpack'``.
host(str): A string to use for the hostname part of the fully
qualified request URL (default: 'falconframework.org')
remote_addr (str): A string to use as the remote IP address for the
Expand Down Expand Up @@ -1968,6 +2005,13 @@
overrides `body` and sets the Content-Type header to
``'application/json'``, overriding any value specified by either
the `content_type` or `headers` arguments.
msgpack(Msgpack serializable): A Msgpack document to serialize as the
body of the request (default: ``None``). If specified,
overrides `body` and sets the Content-Type header to
``'application/msgpack'``, overriding any value specified by
either the `content_type` or `headers` arguments. If msgpack and json
are both specified, the Content-Type header will be set as
``'application/msgpack'``.
host(str): A string to use for the hostname part of the fully
qualified request URL (default: 'falconframework.org')
remote_addr (str): A string to use as the remote IP address for the
Expand Down Expand Up @@ -2248,6 +2292,7 @@
headers: Optional[HeaderArg],
body: Optional[Union[str, bytes]],
json: Optional[Any],
msgpack: Optional[Any],
extras: Optional[Mapping[str, Any]],
) -> Tuple[
str, str, Optional[HeaderArg], Optional[Union[str, bytes]], Mapping[str, Any]
Expand Down Expand Up @@ -2284,6 +2329,11 @@
headers = dict(headers or {})
headers['Content-Type'] = MEDIA_JSON

if msgpack is not None:
body = MessagePackHandler().serialize(content_type=None, media=msgpack)
headers = dict(headers or {})
headers['Content-Type'] = MEDIA_MSGPACK

Check warning on line 2335 in falcon/testing/client.py

View check run for this annotation

Codecov / codecov/patch

falcon/testing/client.py#L2333-L2335

Added lines #L2333 - L2335 were not covered by tests

return path, query_string, headers, body, extras


Expand Down
89 changes: 89 additions & 0 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
from falcon import testing
from falcon.util.sync import async_to_sync

try:
import msgpack
except ImportError:
msgpack = None

SAMPLE_BODY = testing.rand_string(0, 128 * 1024)


class CustomCookies:
def items(self):
Expand Down Expand Up @@ -104,6 +111,88 @@ def on_post(self, req, resp):
assert result.text == falcon.MEDIA_JSON


@pytest.mark.skipif(not msgpack, reason='msgpack not installed')
@pytest.mark.parametrize(
'json,msgpack,response',
[
({}, None, falcon.MEDIA_JSON),
(None, {}, falcon.MEDIA_MSGPACK),
({}, {}, falcon.MEDIA_MSGPACK),
],
)
def test_simulate_request_msgpack_content_type(json, msgpack, response):
class Foo:
def on_post(self, req, resp):
resp.text = req.content_type

app = App()
app.add_route('/', Foo())

headers = {'Content-Type': falcon.MEDIA_TEXT}

result = testing.simulate_post(app, '/', json=json, msgpack=msgpack)
assert result.text == response

result = testing.simulate_post(
app, '/', json=json, msgpack=msgpack, content_type=falcon.MEDIA_HTML
)
assert result.text == response

result = testing.simulate_post(
app, '/', json=json, msgpack=msgpack, headers=headers
)
assert result.text == response

result = testing.simulate_post(
app,
'/',
json=json,
msgpack=msgpack,
headers=headers,
content_type=falcon.MEDIA_HTML,
)
assert result.text == response


@pytest.mark.skipif(not msgpack, reason='msgpack not installed')
@pytest.mark.parametrize(
'value',
(
'd\xff\xff\x00',
'quick fox jumps over the lazy dog',
'{"hello": "WORLD!"}',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praese',
'{"hello": "WORLD!", "greetings": "fellow traveller"}',
'\xe9\xe8',
),
)
def test_simulate_request_msgpack_different_bodies(value):
value = bytes(value, 'UTF-8')

resource = testing.SimpleTestResource(body=value)

app = App()
app.add_route('/', resource)

result = testing.simulate_post(app, '/', msgpack={})
captured_resp = resource.captured_resp
content = captured_resp.text

if len(value) > 40:
content = value[:20] + b'...' + value[-20:]
else:
content = value

args = [
captured_resp.status,
captured_resp.headers['content-type'],
str(content),
]

expected_content = 'Result<{}>'.format(' '.join(filter(None, args)))
assert str(result) == expected_content


@pytest.mark.parametrize('mode', ['wsgi', 'asgi', 'asgi-stream'])
def test_content_type(util, mode):
class Responder:
Expand Down