mirror of https://github.com/pallets/flask.git
				
				
				
			
		
			
				
	
	
		
			347 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
import datetime
 | 
						|
import decimal
 | 
						|
import io
 | 
						|
import uuid
 | 
						|
 | 
						|
import pytest
 | 
						|
from werkzeug.http import http_date
 | 
						|
 | 
						|
import flask
 | 
						|
from flask import json
 | 
						|
from flask.json.provider import DefaultJSONProvider
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("debug", (True, False))
 | 
						|
def test_bad_request_debug_message(app, client, debug):
 | 
						|
    app.config["DEBUG"] = debug
 | 
						|
    app.config["TRAP_BAD_REQUEST_ERRORS"] = False
 | 
						|
 | 
						|
    @app.route("/json", methods=["POST"])
 | 
						|
    def post_json():
 | 
						|
        flask.request.get_json()
 | 
						|
        return None
 | 
						|
 | 
						|
    rv = client.post("/json", data=None, content_type="application/json")
 | 
						|
    assert rv.status_code == 400
 | 
						|
    contains = b"Failed to decode JSON object" in rv.data
 | 
						|
    assert contains == debug
 | 
						|
 | 
						|
 | 
						|
def test_json_bad_requests(app, client):
 | 
						|
    @app.route("/json", methods=["POST"])
 | 
						|
    def return_json():
 | 
						|
        return flask.jsonify(foo=str(flask.request.get_json()))
 | 
						|
 | 
						|
    rv = client.post("/json", data="malformed", content_type="application/json")
 | 
						|
    assert rv.status_code == 400
 | 
						|
 | 
						|
 | 
						|
def test_json_custom_mimetypes(app, client):
 | 
						|
    @app.route("/json", methods=["POST"])
 | 
						|
    def return_json():
 | 
						|
        return flask.request.get_json()
 | 
						|
 | 
						|
    rv = client.post("/json", data='"foo"', content_type="application/x+json")
 | 
						|
    assert rv.data == b"foo"
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "test_value,expected", [(True, '"\\u2603"'), (False, '"\u2603"')]
 | 
						|
)
 | 
						|
def test_json_as_unicode(test_value, expected, app, app_ctx):
 | 
						|
    app.json.ensure_ascii = test_value
 | 
						|
    rv = app.json.dumps("\N{SNOWMAN}")
 | 
						|
    assert rv == expected
 | 
						|
 | 
						|
 | 
						|
def test_json_dump_to_file(app, app_ctx):
 | 
						|
    test_data = {"name": "Flask"}
 | 
						|
    out = io.StringIO()
 | 
						|
 | 
						|
    flask.json.dump(test_data, out)
 | 
						|
    out.seek(0)
 | 
						|
    rv = flask.json.load(out)
 | 
						|
    assert rv == test_data
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "test_value", [0, -1, 1, 23, 3.14, "s", "longer string", True, False, None]
 | 
						|
)
 | 
						|
def test_jsonify_basic_types(test_value, app, client):
 | 
						|
    url = "/jsonify_basic_types"
 | 
						|
    app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x))
 | 
						|
    rv = client.get(url)
 | 
						|
    assert rv.mimetype == "application/json"
 | 
						|
    assert flask.json.loads(rv.data) == test_value
 | 
						|
 | 
						|
 | 
						|
def test_jsonify_dicts(app, client):
 | 
						|
    d = {
 | 
						|
        "a": 0,
 | 
						|
        "b": 23,
 | 
						|
        "c": 3.14,
 | 
						|
        "d": "t",
 | 
						|
        "e": "Hi",
 | 
						|
        "f": True,
 | 
						|
        "g": False,
 | 
						|
        "h": ["test list", 10, False],
 | 
						|
        "i": {"test": "dict"},
 | 
						|
    }
 | 
						|
 | 
						|
    @app.route("/kw")
 | 
						|
    def return_kwargs():
 | 
						|
        return flask.jsonify(**d)
 | 
						|
 | 
						|
    @app.route("/dict")
 | 
						|
    def return_dict():
 | 
						|
        return flask.jsonify(d)
 | 
						|
 | 
						|
    for url in "/kw", "/dict":
 | 
						|
        rv = client.get(url)
 | 
						|
        assert rv.mimetype == "application/json"
 | 
						|
        assert flask.json.loads(rv.data) == d
 | 
						|
 | 
						|
 | 
						|
def test_jsonify_arrays(app, client):
 | 
						|
    """Test jsonify of lists and args unpacking."""
 | 
						|
    a_list = [
 | 
						|
        0,
 | 
						|
        42,
 | 
						|
        3.14,
 | 
						|
        "t",
 | 
						|
        "hello",
 | 
						|
        True,
 | 
						|
        False,
 | 
						|
        ["test list", 2, False],
 | 
						|
        {"test": "dict"},
 | 
						|
    ]
 | 
						|
 | 
						|
    @app.route("/args_unpack")
 | 
						|
    def return_args_unpack():
 | 
						|
        return flask.jsonify(*a_list)
 | 
						|
 | 
						|
    @app.route("/array")
 | 
						|
    def return_array():
 | 
						|
        return flask.jsonify(a_list)
 | 
						|
 | 
						|
    for url in "/args_unpack", "/array":
 | 
						|
        rv = client.get(url)
 | 
						|
        assert rv.mimetype == "application/json"
 | 
						|
        assert flask.json.loads(rv.data) == a_list
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "value", [datetime.datetime(1973, 3, 11, 6, 30, 45), datetime.date(1975, 1, 5)]
 | 
						|
)
 | 
						|
def test_jsonify_datetime(app, client, value):
 | 
						|
    @app.route("/")
 | 
						|
    def index():
 | 
						|
        return flask.jsonify(value=value)
 | 
						|
 | 
						|
    r = client.get()
 | 
						|
    assert r.json["value"] == http_date(value)
 | 
						|
 | 
						|
 | 
						|
class FixedOffset(datetime.tzinfo):
 | 
						|
    """Fixed offset in hours east from UTC.
 | 
						|
 | 
						|
    This is a slight adaptation of the ``FixedOffset`` example found in
 | 
						|
    https://docs.python.org/2.7/library/datetime.html.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, hours, name):
 | 
						|
        self.__offset = datetime.timedelta(hours=hours)
 | 
						|
        self.__name = name
 | 
						|
 | 
						|
    def utcoffset(self, dt):
 | 
						|
        return self.__offset
 | 
						|
 | 
						|
    def tzname(self, dt):
 | 
						|
        return self.__name
 | 
						|
 | 
						|
    def dst(self, dt):
 | 
						|
        return datetime.timedelta()
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("tz", (("UTC", 0), ("PST", -8), ("KST", 9)))
 | 
						|
def test_jsonify_aware_datetimes(tz):
 | 
						|
    """Test if aware datetime.datetime objects are converted into GMT."""
 | 
						|
    tzinfo = FixedOffset(hours=tz[1], name=tz[0])
 | 
						|
    dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo)
 | 
						|
    gmt = FixedOffset(hours=0, name="GMT")
 | 
						|
    expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"')
 | 
						|
    assert flask.json.dumps(dt) == expected
 | 
						|
 | 
						|
 | 
						|
def test_jsonify_uuid_types(app, client):
 | 
						|
    """Test jsonify with uuid.UUID types"""
 | 
						|
 | 
						|
    test_uuid = uuid.UUID(bytes=b"\xDE\xAD\xBE\xEF" * 4)
 | 
						|
    url = "/uuid_test"
 | 
						|
    app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid))
 | 
						|
 | 
						|
    rv = client.get(url)
 | 
						|
 | 
						|
    rv_x = flask.json.loads(rv.data)["x"]
 | 
						|
    assert rv_x == str(test_uuid)
 | 
						|
    rv_uuid = uuid.UUID(rv_x)
 | 
						|
    assert rv_uuid == test_uuid
 | 
						|
 | 
						|
 | 
						|
def test_json_decimal():
 | 
						|
    rv = flask.json.dumps(decimal.Decimal("0.003"))
 | 
						|
    assert rv == '"0.003"'
 | 
						|
 | 
						|
 | 
						|
def test_json_attr(app, client):
 | 
						|
    @app.route("/add", methods=["POST"])
 | 
						|
    def add():
 | 
						|
        json = flask.request.get_json()
 | 
						|
        return str(json["a"] + json["b"])
 | 
						|
 | 
						|
    rv = client.post(
 | 
						|
        "/add",
 | 
						|
        data=flask.json.dumps({"a": 1, "b": 2}),
 | 
						|
        content_type="application/json",
 | 
						|
    )
 | 
						|
    assert rv.data == b"3"
 | 
						|
 | 
						|
 | 
						|
def test_tojson_filter(app, req_ctx):
 | 
						|
    # The tojson filter is tested in Jinja, this confirms that it's
 | 
						|
    # using Flask's dumps.
 | 
						|
    rv = flask.render_template_string(
 | 
						|
        "const data = {{ data|tojson }};",
 | 
						|
        data={"name": "</script>", "time": datetime.datetime(2021, 2, 1, 7, 15)},
 | 
						|
    )
 | 
						|
    assert rv == (
 | 
						|
        'const data = {"name": "\\u003c/script\\u003e",'
 | 
						|
        ' "time": "Mon, 01 Feb 2021 07:15:00 GMT"};'
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def test_json_customization(app, client):
 | 
						|
    class X:  # noqa: B903, for Python2 compatibility
 | 
						|
        def __init__(self, val):
 | 
						|
            self.val = val
 | 
						|
 | 
						|
    def default(o):
 | 
						|
        if isinstance(o, X):
 | 
						|
            return f"<{o.val}>"
 | 
						|
 | 
						|
        return DefaultJSONProvider.default(o)
 | 
						|
 | 
						|
    class CustomProvider(DefaultJSONProvider):
 | 
						|
        def object_hook(self, obj):
 | 
						|
            if len(obj) == 1 and "_foo" in obj:
 | 
						|
                return X(obj["_foo"])
 | 
						|
 | 
						|
            return obj
 | 
						|
 | 
						|
        def loads(self, s, **kwargs):
 | 
						|
            kwargs.setdefault("object_hook", self.object_hook)
 | 
						|
            return super().loads(s, **kwargs)
 | 
						|
 | 
						|
    app.json = CustomProvider(app)
 | 
						|
    app.json.default = default
 | 
						|
 | 
						|
    @app.route("/", methods=["POST"])
 | 
						|
    def index():
 | 
						|
        return flask.json.dumps(flask.request.get_json()["x"])
 | 
						|
 | 
						|
    rv = client.post(
 | 
						|
        "/",
 | 
						|
        data=flask.json.dumps({"x": {"_foo": 42}}),
 | 
						|
        content_type="application/json",
 | 
						|
    )
 | 
						|
    assert rv.data == b'"<42>"'
 | 
						|
 | 
						|
 | 
						|
def _has_encoding(name):
 | 
						|
    try:
 | 
						|
        import codecs
 | 
						|
 | 
						|
        codecs.lookup(name)
 | 
						|
        return True
 | 
						|
    except LookupError:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def test_json_key_sorting(app, client):
 | 
						|
    app.debug = True
 | 
						|
    assert app.json.sort_keys
 | 
						|
    d = dict.fromkeys(range(20), "foo")
 | 
						|
 | 
						|
    @app.route("/")
 | 
						|
    def index():
 | 
						|
        return flask.jsonify(values=d)
 | 
						|
 | 
						|
    rv = client.get("/")
 | 
						|
    lines = [x.strip() for x in rv.data.strip().decode("utf-8").splitlines()]
 | 
						|
    sorted_by_str = [
 | 
						|
        "{",
 | 
						|
        '"values": {',
 | 
						|
        '"0": "foo",',
 | 
						|
        '"1": "foo",',
 | 
						|
        '"10": "foo",',
 | 
						|
        '"11": "foo",',
 | 
						|
        '"12": "foo",',
 | 
						|
        '"13": "foo",',
 | 
						|
        '"14": "foo",',
 | 
						|
        '"15": "foo",',
 | 
						|
        '"16": "foo",',
 | 
						|
        '"17": "foo",',
 | 
						|
        '"18": "foo",',
 | 
						|
        '"19": "foo",',
 | 
						|
        '"2": "foo",',
 | 
						|
        '"3": "foo",',
 | 
						|
        '"4": "foo",',
 | 
						|
        '"5": "foo",',
 | 
						|
        '"6": "foo",',
 | 
						|
        '"7": "foo",',
 | 
						|
        '"8": "foo",',
 | 
						|
        '"9": "foo"',
 | 
						|
        "}",
 | 
						|
        "}",
 | 
						|
    ]
 | 
						|
    sorted_by_int = [
 | 
						|
        "{",
 | 
						|
        '"values": {',
 | 
						|
        '"0": "foo",',
 | 
						|
        '"1": "foo",',
 | 
						|
        '"2": "foo",',
 | 
						|
        '"3": "foo",',
 | 
						|
        '"4": "foo",',
 | 
						|
        '"5": "foo",',
 | 
						|
        '"6": "foo",',
 | 
						|
        '"7": "foo",',
 | 
						|
        '"8": "foo",',
 | 
						|
        '"9": "foo",',
 | 
						|
        '"10": "foo",',
 | 
						|
        '"11": "foo",',
 | 
						|
        '"12": "foo",',
 | 
						|
        '"13": "foo",',
 | 
						|
        '"14": "foo",',
 | 
						|
        '"15": "foo",',
 | 
						|
        '"16": "foo",',
 | 
						|
        '"17": "foo",',
 | 
						|
        '"18": "foo",',
 | 
						|
        '"19": "foo"',
 | 
						|
        "}",
 | 
						|
        "}",
 | 
						|
    ]
 | 
						|
 | 
						|
    try:
 | 
						|
        assert lines == sorted_by_int
 | 
						|
    except AssertionError:
 | 
						|
        assert lines == sorted_by_str
 | 
						|
 | 
						|
 | 
						|
def test_html_method():
 | 
						|
    class ObjectWithHTML:
 | 
						|
        def __html__(self):
 | 
						|
            return "<p>test</p>"
 | 
						|
 | 
						|
    result = json.dumps(ObjectWithHTML())
 | 
						|
    assert result == '"<p>test</p>"'
 |