359 lines
12 KiB
Python
359 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
tests.helpers
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Various helpers.
|
|
|
|
:copyright: (c) 2015 by Armin Ronacher.
|
|
:license: BSD, see LICENSE for more details.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
import uuid
|
|
import datetime
|
|
|
|
import flask
|
|
from werkzeug.exceptions import BadRequest
|
|
from werkzeug.http import http_date
|
|
|
|
from flask import json
|
|
from flask._compat import StringIO, text_type
|
|
|
|
|
|
def has_encoding(name):
|
|
try:
|
|
import codecs
|
|
codecs.lookup(name)
|
|
return True
|
|
except LookupError:
|
|
return False
|
|
|
|
|
|
class TestJSON(object):
|
|
@pytest.mark.parametrize('value', (
|
|
1, 't', True, False, None,
|
|
[], [1, 2, 3],
|
|
{}, {'foo': u'🐍'},
|
|
))
|
|
@pytest.mark.parametrize('encoding', (
|
|
'utf-8', 'utf-8-sig',
|
|
'utf-16-le', 'utf-16-be', 'utf-16',
|
|
'utf-32-le', 'utf-32-be', 'utf-32',
|
|
))
|
|
def test_detect_encoding(self, value, encoding):
|
|
data = json.dumps(value).encode(encoding)
|
|
assert json.detect_encoding(data) == encoding
|
|
assert json.loads(data) == value
|
|
|
|
def test_ignore_cached_json(self):
|
|
app = flask.Flask(__name__)
|
|
with app.test_request_context('/', method='POST', data='malformed',
|
|
content_type='application/json'):
|
|
assert flask.request.get_json(silent=True, cache=True) is None
|
|
with pytest.raises(BadRequest):
|
|
flask.request.get_json(silent=False, cache=False)
|
|
|
|
def test_post_empty_json_adds_exception_to_response_content_in_debug(self):
|
|
app = flask.Flask(__name__)
|
|
app.config['DEBUG'] = True
|
|
@app.route('/json', methods=['POST'])
|
|
def post_json():
|
|
flask.request.get_json()
|
|
return None
|
|
c = app.test_client()
|
|
rv = c.post('/json', data=None, content_type='application/json')
|
|
assert rv.status_code == 400
|
|
assert b'Failed to decode JSON object' in rv.data
|
|
|
|
def test_post_empty_json_wont_add_exception_to_response_if_no_debug(self):
|
|
app = flask.Flask(__name__)
|
|
app.config['DEBUG'] = False
|
|
@app.route('/json', methods=['POST'])
|
|
def post_json():
|
|
flask.request.get_json()
|
|
return None
|
|
c = app.test_client()
|
|
rv = c.post('/json', data=None, content_type='application/json')
|
|
assert rv.status_code == 400
|
|
assert b'Failed to decode JSON object' not in rv.data
|
|
|
|
def test_json_bad_requests(self):
|
|
app = flask.Flask(__name__)
|
|
@app.route('/json', methods=['POST'])
|
|
def return_json():
|
|
return flask.jsonify(foo=text_type(flask.request.get_json()))
|
|
c = app.test_client()
|
|
rv = c.post('/json', data='malformed', content_type='application/json')
|
|
assert rv.status_code == 400
|
|
|
|
def test_json_custom_mimetypes(self):
|
|
app = flask.Flask(__name__)
|
|
@app.route('/json', methods=['POST'])
|
|
def return_json():
|
|
return flask.request.get_json()
|
|
c = app.test_client()
|
|
rv = c.post('/json', data='"foo"', content_type='application/x+json')
|
|
assert rv.data == b'foo'
|
|
|
|
def test_json_as_unicode(self):
|
|
app = flask.Flask(__name__)
|
|
|
|
app.config['JSON_AS_ASCII'] = True
|
|
with app.app_context():
|
|
rv = flask.json.dumps(u'\N{SNOWMAN}')
|
|
assert rv == '"\\u2603"'
|
|
|
|
app.config['JSON_AS_ASCII'] = False
|
|
with app.app_context():
|
|
rv = flask.json.dumps(u'\N{SNOWMAN}')
|
|
assert rv == u'"\u2603"'
|
|
|
|
def test_json_dump_to_file(self):
|
|
app = flask.Flask(__name__)
|
|
test_data = {'name': 'Flask'}
|
|
out = StringIO()
|
|
|
|
with app.app_context():
|
|
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(self, test_value):
|
|
"""Test jsonify with basic types."""
|
|
app = flask.Flask(__name__)
|
|
c = app.test_client()
|
|
|
|
url = '/jsonify_basic_types'
|
|
app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x))
|
|
rv = c.get(url)
|
|
assert rv.mimetype == 'application/json'
|
|
assert flask.json.loads(rv.data) == test_value
|
|
|
|
def test_jsonify_dicts(self):
|
|
"""Test jsonify with dicts and kwargs unpacking."""
|
|
d = dict(
|
|
a=0, b=23, c=3.14, d='t', e='Hi', f=True, g=False,
|
|
h=['test list', 10, False],
|
|
i={'test':'dict'}
|
|
)
|
|
app = flask.Flask(__name__)
|
|
@app.route('/kw')
|
|
def return_kwargs():
|
|
return flask.jsonify(**d)
|
|
@app.route('/dict')
|
|
def return_dict():
|
|
return flask.jsonify(d)
|
|
c = app.test_client()
|
|
for url in '/kw', '/dict':
|
|
rv = c.get(url)
|
|
assert rv.mimetype == 'application/json'
|
|
assert flask.json.loads(rv.data) == d
|
|
|
|
def test_jsonify_arrays(self):
|
|
"""Test jsonify of lists and args unpacking."""
|
|
l = [
|
|
0, 42, 3.14, 't', 'hello', True, False,
|
|
['test list', 2, False],
|
|
{'test':'dict'}
|
|
]
|
|
app = flask.Flask(__name__)
|
|
@app.route('/args_unpack')
|
|
def return_args_unpack():
|
|
return flask.jsonify(*l)
|
|
@app.route('/array')
|
|
def return_array():
|
|
return flask.jsonify(l)
|
|
c = app.test_client()
|
|
for url in '/args_unpack', '/array':
|
|
rv = c.get(url)
|
|
assert rv.mimetype == 'application/json'
|
|
assert flask.json.loads(rv.data) == l
|
|
|
|
def test_jsonify_date_types(self):
|
|
"""Test jsonify with datetime.date and datetime.datetime types."""
|
|
test_dates = (
|
|
datetime.datetime(1973, 3, 11, 6, 30, 45),
|
|
datetime.date(1975, 1, 5)
|
|
)
|
|
app = flask.Flask(__name__)
|
|
c = app.test_client()
|
|
|
|
for i, d in enumerate(test_dates):
|
|
url = '/datetest{0}'.format(i)
|
|
app.add_url_rule(url, str(i), lambda val=d: flask.jsonify(x=val))
|
|
rv = c.get(url)
|
|
assert rv.mimetype == 'application/json'
|
|
assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple())
|
|
|
|
def test_jsonify_uuid_types(self):
|
|
"""Test jsonify with uuid.UUID types"""
|
|
|
|
test_uuid = uuid.UUID(bytes=b'\xDE\xAD\xBE\xEF' * 4)
|
|
app = flask.Flask(__name__)
|
|
url = '/uuid_test'
|
|
app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid))
|
|
|
|
c = app.test_client()
|
|
rv = c.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_attr(self):
|
|
app = flask.Flask(__name__)
|
|
@app.route('/add', methods=['POST'])
|
|
def add():
|
|
json = flask.request.get_json()
|
|
return text_type(json['a'] + json['b'])
|
|
c = app.test_client()
|
|
rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
|
|
content_type='application/json')
|
|
assert rv.data == b'3'
|
|
|
|
def test_template_escaping(self):
|
|
app = flask.Flask(__name__)
|
|
render = flask.render_template_string
|
|
with app.test_request_context():
|
|
rv = flask.json.htmlsafe_dumps('</script>')
|
|
assert rv == u'"\\u003c/script\\u003e"'
|
|
assert type(rv) == text_type
|
|
rv = render('{{ "</script>"|tojson }}')
|
|
assert rv == '"\\u003c/script\\u003e"'
|
|
rv = render('{{ "<\0/script>"|tojson }}')
|
|
assert rv == '"\\u003c\\u0000/script\\u003e"'
|
|
rv = render('{{ "<!--<script>"|tojson }}')
|
|
assert rv == '"\\u003c!--\\u003cscript\\u003e"'
|
|
rv = render('{{ "&"|tojson }}')
|
|
assert rv == '"\\u0026"'
|
|
rv = render('{{ "\'"|tojson }}')
|
|
assert rv == '"\\u0027"'
|
|
rv = render("<a ng-data='{{ data|tojson }}'></a>",
|
|
data={'x': ["foo", "bar", "baz'"]})
|
|
assert rv == '<a ng-data=\'{"x": ["foo", "bar", "baz\\u0027"]}\'></a>'
|
|
|
|
def test_json_customization(self):
|
|
class X(object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
class MyEncoder(flask.json.JSONEncoder):
|
|
def default(self, o):
|
|
if isinstance(o, X):
|
|
return '<%d>' % o.val
|
|
return flask.json.JSONEncoder.default(self, o)
|
|
class MyDecoder(flask.json.JSONDecoder):
|
|
def __init__(self, *args, **kwargs):
|
|
kwargs.setdefault('object_hook', self.object_hook)
|
|
flask.json.JSONDecoder.__init__(self, *args, **kwargs)
|
|
def object_hook(self, obj):
|
|
if len(obj) == 1 and '_foo' in obj:
|
|
return X(obj['_foo'])
|
|
return obj
|
|
app = flask.Flask(__name__)
|
|
app.testing = True
|
|
app.json_encoder = MyEncoder
|
|
app.json_decoder = MyDecoder
|
|
@app.route('/', methods=['POST'])
|
|
def index():
|
|
return flask.json.dumps(flask.request.get_json()['x'])
|
|
c = app.test_client()
|
|
rv = c.post('/', data=flask.json.dumps({
|
|
'x': {'_foo': 42}
|
|
}), content_type='application/json')
|
|
assert rv.data == b'"<42>"'
|
|
|
|
def test_modified_url_encoding(self):
|
|
class ModifiedRequest(flask.Request):
|
|
url_charset = 'euc-kr'
|
|
app = flask.Flask(__name__)
|
|
app.testing = True
|
|
app.request_class = ModifiedRequest
|
|
app.url_map.charset = 'euc-kr'
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return flask.request.args['foo']
|
|
|
|
rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
|
|
assert rv.status_code == 200
|
|
assert rv.data == u'정상처리'.encode('utf-8')
|
|
|
|
if not has_encoding('euc-kr'):
|
|
test_modified_url_encoding = None
|
|
|
|
def test_json_key_sorting(self):
|
|
app = flask.Flask(__name__)
|
|
app.testing = True
|
|
assert app.config['JSON_SORT_KEYS'] == True
|
|
d = dict.fromkeys(range(20), 'foo')
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return flask.jsonify(values=d)
|
|
|
|
c = app.test_client()
|
|
rv = c.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
|