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>"'
 |