werkzeug

Форк
0
/
test_utils.py 
303 строки · 8.7 Кб
1
from __future__ import annotations
2

3
import inspect
4
from datetime import datetime
5

6
import pytest
7

8
from werkzeug import Request
9
from werkzeug import utils
10
from werkzeug.datastructures import Headers
11
from werkzeug.http import http_date
12
from werkzeug.http import parse_date
13
from werkzeug.test import Client
14
from werkzeug.test import EnvironBuilder
15
from werkzeug.wrappers import Response
16

17

18
@pytest.mark.parametrize(
19
    ("url", "code", "expect"),
20
    [
21
        ("http://example.com", None, "http://example.com"),
22
        ("/füübär", 305, "/f%C3%BC%C3%BCb%C3%A4r"),
23
        ("http://☃.example.com/", 307, "http://xn--n3h.example.com/"),
24
        ("itms-services://?url=abc", None, "itms-services://?url=abc"),
25
    ],
26
)
27
def test_redirect(url: str, code: int | None, expect: str) -> None:
28
    environ = EnvironBuilder().get_environ()
29

30
    if code is None:
31
        resp = utils.redirect(url)
32
        assert resp.status_code == 302
33
    else:
34
        resp = utils.redirect(url, code)
35
        assert resp.status_code == code
36

37
    assert resp.headers["Location"] == url
38
    assert resp.get_wsgi_headers(environ)["Location"] == expect
39
    assert resp.get_data(as_text=True).count(url) == 2
40

41

42
def test_redirect_xss():
43
    location = 'http://example.com/?xss="><script>alert(1)</script>'
44
    resp = utils.redirect(location)
45
    assert b"<script>alert(1)</script>" not in resp.get_data()
46

47
    location = 'http://example.com/?xss="onmouseover="alert(1)'
48
    resp = utils.redirect(location)
49
    assert (
50
        b'href="http://example.com/?xss="onmouseover="alert(1)"' not in resp.get_data()
51
    )
52

53

54
def test_redirect_with_custom_response_class():
55
    class MyResponse(Response):
56
        pass
57

58
    location = "http://example.com/redirect"
59
    resp = utils.redirect(location, Response=MyResponse)
60

61
    assert isinstance(resp, MyResponse)
62
    assert resp.headers["Location"] == location
63

64

65
def test_cached_property():
66
    foo = []
67

68
    class A:
69
        def prop(self):
70
            foo.append(42)
71
            return 42
72

73
        prop = utils.cached_property(prop)
74

75
    a = A()
76
    p = a.prop
77
    q = a.prop
78
    assert p == q == 42
79
    assert foo == [42]
80

81
    foo = []
82

83
    class A:
84
        def _prop(self):
85
            foo.append(42)
86
            return 42
87

88
        prop = utils.cached_property(_prop, name="prop")
89
        del _prop
90

91
    a = A()
92
    p = a.prop
93
    q = a.prop
94
    assert p == q == 42
95
    assert foo == [42]
96

97

98
def test_can_set_cached_property():
99
    class A:
100
        @utils.cached_property
101
        def _prop(self):
102
            return "cached_property return value"
103

104
    a = A()
105
    a._prop = "value"
106
    assert a._prop == "value"
107

108

109
def test_invalidate_cached_property():
110
    accessed = 0
111

112
    class A:
113
        @utils.cached_property
114
        def prop(self):
115
            nonlocal accessed
116
            accessed += 1
117
            return 42
118

119
    a = A()
120
    p = a.prop
121
    q = a.prop
122
    assert p == q == 42
123
    assert accessed == 1
124

125
    a.prop = 16
126
    assert a.prop == 16
127
    assert accessed == 1
128

129
    del a.prop
130
    r = a.prop
131
    assert r == 42
132
    assert accessed == 2
133

134

135
def test_inspect_treats_cached_property_as_property():
136
    class A:
137
        @utils.cached_property
138
        def _prop(self):
139
            return "cached_property return value"
140

141
    attrs = inspect.classify_class_attrs(A)
142
    for attr in attrs:
143
        if attr.name == "_prop":
144
            break
145
    assert attr.kind == "property"
146

147

148
def test_environ_property():
149
    class A:
150
        environ = {"string": "abc", "number": "42"}
151

152
        string = utils.environ_property("string")
153
        missing = utils.environ_property("missing", "spam")
154
        read_only = utils.environ_property("number")
155
        number = utils.environ_property("number", load_func=int)
156
        broken_number = utils.environ_property("broken_number", load_func=int)
157
        date = utils.environ_property(
158
            "date", None, parse_date, http_date, read_only=False
159
        )
160
        foo = utils.environ_property("foo")
161

162
    a = A()
163
    assert a.string == "abc"
164
    assert a.missing == "spam"
165

166
    def test_assign():
167
        a.read_only = "something"
168

169
    pytest.raises(AttributeError, test_assign)
170
    assert a.number == 42
171
    assert a.broken_number is None
172
    assert a.date is None
173
    a.date = datetime(2008, 1, 22, 10, 0, 0, 0)
174
    assert a.environ["date"] == "Tue, 22 Jan 2008 10:00:00 GMT"
175

176

177
def test_import_string():
178
    from datetime import date
179

180
    from werkzeug.debug import DebuggedApplication
181

182
    assert utils.import_string("datetime.date") is date
183
    assert utils.import_string("datetime.date") is date
184
    assert utils.import_string("datetime:date") is date
185
    assert utils.import_string("XXXXXXXXXXXX", True) is None
186
    assert utils.import_string("datetime.XXXXXXXXXXXX", True) is None
187
    assert (
188
        utils.import_string("werkzeug.debug.DebuggedApplication") is DebuggedApplication
189
    )
190
    pytest.raises(ImportError, utils.import_string, "XXXXXXXXXXXXXXXX")
191
    pytest.raises(ImportError, utils.import_string, "datetime.XXXXXXXXXX")
192

193

194
def test_import_string_provides_traceback(tmpdir, monkeypatch):
195
    monkeypatch.syspath_prepend(str(tmpdir))
196
    # Couple of packages
197
    dir_a = tmpdir.mkdir("a")
198
    dir_b = tmpdir.mkdir("b")
199
    # Totally packages, I promise
200
    dir_a.join("__init__.py").write("")
201
    dir_b.join("__init__.py").write("")
202
    # 'aa.a' that depends on 'bb.b', which in turn has a broken import
203
    dir_a.join("aa.py").write("from b import bb")
204
    dir_b.join("bb.py").write("from os import a_typo")
205

206
    # Do we get all the useful information in the traceback?
207
    with pytest.raises(ImportError) as baz_exc:
208
        utils.import_string("a.aa")
209
    traceback = "".join(str(line) for line in baz_exc.traceback)
210
    assert "bb.py':1" in traceback  # a bit different than typical python tb
211
    assert "from os import a_typo" in traceback
212

213

214
def test_import_string_attribute_error(tmpdir, monkeypatch):
215
    monkeypatch.syspath_prepend(str(tmpdir))
216
    tmpdir.join("foo_test.py").write("from bar_test import value")
217
    tmpdir.join("bar_test.py").write("raise AttributeError('bad')")
218

219
    with pytest.raises(AttributeError) as info:
220
        utils.import_string("foo_test")
221

222
    assert "bad" in str(info.value)
223

224
    with pytest.raises(AttributeError) as info:
225
        utils.import_string("bar_test")
226

227
    assert "bad" in str(info.value)
228

229

230
def test_find_modules():
231
    assert list(utils.find_modules("werkzeug.debug")) == [
232
        "werkzeug.debug.console",
233
        "werkzeug.debug.repr",
234
        "werkzeug.debug.tbtools",
235
    ]
236

237

238
def test_header_set_duplication_bug():
239
    headers = Headers([("Content-Type", "text/html"), ("Foo", "bar"), ("Blub", "blah")])
240
    headers["blub"] = "hehe"
241
    headers["blafasel"] = "humm"
242
    assert headers == Headers(
243
        [
244
            ("Content-Type", "text/html"),
245
            ("Foo", "bar"),
246
            ("blub", "hehe"),
247
            ("blafasel", "humm"),
248
        ]
249
    )
250

251

252
@pytest.mark.parametrize(
253
    ("path", "base_url", "absolute_location"),
254
    [
255
        ("foo", "http://example.org/app", "http://example.org/app/foo/"),
256
        ("/foo", "http://example.org/app", "http://example.org/app/foo/"),
257
        ("/foo/bar", "http://example.org/", "http://example.org/foo/bar/"),
258
        ("/foo/bar", "http://example.org/app", "http://example.org/app/foo/bar/"),
259
        ("/foo?baz", "http://example.org/", "http://example.org/foo/?baz"),
260
        ("/foo/", "http://example.org/", "http://example.org/foo/"),
261
        ("/foo/", "http://example.org/app", "http://example.org/app/foo/"),
262
        ("/", "http://example.org/", "http://example.org/"),
263
        ("/", "http://example.org/app", "http://example.org/app/"),
264
    ],
265
)
266
@pytest.mark.parametrize("autocorrect", [False, True])
267
def test_append_slash_redirect(autocorrect, path, base_url, absolute_location):
268
    @Request.application
269
    def app(request):
270
        rv = utils.append_slash_redirect(request.environ)
271
        rv.autocorrect_location_header = autocorrect
272
        return rv
273

274
    client = Client(app)
275
    response = client.get(path, base_url=base_url)
276
    assert response.status_code == 308
277

278
    if not autocorrect:
279
        assert response.headers["Location"].count("/") == 1
280
    else:
281
        assert response.headers["Location"] == absolute_location
282

283

284
def test_cached_property_doc():
285
    @utils.cached_property
286
    def foo():
287
        """testing"""
288
        return 42
289

290
    assert foo.__doc__ == "testing"
291
    assert foo.__name__ == "foo"
292
    assert foo.__module__ == __name__
293

294

295
def test_secure_filename():
296
    assert utils.secure_filename("My cool movie.mov") == "My_cool_movie.mov"
297
    assert utils.secure_filename("../../../etc/passwd") == "etc_passwd"
298
    assert (
299
        utils.secure_filename("i contain cool \xfcml\xe4uts.txt")
300
        == "i_contain_cool_umlauts.txt"
301
    )
302
    assert utils.secure_filename("__filename__") == "filename"
303
    assert utils.secure_filename("foo$&^*)bar") == "foobar"
304

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.