Skip to content

Commit 71de7ef

Browse files
committed
httpserver: fix query_string type
In werzeug the requets.query_string is a bytes object so expecting a string in the RequestMatcher breaks the query_string matching functionality. Users can provide a bytes object but in this case they will break the official API. In order to not break the API, an implicit conversion is added which converts the string object to bytes. Accepting bytes is also added in case the user does not want this implicit behavior. Fixes #16
1 parent 32e1b2c commit 71de7ef

3 files changed

Lines changed: 45 additions & 3 deletions

File tree

pytest_httpserver/httpserver.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ class RequestMatcher:
136136
by default, see `data_encoding`) or a bytes object.
137137
:param data_encoding: the encoding used for data parameter if data is a string.
138138
:param headers: dictionary of the headers of the request to be matched
139-
:param query_string: the http query string starting with ``?``, such as ``?username=user``
139+
:param query_string: the http query string, after ``?``, such as ``username=user``.
140+
If string is specified it will be encoded to bytes with the encode method of
141+
the string.
140142
"""
141143

142144
def __init__(
@@ -146,9 +148,12 @@ def __init__(
146148
data: Union[str, bytes, None] = None,
147149
data_encoding: str = "utf-8",
148150
headers: Optional[Mapping[str, str]] = None,
149-
query_string: Optional[str] = None,
151+
query_string: Union[None, bytes, str] = None,
150152
header_value_matcher: Optional[HeaderValueMatcher] = None):
151153

154+
if query_string is not None and not isinstance(query_string, (str, bytes)):
155+
raise TypeError("query_string must be a string, or a bytes-like object")
156+
152157
self.uri = uri
153158
self.method = method
154159
self.query_string = query_string
@@ -206,7 +211,15 @@ def difference(self, request: Request) -> list:
206211
if self.method != METHOD_ALL and self.method != request.method:
207212
retval.append(("method", request.method, self.method))
208213

209-
if self.query_string is not None and self.query_string != request.query_string:
214+
if self.query_string is not None:
215+
if isinstance(self.query_string, str):
216+
query_string = self.query_string.encode()
217+
elif isinstance(self.query_string, bytes):
218+
query_string = self.query_string
219+
else:
220+
raise TypeError("query_string must be a string, or a bytes-like object")
221+
222+
if self.query_string is not None and query_string != request.query_string:
210223
retval.append(("query_string", request.query_string, self.query_string))
211224

212225
request_headers = {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
issues:
3+
- |
4+
Fixed issue \#16 by converting string object passed as query_string
5+
to bytes which is the type of the query string in werkzeug, and also allowing
6+
bytes as the parameter.

tests/test_querystring.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import requests
2+
from pytest import approx, raises
3+
4+
from pytest_httpserver import HTTPServer
5+
6+
7+
def test_querystring_str(httpserver: HTTPServer):
8+
httpserver.expect_request("/foobar", query_string="foo=bar", method="GET").respond_with_data(
9+
"example_response"
10+
)
11+
response = requests.get(httpserver.url_for("/foobar?foo=bar"))
12+
httpserver.check_assertions()
13+
assert response.text == "example_response"
14+
assert response.status_code == 200
15+
16+
def test_querystring_bytes(httpserver: HTTPServer):
17+
httpserver.expect_request("/foobar", query_string=b"foo=bar", method="GET").respond_with_data(
18+
"example_response"
19+
)
20+
response = requests.get(httpserver.url_for("/foobar?foo=bar"))
21+
httpserver.check_assertions()
22+
assert response.text == "example_response"
23+
assert response.status_code == 200

0 commit comments

Comments
 (0)