Skip to content

Commit 7fcbb74

Browse files
authored
Preserve encoded query parameters in recorded requests (#330)
* Fix double decoding of recorded query parameters * Use requests for query parameter regression tests * Clarify the query string regression test name
1 parent 3641550 commit 7fcbb74

2 files changed

Lines changed: 45 additions & 6 deletions

File tree

‎mocket/mocks/mockhttp.py‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from functools import cached_property
88
from http.server import BaseHTTPRequestHandler
99
from typing import Any, Callable
10-
from urllib.parse import parse_qs, unquote, urlsplit
10+
from urllib.parse import parse_qs, urlsplit
1111

1212
from h11 import SERVER, Connection, Data
1313
from h11 import Request as H11Request
@@ -90,11 +90,7 @@ def querystring(self) -> dict:
9090
Dictionary of query parameter names to lists of values
9191
"""
9292
parts = self.path.split("?", 1)
93-
return (
94-
parse_qs(unquote(parts[1]), keep_blank_values=True)
95-
if len(parts) == 2
96-
else {}
97-
)
93+
return parse_qs(parts[1], keep_blank_values=True) if len(parts) == 2 else {}
9894

9995
@cached_property
10096
def body(self) -> str:

‎tests/test_http.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,49 @@
1515
from mocket.mocks.mockhttp import Entry, Response
1616

1717

18+
@pytest.mark.parametrize(
19+
("query", "expected"),
20+
[
21+
("q=one%26two", {"q": ["one&two"]}),
22+
("q=one%2Btwo", {"q": ["one+two"]}),
23+
("q=%2526", {"q": ["%26"]}),
24+
("field%26name=value", {"field&name": ["value"]}),
25+
("field%3Dname=value", {"field=name": ["value"]}),
26+
("q=%25E2%2582%25AC", {"q": ["%E2%82%AC"]}),
27+
("q=one+two", {"q": ["one two"]}),
28+
("q=one%20two", {"q": ["one two"]}),
29+
("q=%E2%82%AC", {"q": ["\u20ac"]}),
30+
("q=&q=next&bare&empty=", {"q": ["", "next"], "bare": [""], "empty": [""]}),
31+
("", {}),
32+
(None, {}),
33+
],
34+
)
35+
def test_request_querystring_uses_form_urlencoded_semantics(query, expected):
36+
path = "/" if query is None else f"/?{query}"
37+
url = f"http://testme.org{path}"
38+
with Mocketizer(strict_mode=True):
39+
Entry.single_register(Entry.GET, url, body="ok")
40+
with requests.get(url, timeout=5) as response:
41+
assert response.content == b"ok"
42+
assert Mocket.last_request().querystring == expected
43+
44+
45+
@pytest.mark.parametrize("scheme", ["http", "https"])
46+
def test_recorded_querystring(scheme):
47+
parameters = [("field&name", "a+b&c=%26"), ("q", ""), ("q", "\u20ac")]
48+
url = f"{scheme}://testme.org/?{urlencode(parameters)}"
49+
50+
with Mocketizer(strict_mode=True):
51+
Entry.single_register(Entry.GET, url, body="ok")
52+
with requests.get(url, timeout=5) as response:
53+
assert response.content == b"ok"
54+
55+
assert Mocket.last_request().querystring == {
56+
"field&name": ["a+b&c=%26"],
57+
"q": ["", "\u20ac"],
58+
}
59+
60+
1861
class HttpTestCase(TestCase):
1962
def assertEqualHeaders(self, first, second, msg=None):
2063
first = {k.lower(): v for k, v in first.items()}

0 commit comments

Comments
 (0)