317 lines
9.3 KiB
Python
317 lines
9.3 KiB
Python
import inspect
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
|
|
from werkzeug import Request
|
|
from werkzeug import utils
|
|
from werkzeug.datastructures import Headers
|
|
from werkzeug.http import http_date
|
|
from werkzeug.http import parse_date
|
|
from werkzeug.test import Client
|
|
from werkzeug.wrappers import Response
|
|
|
|
|
|
def test_redirect():
|
|
resp = utils.redirect("/füübär")
|
|
assert resp.headers["Location"] == "/f%C3%BC%C3%BCb%C3%A4r"
|
|
assert resp.status_code == 302
|
|
assert resp.get_data() == (
|
|
b"<!doctype html>\n"
|
|
b"<html lang=en>\n"
|
|
b"<title>Redirecting...</title>\n"
|
|
b"<h1>Redirecting...</h1>\n"
|
|
b"<p>You should be redirected automatically to the target URL: "
|
|
b'<a href="/f%C3%BC%C3%BCb%C3%A4r">/f\xc3\xbc\xc3\xbcb\xc3\xa4r</a>. '
|
|
b"If not, click the link.\n"
|
|
)
|
|
|
|
resp = utils.redirect("http://☃.net/", 307)
|
|
assert resp.headers["Location"] == "http://xn--n3h.net/"
|
|
assert resp.status_code == 307
|
|
assert resp.get_data() == (
|
|
b"<!doctype html>\n"
|
|
b"<html lang=en>\n"
|
|
b"<title>Redirecting...</title>\n"
|
|
b"<h1>Redirecting...</h1>\n"
|
|
b"<p>You should be redirected automatically to the target URL: "
|
|
b'<a href="http://xn--n3h.net/">http://\xe2\x98\x83.net/</a>. '
|
|
b"If not, click the link.\n"
|
|
)
|
|
|
|
resp = utils.redirect("http://example.com/", 305)
|
|
assert resp.headers["Location"] == "http://example.com/"
|
|
assert resp.status_code == 305
|
|
assert resp.get_data() == (
|
|
b"<!doctype html>\n"
|
|
b"<html lang=en>\n"
|
|
b"<title>Redirecting...</title>\n"
|
|
b"<h1>Redirecting...</h1>\n"
|
|
b"<p>You should be redirected automatically to the target URL: "
|
|
b'<a href="http://example.com/">http://example.com/</a>. '
|
|
b"If not, click the link.\n"
|
|
)
|
|
|
|
|
|
def test_redirect_xss():
|
|
location = 'http://example.com/?xss="><script>alert(1)</script>'
|
|
resp = utils.redirect(location)
|
|
assert b"<script>alert(1)</script>" not in resp.get_data()
|
|
|
|
location = 'http://example.com/?xss="onmouseover="alert(1)'
|
|
resp = utils.redirect(location)
|
|
assert (
|
|
b'href="http://example.com/?xss="onmouseover="alert(1)"' not in resp.get_data()
|
|
)
|
|
|
|
|
|
def test_redirect_with_custom_response_class():
|
|
class MyResponse(Response):
|
|
pass
|
|
|
|
location = "http://example.com/redirect"
|
|
resp = utils.redirect(location, Response=MyResponse)
|
|
|
|
assert isinstance(resp, MyResponse)
|
|
assert resp.headers["Location"] == location
|
|
|
|
|
|
def test_cached_property():
|
|
foo = []
|
|
|
|
class A:
|
|
def prop(self):
|
|
foo.append(42)
|
|
return 42
|
|
|
|
prop = utils.cached_property(prop)
|
|
|
|
a = A()
|
|
p = a.prop
|
|
q = a.prop
|
|
assert p == q == 42
|
|
assert foo == [42]
|
|
|
|
foo = []
|
|
|
|
class A:
|
|
def _prop(self):
|
|
foo.append(42)
|
|
return 42
|
|
|
|
prop = utils.cached_property(_prop, name="prop")
|
|
del _prop
|
|
|
|
a = A()
|
|
p = a.prop
|
|
q = a.prop
|
|
assert p == q == 42
|
|
assert foo == [42]
|
|
|
|
|
|
def test_can_set_cached_property():
|
|
class A:
|
|
@utils.cached_property
|
|
def _prop(self):
|
|
return "cached_property return value"
|
|
|
|
a = A()
|
|
a._prop = "value"
|
|
assert a._prop == "value"
|
|
|
|
|
|
def test_invalidate_cached_property():
|
|
accessed = 0
|
|
|
|
class A:
|
|
@utils.cached_property
|
|
def prop(self):
|
|
nonlocal accessed
|
|
accessed += 1
|
|
return 42
|
|
|
|
a = A()
|
|
p = a.prop
|
|
q = a.prop
|
|
assert p == q == 42
|
|
assert accessed == 1
|
|
|
|
a.prop = 16
|
|
assert a.prop == 16
|
|
assert accessed == 1
|
|
|
|
del a.prop
|
|
r = a.prop
|
|
assert r == 42
|
|
assert accessed == 2
|
|
|
|
|
|
def test_inspect_treats_cached_property_as_property():
|
|
class A:
|
|
@utils.cached_property
|
|
def _prop(self):
|
|
return "cached_property return value"
|
|
|
|
attrs = inspect.classify_class_attrs(A)
|
|
for attr in attrs:
|
|
if attr.name == "_prop":
|
|
break
|
|
assert attr.kind == "property"
|
|
|
|
|
|
def test_environ_property():
|
|
class A:
|
|
environ = {"string": "abc", "number": "42"}
|
|
|
|
string = utils.environ_property("string")
|
|
missing = utils.environ_property("missing", "spam")
|
|
read_only = utils.environ_property("number")
|
|
number = utils.environ_property("number", load_func=int)
|
|
broken_number = utils.environ_property("broken_number", load_func=int)
|
|
date = utils.environ_property(
|
|
"date", None, parse_date, http_date, read_only=False
|
|
)
|
|
foo = utils.environ_property("foo")
|
|
|
|
a = A()
|
|
assert a.string == "abc"
|
|
assert a.missing == "spam"
|
|
|
|
def test_assign():
|
|
a.read_only = "something"
|
|
|
|
pytest.raises(AttributeError, test_assign)
|
|
assert a.number == 42
|
|
assert a.broken_number is None
|
|
assert a.date is None
|
|
a.date = datetime(2008, 1, 22, 10, 0, 0, 0)
|
|
assert a.environ["date"] == "Tue, 22 Jan 2008 10:00:00 GMT"
|
|
|
|
|
|
def test_import_string():
|
|
from datetime import date
|
|
from werkzeug.debug import DebuggedApplication
|
|
|
|
assert utils.import_string("datetime.date") is date
|
|
assert utils.import_string("datetime.date") is date
|
|
assert utils.import_string("datetime:date") is date
|
|
assert utils.import_string("XXXXXXXXXXXX", True) is None
|
|
assert utils.import_string("datetime.XXXXXXXXXXXX", True) is None
|
|
assert (
|
|
utils.import_string("werkzeug.debug.DebuggedApplication") is DebuggedApplication
|
|
)
|
|
pytest.raises(ImportError, utils.import_string, "XXXXXXXXXXXXXXXX")
|
|
pytest.raises(ImportError, utils.import_string, "datetime.XXXXXXXXXX")
|
|
|
|
|
|
def test_import_string_provides_traceback(tmpdir, monkeypatch):
|
|
monkeypatch.syspath_prepend(str(tmpdir))
|
|
# Couple of packages
|
|
dir_a = tmpdir.mkdir("a")
|
|
dir_b = tmpdir.mkdir("b")
|
|
# Totally packages, I promise
|
|
dir_a.join("__init__.py").write("")
|
|
dir_b.join("__init__.py").write("")
|
|
# 'aa.a' that depends on 'bb.b', which in turn has a broken import
|
|
dir_a.join("aa.py").write("from b import bb")
|
|
dir_b.join("bb.py").write("from os import a_typo")
|
|
|
|
# Do we get all the useful information in the traceback?
|
|
with pytest.raises(ImportError) as baz_exc:
|
|
utils.import_string("a.aa")
|
|
traceback = "".join(str(line) for line in baz_exc.traceback)
|
|
assert "bb.py':1" in traceback # a bit different than typical python tb
|
|
assert "from os import a_typo" in traceback
|
|
|
|
|
|
def test_import_string_attribute_error(tmpdir, monkeypatch):
|
|
monkeypatch.syspath_prepend(str(tmpdir))
|
|
tmpdir.join("foo_test.py").write("from bar_test import value")
|
|
tmpdir.join("bar_test.py").write("raise AttributeError('bad')")
|
|
|
|
with pytest.raises(AttributeError) as info:
|
|
utils.import_string("foo_test")
|
|
|
|
assert "bad" in str(info.value)
|
|
|
|
with pytest.raises(AttributeError) as info:
|
|
utils.import_string("bar_test")
|
|
|
|
assert "bad" in str(info.value)
|
|
|
|
|
|
def test_find_modules():
|
|
assert list(utils.find_modules("werkzeug.debug")) == [
|
|
"werkzeug.debug.console",
|
|
"werkzeug.debug.repr",
|
|
"werkzeug.debug.tbtools",
|
|
]
|
|
|
|
|
|
def test_header_set_duplication_bug():
|
|
headers = Headers([("Content-Type", "text/html"), ("Foo", "bar"), ("Blub", "blah")])
|
|
headers["blub"] = "hehe"
|
|
headers["blafasel"] = "humm"
|
|
assert headers == Headers(
|
|
[
|
|
("Content-Type", "text/html"),
|
|
("Foo", "bar"),
|
|
("blub", "hehe"),
|
|
("blafasel", "humm"),
|
|
]
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("path", "base_url", "absolute_location"),
|
|
[
|
|
("foo", "http://example.org/app", "http://example.org/app/foo/"),
|
|
("/foo", "http://example.org/app", "http://example.org/app/foo/"),
|
|
("/foo/bar", "http://example.org/", "http://example.org/foo/bar/"),
|
|
("/foo/bar", "http://example.org/app", "http://example.org/app/foo/bar/"),
|
|
("/foo?baz", "http://example.org/", "http://example.org/foo/?baz"),
|
|
("/foo/", "http://example.org/", "http://example.org/foo/"),
|
|
("/foo/", "http://example.org/app", "http://example.org/app/foo/"),
|
|
("/", "http://example.org/", "http://example.org/"),
|
|
("/", "http://example.org/app", "http://example.org/app/"),
|
|
],
|
|
)
|
|
@pytest.mark.parametrize("autocorrect", [False, True])
|
|
def test_append_slash_redirect(autocorrect, path, base_url, absolute_location):
|
|
@Request.application
|
|
def app(request):
|
|
rv = utils.append_slash_redirect(request.environ)
|
|
rv.autocorrect_location_header = autocorrect
|
|
return rv
|
|
|
|
client = Client(app)
|
|
response = client.get(path, base_url=base_url)
|
|
assert response.status_code == 308
|
|
|
|
if not autocorrect:
|
|
assert response.headers["Location"].count("/") == 1
|
|
else:
|
|
assert response.headers["Location"] == absolute_location
|
|
|
|
|
|
def test_cached_property_doc():
|
|
@utils.cached_property
|
|
def foo():
|
|
"""testing"""
|
|
return 42
|
|
|
|
assert foo.__doc__ == "testing"
|
|
assert foo.__name__ == "foo"
|
|
assert foo.__module__ == __name__
|
|
|
|
|
|
def test_secure_filename():
|
|
assert utils.secure_filename("My cool movie.mov") == "My_cool_movie.mov"
|
|
assert utils.secure_filename("../../../etc/passwd") == "etc_passwd"
|
|
assert (
|
|
utils.secure_filename("i contain cool \xfcml\xe4uts.txt")
|
|
== "i_contain_cool_umlauts.txt"
|
|
)
|
|
assert utils.secure_filename("__filename__") == "filename"
|
|
assert utils.secure_filename("foo$&^*)bar") == "foobar"
|