"Fossies" - the Fresh Open Source Software Archive

Member "flask-1.1.2/tests/test_helpers.py" (3 Apr 2020, 34689 Bytes) of package /linux/www/flask-1.1.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_helpers.py": 1.1.1_vs_1.1.2.

    1 # -*- coding: utf-8 -*-
    2 """
    3     tests.helpers
    4     ~~~~~~~~~~~~~~~~~~~~~~~
    5 
    6     Various helpers.
    7 
    8     :copyright: 2010 Pallets
    9     :license: BSD-3-Clause
   10 """
   11 import datetime
   12 import io
   13 import os
   14 import sys
   15 import uuid
   16 
   17 import pytest
   18 from werkzeug.datastructures import Range
   19 from werkzeug.exceptions import BadRequest
   20 from werkzeug.exceptions import NotFound
   21 from werkzeug.http import http_date
   22 from werkzeug.http import parse_cache_control_header
   23 from werkzeug.http import parse_options_header
   24 
   25 import flask
   26 from flask import json
   27 from flask._compat import StringIO
   28 from flask._compat import text_type
   29 from flask.helpers import get_debug_flag
   30 from flask.helpers import get_env
   31 
   32 
   33 def has_encoding(name):
   34     try:
   35         import codecs
   36 
   37         codecs.lookup(name)
   38         return True
   39     except LookupError:
   40         return False
   41 
   42 
   43 class FakePath(object):
   44     """Fake object to represent a ``PathLike object``.
   45 
   46     This represents a ``pathlib.Path`` object in python 3.
   47     See: https://www.python.org/dev/peps/pep-0519/
   48     """
   49 
   50     def __init__(self, path):
   51         self.path = path
   52 
   53     def __fspath__(self):
   54         return self.path
   55 
   56 
   57 class FixedOffset(datetime.tzinfo):
   58     """Fixed offset in hours east from UTC.
   59 
   60     This is a slight adaptation of the ``FixedOffset`` example found in
   61     https://docs.python.org/2.7/library/datetime.html.
   62     """
   63 
   64     def __init__(self, hours, name):
   65         self.__offset = datetime.timedelta(hours=hours)
   66         self.__name = name
   67 
   68     def utcoffset(self, dt):
   69         return self.__offset
   70 
   71     def tzname(self, dt):
   72         return self.__name
   73 
   74     def dst(self, dt):
   75         return datetime.timedelta()
   76 
   77 
   78 class TestJSON(object):
   79     @pytest.mark.parametrize(
   80         "value", (1, "t", True, False, None, [], [1, 2, 3], {}, {"foo": u"🐍"})
   81     )
   82     @pytest.mark.parametrize(
   83         "encoding",
   84         (
   85             "utf-8",
   86             "utf-8-sig",
   87             "utf-16-le",
   88             "utf-16-be",
   89             "utf-16",
   90             "utf-32-le",
   91             "utf-32-be",
   92             "utf-32",
   93         ),
   94     )
   95     def test_detect_encoding(self, value, encoding):
   96         data = json.dumps(value).encode(encoding)
   97         assert json.detect_encoding(data) == encoding
   98         assert json.loads(data) == value
   99 
  100     @pytest.mark.parametrize("debug", (True, False))
  101     def test_bad_request_debug_message(self, app, client, debug):
  102         app.config["DEBUG"] = debug
  103         app.config["TRAP_BAD_REQUEST_ERRORS"] = False
  104 
  105         @app.route("/json", methods=["POST"])
  106         def post_json():
  107             flask.request.get_json()
  108             return None
  109 
  110         rv = client.post("/json", data=None, content_type="application/json")
  111         assert rv.status_code == 400
  112         contains = b"Failed to decode JSON object" in rv.data
  113         assert contains == debug
  114 
  115     def test_json_bad_requests(self, app, client):
  116         @app.route("/json", methods=["POST"])
  117         def return_json():
  118             return flask.jsonify(foo=text_type(flask.request.get_json()))
  119 
  120         rv = client.post("/json", data="malformed", content_type="application/json")
  121         assert rv.status_code == 400
  122 
  123     def test_json_custom_mimetypes(self, app, client):
  124         @app.route("/json", methods=["POST"])
  125         def return_json():
  126             return flask.request.get_json()
  127 
  128         rv = client.post("/json", data='"foo"', content_type="application/x+json")
  129         assert rv.data == b"foo"
  130 
  131     @pytest.mark.parametrize(
  132         "test_value,expected", [(True, '"\\u2603"'), (False, u'"\u2603"')]
  133     )
  134     def test_json_as_unicode(self, test_value, expected, app, app_ctx):
  135 
  136         app.config["JSON_AS_ASCII"] = test_value
  137         rv = flask.json.dumps(u"\N{SNOWMAN}")
  138         assert rv == expected
  139 
  140     def test_json_dump_to_file(self, app, app_ctx):
  141         test_data = {"name": "Flask"}
  142         out = StringIO()
  143 
  144         flask.json.dump(test_data, out)
  145         out.seek(0)
  146         rv = flask.json.load(out)
  147         assert rv == test_data
  148 
  149     @pytest.mark.parametrize(
  150         "test_value", [0, -1, 1, 23, 3.14, "s", "longer string", True, False, None]
  151     )
  152     def test_jsonify_basic_types(self, test_value, app, client):
  153         """Test jsonify with basic types."""
  154 
  155         url = "/jsonify_basic_types"
  156         app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x))
  157         rv = client.get(url)
  158         assert rv.mimetype == "application/json"
  159         assert flask.json.loads(rv.data) == test_value
  160 
  161     def test_jsonify_dicts(self, app, client):
  162         """Test jsonify with dicts and kwargs unpacking."""
  163         d = {
  164             "a": 0,
  165             "b": 23,
  166             "c": 3.14,
  167             "d": "t",
  168             "e": "Hi",
  169             "f": True,
  170             "g": False,
  171             "h": ["test list", 10, False],
  172             "i": {"test": "dict"},
  173         }
  174 
  175         @app.route("/kw")
  176         def return_kwargs():
  177             return flask.jsonify(**d)
  178 
  179         @app.route("/dict")
  180         def return_dict():
  181             return flask.jsonify(d)
  182 
  183         for url in "/kw", "/dict":
  184             rv = client.get(url)
  185             assert rv.mimetype == "application/json"
  186             assert flask.json.loads(rv.data) == d
  187 
  188     def test_jsonify_arrays(self, app, client):
  189         """Test jsonify of lists and args unpacking."""
  190         a_list = [
  191             0,
  192             42,
  193             3.14,
  194             "t",
  195             "hello",
  196             True,
  197             False,
  198             ["test list", 2, False],
  199             {"test": "dict"},
  200         ]
  201 
  202         @app.route("/args_unpack")
  203         def return_args_unpack():
  204             return flask.jsonify(*a_list)
  205 
  206         @app.route("/array")
  207         def return_array():
  208             return flask.jsonify(a_list)
  209 
  210         for url in "/args_unpack", "/array":
  211             rv = client.get(url)
  212             assert rv.mimetype == "application/json"
  213             assert flask.json.loads(rv.data) == a_list
  214 
  215     def test_jsonify_date_types(self, app, client):
  216         """Test jsonify with datetime.date and datetime.datetime types."""
  217         test_dates = (
  218             datetime.datetime(1973, 3, 11, 6, 30, 45),
  219             datetime.date(1975, 1, 5),
  220         )
  221 
  222         for i, d in enumerate(test_dates):
  223             url = "/datetest{0}".format(i)
  224             app.add_url_rule(url, str(i), lambda val=d: flask.jsonify(x=val))
  225             rv = client.get(url)
  226             assert rv.mimetype == "application/json"
  227             assert flask.json.loads(rv.data)["x"] == http_date(d.timetuple())
  228 
  229     @pytest.mark.parametrize("tz", (("UTC", 0), ("PST", -8), ("KST", 9)))
  230     def test_jsonify_aware_datetimes(self, tz):
  231         """Test if aware datetime.datetime objects are converted into GMT."""
  232         tzinfo = FixedOffset(hours=tz[1], name=tz[0])
  233         dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo)
  234         gmt = FixedOffset(hours=0, name="GMT")
  235         expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"')
  236         assert flask.json.JSONEncoder().encode(dt) == expected
  237 
  238     def test_jsonify_uuid_types(self, app, client):
  239         """Test jsonify with uuid.UUID types"""
  240 
  241         test_uuid = uuid.UUID(bytes=b"\xDE\xAD\xBE\xEF" * 4)
  242         url = "/uuid_test"
  243         app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid))
  244 
  245         rv = client.get(url)
  246 
  247         rv_x = flask.json.loads(rv.data)["x"]
  248         assert rv_x == str(test_uuid)
  249         rv_uuid = uuid.UUID(rv_x)
  250         assert rv_uuid == test_uuid
  251 
  252     def test_json_attr(self, app, client):
  253         @app.route("/add", methods=["POST"])
  254         def add():
  255             json = flask.request.get_json()
  256             return text_type(json["a"] + json["b"])
  257 
  258         rv = client.post(
  259             "/add",
  260             data=flask.json.dumps({"a": 1, "b": 2}),
  261             content_type="application/json",
  262         )
  263         assert rv.data == b"3"
  264 
  265     def test_template_escaping(self, app, req_ctx):
  266         render = flask.render_template_string
  267         rv = flask.json.htmlsafe_dumps("</script>")
  268         assert rv == u'"\\u003c/script\\u003e"'
  269         assert type(rv) == text_type
  270         rv = render('{{ "</script>"|tojson }}')
  271         assert rv == '"\\u003c/script\\u003e"'
  272         rv = render('{{ "<\0/script>"|tojson }}')
  273         assert rv == '"\\u003c\\u0000/script\\u003e"'
  274         rv = render('{{ "<!--<script>"|tojson }}')
  275         assert rv == '"\\u003c!--\\u003cscript\\u003e"'
  276         rv = render('{{ "&"|tojson }}')
  277         assert rv == '"\\u0026"'
  278         rv = render('{{ "\'"|tojson }}')
  279         assert rv == '"\\u0027"'
  280         rv = render(
  281             "<a ng-data='{{ data|tojson }}'></a>", data={"x": ["foo", "bar", "baz'"]}
  282         )
  283         assert rv == '<a ng-data=\'{"x": ["foo", "bar", "baz\\u0027"]}\'></a>'
  284 
  285     def test_json_customization(self, app, client):
  286         class X(object):  # noqa: B903, for Python2 compatibility
  287             def __init__(self, val):
  288                 self.val = val
  289 
  290         class MyEncoder(flask.json.JSONEncoder):
  291             def default(self, o):
  292                 if isinstance(o, X):
  293                     return "<%d>" % o.val
  294                 return flask.json.JSONEncoder.default(self, o)
  295 
  296         class MyDecoder(flask.json.JSONDecoder):
  297             def __init__(self, *args, **kwargs):
  298                 kwargs.setdefault("object_hook", self.object_hook)
  299                 flask.json.JSONDecoder.__init__(self, *args, **kwargs)
  300 
  301             def object_hook(self, obj):
  302                 if len(obj) == 1 and "_foo" in obj:
  303                     return X(obj["_foo"])
  304                 return obj
  305 
  306         app.json_encoder = MyEncoder
  307         app.json_decoder = MyDecoder
  308 
  309         @app.route("/", methods=["POST"])
  310         def index():
  311             return flask.json.dumps(flask.request.get_json()["x"])
  312 
  313         rv = client.post(
  314             "/",
  315             data=flask.json.dumps({"x": {"_foo": 42}}),
  316             content_type="application/json",
  317         )
  318         assert rv.data == b'"<42>"'
  319 
  320     def test_blueprint_json_customization(self, app, client):
  321         class X(object):  # noqa: B903, for Python2 compatibility
  322             def __init__(self, val):
  323                 self.val = val
  324 
  325         class MyEncoder(flask.json.JSONEncoder):
  326             def default(self, o):
  327                 if isinstance(o, X):
  328                     return "<%d>" % o.val
  329 
  330                 return flask.json.JSONEncoder.default(self, o)
  331 
  332         class MyDecoder(flask.json.JSONDecoder):
  333             def __init__(self, *args, **kwargs):
  334                 kwargs.setdefault("object_hook", self.object_hook)
  335                 flask.json.JSONDecoder.__init__(self, *args, **kwargs)
  336 
  337             def object_hook(self, obj):
  338                 if len(obj) == 1 and "_foo" in obj:
  339                     return X(obj["_foo"])
  340 
  341                 return obj
  342 
  343         bp = flask.Blueprint("bp", __name__)
  344         bp.json_encoder = MyEncoder
  345         bp.json_decoder = MyDecoder
  346 
  347         @bp.route("/bp", methods=["POST"])
  348         def index():
  349             return flask.json.dumps(flask.request.get_json()["x"])
  350 
  351         app.register_blueprint(bp)
  352 
  353         rv = client.post(
  354             "/bp",
  355             data=flask.json.dumps({"x": {"_foo": 42}}),
  356             content_type="application/json",
  357         )
  358         assert rv.data == b'"<42>"'
  359 
  360     @pytest.mark.skipif(
  361         not has_encoding("euc-kr"), reason="The euc-kr encoding is required."
  362     )
  363     def test_modified_url_encoding(self, app, client):
  364         class ModifiedRequest(flask.Request):
  365             url_charset = "euc-kr"
  366 
  367         app.request_class = ModifiedRequest
  368         app.url_map.charset = "euc-kr"
  369 
  370         @app.route("/")
  371         def index():
  372             return flask.request.args["foo"]
  373 
  374         rv = client.get(u"/?foo=정상처리".encode("euc-kr"))
  375         assert rv.status_code == 200
  376         assert rv.data == u"정상처리".encode("utf-8")
  377 
  378     def test_json_key_sorting(self, app, client):
  379         app.debug = True
  380 
  381         assert app.config["JSON_SORT_KEYS"]
  382         d = dict.fromkeys(range(20), "foo")
  383 
  384         @app.route("/")
  385         def index():
  386             return flask.jsonify(values=d)
  387 
  388         rv = client.get("/")
  389         lines = [x.strip() for x in rv.data.strip().decode("utf-8").splitlines()]
  390         sorted_by_str = [
  391             "{",
  392             '"values": {',
  393             '"0": "foo",',
  394             '"1": "foo",',
  395             '"10": "foo",',
  396             '"11": "foo",',
  397             '"12": "foo",',
  398             '"13": "foo",',
  399             '"14": "foo",',
  400             '"15": "foo",',
  401             '"16": "foo",',
  402             '"17": "foo",',
  403             '"18": "foo",',
  404             '"19": "foo",',
  405             '"2": "foo",',
  406             '"3": "foo",',
  407             '"4": "foo",',
  408             '"5": "foo",',
  409             '"6": "foo",',
  410             '"7": "foo",',
  411             '"8": "foo",',
  412             '"9": "foo"',
  413             "}",
  414             "}",
  415         ]
  416         sorted_by_int = [
  417             "{",
  418             '"values": {',
  419             '"0": "foo",',
  420             '"1": "foo",',
  421             '"2": "foo",',
  422             '"3": "foo",',
  423             '"4": "foo",',
  424             '"5": "foo",',
  425             '"6": "foo",',
  426             '"7": "foo",',
  427             '"8": "foo",',
  428             '"9": "foo",',
  429             '"10": "foo",',
  430             '"11": "foo",',
  431             '"12": "foo",',
  432             '"13": "foo",',
  433             '"14": "foo",',
  434             '"15": "foo",',
  435             '"16": "foo",',
  436             '"17": "foo",',
  437             '"18": "foo",',
  438             '"19": "foo"',
  439             "}",
  440             "}",
  441         ]
  442 
  443         try:
  444             assert lines == sorted_by_int
  445         except AssertionError:
  446             assert lines == sorted_by_str
  447 
  448 
  449 class TestSendfile(object):
  450     def test_send_file_regular(self, app, req_ctx):
  451         rv = flask.send_file("static/index.html")
  452         assert rv.direct_passthrough
  453         assert rv.mimetype == "text/html"
  454         with app.open_resource("static/index.html") as f:
  455             rv.direct_passthrough = False
  456             assert rv.data == f.read()
  457         rv.close()
  458 
  459     def test_send_file_xsendfile(self, app, req_ctx):
  460         app.use_x_sendfile = True
  461         rv = flask.send_file("static/index.html")
  462         assert rv.direct_passthrough
  463         assert "x-sendfile" in rv.headers
  464         assert rv.headers["x-sendfile"] == os.path.join(
  465             app.root_path, "static/index.html"
  466         )
  467         assert rv.mimetype == "text/html"
  468         rv.close()
  469 
  470     def test_send_file_last_modified(self, app, client):
  471         last_modified = datetime.datetime(1999, 1, 1)
  472 
  473         @app.route("/")
  474         def index():
  475             return flask.send_file(
  476                 StringIO("party like it's"),
  477                 last_modified=last_modified,
  478                 mimetype="text/plain",
  479             )
  480 
  481         rv = client.get("/")
  482         assert rv.last_modified == last_modified
  483 
  484     def test_send_file_object_without_mimetype(self, app, req_ctx):
  485         with pytest.raises(ValueError) as excinfo:
  486             flask.send_file(StringIO("LOL"))
  487         assert "Unable to infer MIME-type" in str(excinfo.value)
  488         assert "no filename is available" in str(excinfo.value)
  489 
  490         flask.send_file(StringIO("LOL"), attachment_filename="filename")
  491 
  492     def test_send_file_object(self, app, req_ctx):
  493         with open(os.path.join(app.root_path, "static/index.html"), mode="rb") as f:
  494             rv = flask.send_file(f, mimetype="text/html")
  495             rv.direct_passthrough = False
  496             with app.open_resource("static/index.html") as f:
  497                 assert rv.data == f.read()
  498             assert rv.mimetype == "text/html"
  499             rv.close()
  500 
  501         app.use_x_sendfile = True
  502 
  503         with open(os.path.join(app.root_path, "static/index.html")) as f:
  504             rv = flask.send_file(f, mimetype="text/html")
  505             assert rv.mimetype == "text/html"
  506             assert "x-sendfile" not in rv.headers
  507             rv.close()
  508 
  509         app.use_x_sendfile = False
  510         f = StringIO("Test")
  511         rv = flask.send_file(f, mimetype="application/octet-stream")
  512         rv.direct_passthrough = False
  513         assert rv.data == b"Test"
  514         assert rv.mimetype == "application/octet-stream"
  515         rv.close()
  516 
  517         class PyStringIO(object):
  518             def __init__(self, *args, **kwargs):
  519                 self._io = StringIO(*args, **kwargs)
  520 
  521             def __getattr__(self, name):
  522                 return getattr(self._io, name)
  523 
  524         f = PyStringIO("Test")
  525         f.name = "test.txt"
  526         rv = flask.send_file(f, attachment_filename=f.name)
  527         rv.direct_passthrough = False
  528         assert rv.data == b"Test"
  529         assert rv.mimetype == "text/plain"
  530         rv.close()
  531 
  532         f = StringIO("Test")
  533         rv = flask.send_file(f, mimetype="text/plain")
  534         rv.direct_passthrough = False
  535         assert rv.data == b"Test"
  536         assert rv.mimetype == "text/plain"
  537         rv.close()
  538 
  539         app.use_x_sendfile = True
  540 
  541         f = StringIO("Test")
  542         rv = flask.send_file(f, mimetype="text/html")
  543         assert "x-sendfile" not in rv.headers
  544         rv.close()
  545 
  546     def test_send_file_pathlike(self, app, req_ctx):
  547         rv = flask.send_file(FakePath("static/index.html"))
  548         assert rv.direct_passthrough
  549         assert rv.mimetype == "text/html"
  550         with app.open_resource("static/index.html") as f:
  551             rv.direct_passthrough = False
  552             assert rv.data == f.read()
  553         rv.close()
  554 
  555     @pytest.mark.skipif(
  556         not callable(getattr(Range, "to_content_range_header", None)),
  557         reason="not implemented within werkzeug",
  558     )
  559     def test_send_file_range_request(self, app, client):
  560         @app.route("/")
  561         def index():
  562             return flask.send_file("static/index.html", conditional=True)
  563 
  564         rv = client.get("/", headers={"Range": "bytes=4-15"})
  565         assert rv.status_code == 206
  566         with app.open_resource("static/index.html") as f:
  567             assert rv.data == f.read()[4:16]
  568         rv.close()
  569 
  570         rv = client.get("/", headers={"Range": "bytes=4-"})
  571         assert rv.status_code == 206
  572         with app.open_resource("static/index.html") as f:
  573             assert rv.data == f.read()[4:]
  574         rv.close()
  575 
  576         rv = client.get("/", headers={"Range": "bytes=4-1000"})
  577         assert rv.status_code == 206
  578         with app.open_resource("static/index.html") as f:
  579             assert rv.data == f.read()[4:]
  580         rv.close()
  581 
  582         rv = client.get("/", headers={"Range": "bytes=-10"})
  583         assert rv.status_code == 206
  584         with app.open_resource("static/index.html") as f:
  585             assert rv.data == f.read()[-10:]
  586         rv.close()
  587 
  588         rv = client.get("/", headers={"Range": "bytes=1000-"})
  589         assert rv.status_code == 416
  590         rv.close()
  591 
  592         rv = client.get("/", headers={"Range": "bytes=-"})
  593         assert rv.status_code == 416
  594         rv.close()
  595 
  596         rv = client.get("/", headers={"Range": "somethingsomething"})
  597         assert rv.status_code == 416
  598         rv.close()
  599 
  600         last_modified = datetime.datetime.utcfromtimestamp(
  601             os.path.getmtime(os.path.join(app.root_path, "static/index.html"))
  602         ).replace(microsecond=0)
  603 
  604         rv = client.get(
  605             "/", headers={"Range": "bytes=4-15", "If-Range": http_date(last_modified)}
  606         )
  607         assert rv.status_code == 206
  608         rv.close()
  609 
  610         rv = client.get(
  611             "/",
  612             headers={
  613                 "Range": "bytes=4-15",
  614                 "If-Range": http_date(datetime.datetime(1999, 1, 1)),
  615             },
  616         )
  617         assert rv.status_code == 200
  618         rv.close()
  619 
  620     def test_send_file_range_request_bytesio(self, app, client):
  621         @app.route("/")
  622         def index():
  623             file = io.BytesIO(b"somethingsomething")
  624             return flask.send_file(
  625                 file, attachment_filename="filename", conditional=True
  626             )
  627 
  628         rv = client.get("/", headers={"Range": "bytes=4-15"})
  629         assert rv.status_code == 206
  630         assert rv.data == b"somethingsomething"[4:16]
  631         rv.close()
  632 
  633     @pytest.mark.skipif(
  634         not callable(getattr(Range, "to_content_range_header", None)),
  635         reason="not implemented within werkzeug",
  636     )
  637     def test_send_file_range_request_xsendfile_invalid(self, app, client):
  638         # https://github.com/pallets/flask/issues/2526
  639         app.use_x_sendfile = True
  640 
  641         @app.route("/")
  642         def index():
  643             return flask.send_file("static/index.html", conditional=True)
  644 
  645         rv = client.get("/", headers={"Range": "bytes=1000-"})
  646         assert rv.status_code == 416
  647         rv.close()
  648 
  649     def test_attachment(self, app, req_ctx):
  650         app = flask.Flask(__name__)
  651         with app.test_request_context():
  652             with open(os.path.join(app.root_path, "static/index.html")) as f:
  653                 rv = flask.send_file(
  654                     f, as_attachment=True, attachment_filename="index.html"
  655                 )
  656                 value, options = parse_options_header(rv.headers["Content-Disposition"])
  657                 assert value == "attachment"
  658                 rv.close()
  659 
  660         with open(os.path.join(app.root_path, "static/index.html")) as f:
  661             rv = flask.send_file(
  662                 f, as_attachment=True, attachment_filename="index.html"
  663             )
  664             value, options = parse_options_header(rv.headers["Content-Disposition"])
  665             assert value == "attachment"
  666             assert options["filename"] == "index.html"
  667             assert "filename*" not in rv.headers["Content-Disposition"]
  668             rv.close()
  669 
  670         rv = flask.send_file("static/index.html", as_attachment=True)
  671         value, options = parse_options_header(rv.headers["Content-Disposition"])
  672         assert value == "attachment"
  673         assert options["filename"] == "index.html"
  674         rv.close()
  675 
  676         rv = flask.send_file(
  677             StringIO("Test"),
  678             as_attachment=True,
  679             attachment_filename="index.txt",
  680             add_etags=False,
  681         )
  682         assert rv.mimetype == "text/plain"
  683         value, options = parse_options_header(rv.headers["Content-Disposition"])
  684         assert value == "attachment"
  685         assert options["filename"] == "index.txt"
  686         rv.close()
  687 
  688     @pytest.mark.usefixtures("req_ctx")
  689     @pytest.mark.parametrize(
  690         ("filename", "ascii", "utf8"),
  691         (
  692             ("index.html", "index.html", False),
  693             (
  694                 u"Ñandú/pingüino.txt",
  695                 '"Nandu/pinguino.txt"',
  696                 "%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt",
  697             ),
  698             (u"Vögel.txt", "Vogel.txt", "V%C3%B6gel.txt"),
  699             # Native string not marked as Unicode on Python 2
  700             ("tést.txt", "test.txt", "t%C3%A9st.txt"),
  701             # ":/" are not safe in filename* value
  702             (u"те:/ст", '":/"', "%D1%82%D0%B5%3A%2F%D1%81%D1%82"),
  703         ),
  704     )
  705     def test_attachment_filename_encoding(self, filename, ascii, utf8):
  706         rv = flask.send_file(
  707             "static/index.html", as_attachment=True, attachment_filename=filename
  708         )
  709         rv.close()
  710         content_disposition = rv.headers["Content-Disposition"]
  711         assert "filename=%s" % ascii in content_disposition
  712         if utf8:
  713             assert "filename*=UTF-8''" + utf8 in content_disposition
  714         else:
  715             assert "filename*=UTF-8''" not in content_disposition
  716 
  717     def test_static_file(self, app, req_ctx):
  718         # default cache timeout is 12 hours
  719 
  720         # Test with static file handler.
  721         rv = app.send_static_file("index.html")
  722         cc = parse_cache_control_header(rv.headers["Cache-Control"])
  723         assert cc.max_age == 12 * 60 * 60
  724         rv.close()
  725         # Test again with direct use of send_file utility.
  726         rv = flask.send_file("static/index.html")
  727         cc = parse_cache_control_header(rv.headers["Cache-Control"])
  728         assert cc.max_age == 12 * 60 * 60
  729         rv.close()
  730         app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 3600
  731 
  732         # Test with static file handler.
  733         rv = app.send_static_file("index.html")
  734         cc = parse_cache_control_header(rv.headers["Cache-Control"])
  735         assert cc.max_age == 3600
  736         rv.close()
  737         # Test again with direct use of send_file utility.
  738         rv = flask.send_file("static/index.html")
  739         cc = parse_cache_control_header(rv.headers["Cache-Control"])
  740         assert cc.max_age == 3600
  741         rv.close()
  742 
  743         # Test with static file handler.
  744         rv = app.send_static_file(FakePath("index.html"))
  745         cc = parse_cache_control_header(rv.headers["Cache-Control"])
  746         assert cc.max_age == 3600
  747         rv.close()
  748 
  749         class StaticFileApp(flask.Flask):
  750             def get_send_file_max_age(self, filename):
  751                 return 10
  752 
  753         app = StaticFileApp(__name__)
  754         with app.test_request_context():
  755             # Test with static file handler.
  756             rv = app.send_static_file("index.html")
  757             cc = parse_cache_control_header(rv.headers["Cache-Control"])
  758             assert cc.max_age == 10
  759             rv.close()
  760             # Test again with direct use of send_file utility.
  761             rv = flask.send_file("static/index.html")
  762             cc = parse_cache_control_header(rv.headers["Cache-Control"])
  763             assert cc.max_age == 10
  764             rv.close()
  765 
  766     def test_send_from_directory(self, app, req_ctx):
  767         app.root_path = os.path.join(
  768             os.path.dirname(__file__), "test_apps", "subdomaintestmodule"
  769         )
  770         rv = flask.send_from_directory("static", "hello.txt")
  771         rv.direct_passthrough = False
  772         assert rv.data.strip() == b"Hello Subdomain"
  773         rv.close()
  774 
  775     def test_send_from_directory_pathlike(self, app, req_ctx):
  776         app.root_path = os.path.join(
  777             os.path.dirname(__file__), "test_apps", "subdomaintestmodule"
  778         )
  779         rv = flask.send_from_directory(FakePath("static"), FakePath("hello.txt"))
  780         rv.direct_passthrough = False
  781         assert rv.data.strip() == b"Hello Subdomain"
  782         rv.close()
  783 
  784     def test_send_from_directory_null_character(self, app, req_ctx):
  785         app.root_path = os.path.join(
  786             os.path.dirname(__file__), "test_apps", "subdomaintestmodule"
  787         )
  788 
  789         if sys.version_info >= (3, 8):
  790             exception = NotFound
  791         else:
  792             exception = BadRequest
  793 
  794         with pytest.raises(exception):
  795             flask.send_from_directory("static", "bad\x00")
  796 
  797 
  798 class TestUrlFor(object):
  799     def test_url_for_with_anchor(self, app, req_ctx):
  800         @app.route("/")
  801         def index():
  802             return "42"
  803 
  804         assert flask.url_for("index", _anchor="x y") == "/#x%20y"
  805 
  806     def test_url_for_with_scheme(self, app, req_ctx):
  807         @app.route("/")
  808         def index():
  809             return "42"
  810 
  811         assert (
  812             flask.url_for("index", _external=True, _scheme="https")
  813             == "https://localhost/"
  814         )
  815 
  816     def test_url_for_with_scheme_not_external(self, app, req_ctx):
  817         @app.route("/")
  818         def index():
  819             return "42"
  820 
  821         pytest.raises(ValueError, flask.url_for, "index", _scheme="https")
  822 
  823     def test_url_for_with_alternating_schemes(self, app, req_ctx):
  824         @app.route("/")
  825         def index():
  826             return "42"
  827 
  828         assert flask.url_for("index", _external=True) == "http://localhost/"
  829         assert (
  830             flask.url_for("index", _external=True, _scheme="https")
  831             == "https://localhost/"
  832         )
  833         assert flask.url_for("index", _external=True) == "http://localhost/"
  834 
  835     def test_url_with_method(self, app, req_ctx):
  836         from flask.views import MethodView
  837 
  838         class MyView(MethodView):
  839             def get(self, id=None):
  840                 if id is None:
  841                     return "List"
  842                 return "Get %d" % id
  843 
  844             def post(self):
  845                 return "Create"
  846 
  847         myview = MyView.as_view("myview")
  848         app.add_url_rule("/myview/", methods=["GET"], view_func=myview)
  849         app.add_url_rule("/myview/<int:id>", methods=["GET"], view_func=myview)
  850         app.add_url_rule("/myview/create", methods=["POST"], view_func=myview)
  851 
  852         assert flask.url_for("myview", _method="GET") == "/myview/"
  853         assert flask.url_for("myview", id=42, _method="GET") == "/myview/42"
  854         assert flask.url_for("myview", _method="POST") == "/myview/create"
  855 
  856 
  857 class TestNoImports(object):
  858     """Test Flasks are created without import.
  859 
  860     Avoiding ``__import__`` helps create Flask instances where there are errors
  861     at import time.  Those runtime errors will be apparent to the user soon
  862     enough, but tools which build Flask instances meta-programmatically benefit
  863     from a Flask which does not ``__import__``.  Instead of importing to
  864     retrieve file paths or metadata on a module or package, use the pkgutil and
  865     imp modules in the Python standard library.
  866     """
  867 
  868     def test_name_with_import_error(self, modules_tmpdir):
  869         modules_tmpdir.join("importerror.py").write("raise NotImplementedError()")
  870         try:
  871             flask.Flask("importerror")
  872         except NotImplementedError:
  873             AssertionError("Flask(import_name) is importing import_name.")
  874 
  875 
  876 class TestStreaming(object):
  877     def test_streaming_with_context(self, app, client):
  878         @app.route("/")
  879         def index():
  880             def generate():
  881                 yield "Hello "
  882                 yield flask.request.args["name"]
  883                 yield "!"
  884 
  885             return flask.Response(flask.stream_with_context(generate()))
  886 
  887         rv = client.get("/?name=World")
  888         assert rv.data == b"Hello World!"
  889 
  890     def test_streaming_with_context_as_decorator(self, app, client):
  891         @app.route("/")
  892         def index():
  893             @flask.stream_with_context
  894             def generate(hello):
  895                 yield hello
  896                 yield flask.request.args["name"]
  897                 yield "!"
  898 
  899             return flask.Response(generate("Hello "))
  900 
  901         rv = client.get("/?name=World")
  902         assert rv.data == b"Hello World!"
  903 
  904     def test_streaming_with_context_and_custom_close(self, app, client):
  905         called = []
  906 
  907         class Wrapper(object):
  908             def __init__(self, gen):
  909                 self._gen = gen
  910 
  911             def __iter__(self):
  912                 return self
  913 
  914             def close(self):
  915                 called.append(42)
  916 
  917             def __next__(self):
  918                 return next(self._gen)
  919 
  920             next = __next__
  921 
  922         @app.route("/")
  923         def index():
  924             def generate():
  925                 yield "Hello "
  926                 yield flask.request.args["name"]
  927                 yield "!"
  928 
  929             return flask.Response(flask.stream_with_context(Wrapper(generate())))
  930 
  931         rv = client.get("/?name=World")
  932         assert rv.data == b"Hello World!"
  933         assert called == [42]
  934 
  935     def test_stream_keeps_session(self, app, client):
  936         @app.route("/")
  937         def index():
  938             flask.session["test"] = "flask"
  939 
  940             @flask.stream_with_context
  941             def gen():
  942                 yield flask.session["test"]
  943 
  944             return flask.Response(gen())
  945 
  946         rv = client.get("/")
  947         assert rv.data == b"flask"
  948 
  949 
  950 class TestSafeJoin(object):
  951     def test_safe_join(self):
  952         # Valid combinations of *args and expected joined paths.
  953         passing = (
  954             (("a/b/c",), "a/b/c"),
  955             (("/", "a/", "b/", "c/"), "/a/b/c"),
  956             (("a", "b", "c"), "a/b/c"),
  957             (("/a", "b/c"), "/a/b/c"),
  958             (("a/b", "X/../c"), "a/b/c"),
  959             (("/a/b", "c/X/.."), "/a/b/c"),
  960             # If last path is '' add a slash
  961             (("/a/b/c", ""), "/a/b/c/"),
  962             # Preserve dot slash
  963             (("/a/b/c", "./"), "/a/b/c/."),
  964             (("a/b/c", "X/.."), "a/b/c/."),
  965             # Base directory is always considered safe
  966             (("../", "a/b/c"), "../a/b/c"),
  967             (("/..",), "/.."),
  968         )
  969 
  970         for args, expected in passing:
  971             assert flask.safe_join(*args) == expected
  972 
  973     def test_safe_join_exceptions(self):
  974         # Should raise werkzeug.exceptions.NotFound on unsafe joins.
  975         failing = (
  976             # path.isabs and ``..'' checks
  977             ("/a", "b", "/c"),
  978             ("/a", "../b/c"),
  979             ("/a", "..", "b/c"),
  980             # Boundaries violations after path normalization
  981             ("/a", "b/../b/../../c"),
  982             ("/a", "b", "c/../.."),
  983             ("/a", "b/../../c"),
  984         )
  985 
  986         for args in failing:
  987             with pytest.raises(NotFound):
  988                 print(flask.safe_join(*args))
  989 
  990 
  991 class TestHelpers(object):
  992     @pytest.mark.parametrize(
  993         "debug, expected_flag, expected_default_flag",
  994         [
  995             ("", False, False),
  996             ("0", False, False),
  997             ("False", False, False),
  998             ("No", False, False),
  999             ("True", True, True),
 1000         ],
 1001     )
 1002     def test_get_debug_flag(
 1003         self, monkeypatch, debug, expected_flag, expected_default_flag
 1004     ):
 1005         monkeypatch.setenv("FLASK_DEBUG", debug)
 1006         if expected_flag is None:
 1007             assert get_debug_flag() is None
 1008         else:
 1009             assert get_debug_flag() == expected_flag
 1010         assert get_debug_flag() == expected_default_flag
 1011 
 1012     @pytest.mark.parametrize(
 1013         "env, ref_env, debug",
 1014         [
 1015             ("", "production", False),
 1016             ("production", "production", False),
 1017             ("development", "development", True),
 1018             ("other", "other", False),
 1019         ],
 1020     )
 1021     def test_get_env(self, monkeypatch, env, ref_env, debug):
 1022         monkeypatch.setenv("FLASK_ENV", env)
 1023         assert get_debug_flag() == debug
 1024         assert get_env() == ref_env
 1025 
 1026     def test_make_response(self):
 1027         app = flask.Flask(__name__)
 1028         with app.test_request_context():
 1029             rv = flask.helpers.make_response()
 1030             assert rv.status_code == 200
 1031             assert rv.mimetype == "text/html"
 1032 
 1033             rv = flask.helpers.make_response("Hello")
 1034             assert rv.status_code == 200
 1035             assert rv.data == b"Hello"
 1036             assert rv.mimetype == "text/html"
 1037 
 1038     @pytest.mark.parametrize("mode", ("r", "rb", "rt"))
 1039     def test_open_resource(self, mode):
 1040         app = flask.Flask(__name__)
 1041 
 1042         with app.open_resource("static/index.html", mode) as f:
 1043             assert "<h1>Hello World!</h1>" in str(f.read())
 1044 
 1045     @pytest.mark.parametrize("mode", ("w", "x", "a", "r+"))
 1046     def test_open_resource_exceptions(self, mode):
 1047         app = flask.Flask(__name__)
 1048 
 1049         with pytest.raises(ValueError):
 1050             app.open_resource("static/index.html", mode)