diff --git a/tests/test_basic.py b/tests/test_basic.py index 3412b9ab..c66e8eac 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -16,1351 +16,1493 @@ import uuid import time import flask import pickle -import unittest from datetime import datetime from threading import Thread -from tests import emits_module_deprecation_warning from flask._compat import text_type from werkzeug.exceptions import BadRequest, NotFound, Forbidden from werkzeug.http import parse_date from werkzeug.routing import BuildError -class TestBasicFunctionality(object): +def test_options_work(): + app = flask.Flask(__name__) - def test_options_work(self): - app = flask.Flask(__name__) - @app.route('/', methods=['GET', 'POST']) - def index(): - return 'Hello World' - rv = app.test_client().open('/', method='OPTIONS') - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - assert rv.data == b'' + @app.route('/', methods=['GET', 'POST']) + def index(): + return 'Hello World' + rv = app.test_client().open('/', method='OPTIONS') + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] + assert rv.data == b'' - def test_options_on_multiple_rules(self): - app = flask.Flask(__name__) - @app.route('/', methods=['GET', 'POST']) - def index(): - return 'Hello World' - @app.route('/', methods=['PUT']) - def index_put(): - return 'Aha!' - rv = app.test_client().open('/', method='OPTIONS') - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] - - def test_options_handling_disabled(self): - app = flask.Flask(__name__) - def index(): - return 'Hello World!' - index.provide_automatic_options = False - app.route('/')(index) - rv = app.test_client().open('/', method='OPTIONS') - assert rv.status_code == 405 - app = flask.Flask(__name__) - def index2(): - return 'Hello World!' - index2.provide_automatic_options = True - app.route('/', methods=['OPTIONS'])(index2) - rv = app.test_client().open('/', method='OPTIONS') - assert sorted(rv.allow) == ['OPTIONS'] - - def test_request_dispatching(self): - app = flask.Flask(__name__) - @app.route('/') - def index(): - return flask.request.method - @app.route('/more', methods=['GET', 'POST']) - def more(): - return flask.request.method +def test_options_on_multiple_rules(): + app = flask.Flask(__name__) - c = app.test_client() - assert c.get('/').data == b'GET' - rv = c.post('/') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = c.head('/') - assert rv.status_code == 200 - assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' - rv = c.delete('/more') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - - def test_disallow_string_for_allowed_methods(self): - app = flask.Flask(__name__) - with pytest.raises(TypeError): - @app.route('/', methods='GET POST') - def index(): - return "Hey" + @app.route('/', methods=['GET', 'POST']) + def index(): + return 'Hello World' - def test_url_mapping(self): - app = flask.Flask(__name__) - def index(): - return flask.request.method - def more(): - return flask.request.method + @app.route('/', methods=['PUT']) + def index_put(): + return 'Aha!' + rv = app.test_client().open('/', method='OPTIONS') + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] - app.add_url_rule('/', 'index', index) - app.add_url_rule('/more', 'more', more, methods=['GET', 'POST']) - c = app.test_client() - assert c.get('/').data == b'GET' - rv = c.post('/') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = c.head('/') - assert rv.status_code == 200 - assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' - rv = c.delete('/more') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - - def test_werkzeug_routing(self): - from werkzeug.routing import Submount, Rule - app = flask.Flask(__name__) - app.url_map.add(Submount('/foo', [ - Rule('/bar', endpoint='bar'), - Rule('/', endpoint='index') - ])) - def bar(): - return 'bar' - def index(): - return 'index' - app.view_functions['bar'] = bar - app.view_functions['index'] = index +def test_options_handling_disabled(): + app = flask.Flask(__name__) - c = app.test_client() - assert c.get('/foo/').data == b'index' - assert c.get('/foo/bar').data == b'bar' + def index(): + return 'Hello World!' + index.provide_automatic_options = False + app.route('/')(index) + rv = app.test_client().open('/', method='OPTIONS') + assert rv.status_code == 405 - def test_endpoint_decorator(self): - from werkzeug.routing import Submount, Rule - app = flask.Flask(__name__) - app.url_map.add(Submount('/foo', [ - Rule('/bar', endpoint='bar'), - Rule('/', endpoint='index') - ])) + app = flask.Flask(__name__) - @app.endpoint('bar') - def bar(): - return 'bar' + def index2(): + return 'Hello World!' + index2.provide_automatic_options = True + app.route('/', methods=['OPTIONS'])(index2) + rv = app.test_client().open('/', method='OPTIONS') + assert sorted(rv.allow) == ['OPTIONS'] - @app.endpoint('index') - def index(): - return 'index' - c = app.test_client() - assert c.get('/foo/').data == b'index' - assert c.get('/foo/bar').data == b'bar' +def test_request_dispatching(): + app = flask.Flask(__name__) - def test_session(self): - app = flask.Flask(__name__) - app.secret_key = 'testkey' - @app.route('/set', methods=['POST']) - def set(): - flask.session['value'] = flask.request.form['value'] - return 'value set' - @app.route('/get') - def get(): - return flask.session['value'] + @app.route('/') + def index(): + return flask.request.method - c = app.test_client() - assert c.post('/set', data={'value': '42'}).data == b'value set' - assert c.get('/get').data == b'42' + @app.route('/more', methods=['GET', 'POST']) + def more(): + return flask.request.method - def test_session_using_server_name(self): - app = flask.Flask(__name__) - app.config.update( - SECRET_KEY='foo', - SERVER_NAME='example.com' - ) - @app.route('/') - def index(): - flask.session['testing'] = 42 - return 'Hello World' - rv = app.test_client().get('/', 'http://example.com/') - assert 'domain=.example.com' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() + c = app.test_client() + assert c.get('/').data == b'GET' + rv = c.post('/') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] + rv = c.head('/') + assert rv.status_code == 200 + assert not rv.data # head truncates + assert c.post('/more').data == b'POST' + assert c.get('/more').data == b'GET' + rv = c.delete('/more') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - def test_session_using_server_name_and_port(self): - app = flask.Flask(__name__) - app.config.update( - SECRET_KEY='foo', - SERVER_NAME='example.com:8080' - ) - @app.route('/') - def index(): - flask.session['testing'] = 42 - return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/') - assert 'domain=.example.com' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() - def test_session_using_server_name_port_and_path(self): - app = flask.Flask(__name__) - app.config.update( - SECRET_KEY='foo', - SERVER_NAME='example.com:8080', - APPLICATION_ROOT='/foo' - ) - @app.route('/') +def test_disallow_string_for_allowed_methods(): + app = flask.Flask(__name__) + with pytest.raises(TypeError): + @app.route('/', methods='GET POST') def index(): - flask.session['testing'] = 42 - return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/foo') - assert 'domain=example.com' in rv.headers['set-cookie'].lower() - assert 'path=/foo' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() - - def test_session_using_application_root(self): - class PrefixPathMiddleware(object): - def __init__(self, app, prefix): - self.app = app - self.prefix = prefix - def __call__(self, environ, start_response): - environ['SCRIPT_NAME'] = self.prefix - return self.app(environ, start_response) + return "Hey" + + +def test_url_mapping(): + app = flask.Flask(__name__) - app = flask.Flask(__name__) - app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar') - app.config.update( - SECRET_KEY='foo', - APPLICATION_ROOT='/bar' - ) - @app.route('/') - def index(): - flask.session['testing'] = 42 - return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/') - assert 'path=/bar' in rv.headers['set-cookie'].lower() + def index(): + return flask.request.method + + def more(): + return flask.request.method + + app.add_url_rule('/', 'index', index) + app.add_url_rule('/more', 'more', more, methods=['GET', 'POST']) + + c = app.test_client() + assert c.get('/').data == b'GET' + rv = c.post('/') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] + rv = c.head('/') + assert rv.status_code == 200 + assert not rv.data # head truncates + assert c.post('/more').data == b'POST' + assert c.get('/more').data == b'GET' + rv = c.delete('/more') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] + + +def test_werkzeug_routing(): + from werkzeug.routing import Submount, Rule + app = flask.Flask(__name__) + app.url_map.add(Submount('/foo', [ + Rule('/bar', endpoint='bar'), + Rule('/', endpoint='index') + ])) + + def bar(): + return 'bar' + + def index(): + return 'index' + app.view_functions['bar'] = bar + app.view_functions['index'] = index + + c = app.test_client() + assert c.get('/foo/').data == b'index' + assert c.get('/foo/bar').data == b'bar' + + +def test_endpoint_decorator(): + from werkzeug.routing import Submount, Rule + app = flask.Flask(__name__) + app.url_map.add(Submount('/foo', [ + Rule('/bar', endpoint='bar'), + Rule('/', endpoint='index') + ])) + + @app.endpoint('bar') + def bar(): + return 'bar' + + @app.endpoint('index') + def index(): + return 'index' + + c = app.test_client() + assert c.get('/foo/').data == b'index' + assert c.get('/foo/bar').data == b'bar' + + +def test_session(): + app = flask.Flask(__name__) + app.secret_key = 'testkey' + + @app.route('/set', methods=['POST']) + def set(): + flask.session['value'] = flask.request.form['value'] + return 'value set' + + @app.route('/get') + def get(): + return flask.session['value'] + + c = app.test_client() + assert c.post('/set', data={'value': '42'}).data == b'value set' + assert c.get('/get').data == b'42' + + +def test_session_using_server_name(): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='foo', + SERVER_NAME='example.com' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + rv = app.test_client().get('/', 'http://example.com/') + assert 'domain=.example.com' in rv.headers['set-cookie'].lower() + assert 'httponly' in rv.headers['set-cookie'].lower() + + +def test_session_using_server_name_and_port(): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='foo', + SERVER_NAME='example.com:8080' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + rv = app.test_client().get('/', 'http://example.com:8080/') + assert 'domain=.example.com' in rv.headers['set-cookie'].lower() + assert 'httponly' in rv.headers['set-cookie'].lower() + + +def test_session_using_server_name_port_and_path(): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='foo', + SERVER_NAME='example.com:8080', + APPLICATION_ROOT='/foo' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + rv = app.test_client().get('/', 'http://example.com:8080/foo') + assert 'domain=example.com' in rv.headers['set-cookie'].lower() + assert 'path=/foo' in rv.headers['set-cookie'].lower() + assert 'httponly' in rv.headers['set-cookie'].lower() + + +def test_session_using_application_root(): + class PrefixPathMiddleware(object): + + def __init__(self, app, prefix): + self.app = app + self.prefix = prefix + + def __call__(self, environ, start_response): + environ['SCRIPT_NAME'] = self.prefix + return self.app(environ, start_response) + + app = flask.Flask(__name__) + app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar') + app.config.update( + SECRET_KEY='foo', + APPLICATION_ROOT='/bar' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + rv = app.test_client().get('/', 'http://example.com:8080/') + assert 'path=/bar' in rv.headers['set-cookie'].lower() + + +def test_session_using_session_settings(): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='foo', + SERVER_NAME='www.example.com:8080', + APPLICATION_ROOT='/test', + SESSION_COOKIE_DOMAIN='.example.com', + SESSION_COOKIE_HTTPONLY=False, + SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_PATH='/' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + rv = app.test_client().get('/', 'http://www.example.com:8080/test/') + cookie = rv.headers['set-cookie'].lower() + assert 'domain=.example.com' in cookie + assert 'path=/' in cookie + assert 'secure' in cookie + assert 'httponly' not in cookie + + +def test_missing_session(): + app = flask.Flask(__name__) + + def expect_exception(f, *args, **kwargs): + try: + f(*args, **kwargs) + except RuntimeError as e: + assert e.args and 'session is unavailable' in e.args[0] + else: + assert False, 'expected exception' + with app.test_request_context(): + assert flask.session.get('missing_key') is None + expect_exception(flask.session.__setitem__, 'foo', 42) + expect_exception(flask.session.pop, 'foo') + + +def test_session_expiration(): + permanent = True + app = flask.Flask(__name__) + app.secret_key = 'testkey' + + @app.route('/') + def index(): + flask.session['test'] = 42 + flask.session.permanent = permanent + return '' + + @app.route('/test') + def test(): + return text_type(flask.session.permanent) + + client = app.test_client() + rv = client.get('/') + assert 'set-cookie' in rv.headers + match = re.search(r'\bexpires=([^;]+)(?i)', rv.headers['set-cookie']) + expires = parse_date(match.group()) + expected = datetime.utcnow() + app.permanent_session_lifetime + assert expires.year == expected.year + assert expires.month == expected.month + assert expires.day == expected.day + + rv = client.get('/test') + assert rv.data == b'True' + + permanent = False + rv = app.test_client().get('/') + assert 'set-cookie' in rv.headers + match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie']) + assert match is None + + +def test_session_stored_last(): + app = flask.Flask(__name__) + app.secret_key = 'development-key' + app.testing = True + + @app.after_request + def modify_session(response): + flask.session['foo'] = 42 + return response + + @app.route('/') + def dump_session_contents(): + return repr(flask.session.get('foo')) + + c = app.test_client() + assert c.get('/').data == b'None' + assert c.get('/').data == b'42' + + +def test_session_special_types(): + app = flask.Flask(__name__) + app.secret_key = 'development-key' + app.testing = True + now = datetime.utcnow().replace(microsecond=0) + the_uuid = uuid.uuid4() + + @app.after_request + def modify_session(response): + flask.session['m'] = flask.Markup('Hello!') + flask.session['u'] = the_uuid + flask.session['dt'] = now + flask.session['b'] = b'\xff' + flask.session['t'] = (1, 2, 3) + return response + + @app.route('/') + def dump_session_contents(): + return pickle.dumps(dict(flask.session)) + + c = app.test_client() + c.get('/') + rv = pickle.loads(c.get('/').data) + assert rv['m'] == flask.Markup('Hello!') + assert type(rv['m']) == flask.Markup + assert rv['dt'] == now + assert rv['u'] == the_uuid + assert rv['b'] == b'\xff' + assert type(rv['b']) == bytes + assert rv['t'] == (1, 2, 3) + + +def test_session_cookie_setting(): + app = flask.Flask(__name__) + app.testing = True + app.secret_key = 'dev key' + is_permanent = True + + @app.route('/bump') + def bump(): + rv = flask.session['foo'] = flask.session.get('foo', 0) + 1 + flask.session.permanent = is_permanent + return str(rv) + + @app.route('/read') + def read(): + return str(flask.session.get('foo', 0)) + + def run_test(expect_header): + with app.test_client() as c: + assert c.get('/bump').data == b'1' + assert c.get('/bump').data == b'2' + assert c.get('/bump').data == b'3' + + rv = c.get('/read') + set_cookie = rv.headers.get('set-cookie') + assert (set_cookie is not None) == expect_header + assert rv.data == b'3' + + is_permanent = True + app.config['SESSION_REFRESH_EACH_REQUEST'] = True + run_test(expect_header=True) + + is_permanent = True + app.config['SESSION_REFRESH_EACH_REQUEST'] = False + run_test(expect_header=False) + + is_permanent = False + app.config['SESSION_REFRESH_EACH_REQUEST'] = True + run_test(expect_header=False) + + is_permanent = False + app.config['SESSION_REFRESH_EACH_REQUEST'] = False + run_test(expect_header=False) + + +def test_flashes(): + app = flask.Flask(__name__) + app.secret_key = 'testkey' + + with app.test_request_context(): + assert not flask.session.modified + flask.flash('Zap') + flask.session.modified = False + flask.flash('Zip') + assert flask.session.modified + assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] + + +def test_extended_flashing(): + # Be sure app.testing=True below, else tests can fail silently. + # + # Specifically, if app.testing is not set to True, the AssertionErrors + # in the view functions will cause a 500 response to the test client + # instead of propagating exceptions. + + app = flask.Flask(__name__) + app.secret_key = 'testkey' + app.testing = True + + @app.route('/') + def index(): + flask.flash(u'Hello World') + flask.flash(u'Hello World', 'error') + flask.flash(flask.Markup(u'Testing'), 'warning') + return '' + + @app.route('/test/') + def test(): + messages = flask.get_flashed_messages() + assert list(messages) == [ + u'Hello World', + u'Hello World', + flask.Markup(u'Testing') + ] + return '' + + @app.route('/test_with_categories/') + def test_with_categories(): + messages = flask.get_flashed_messages(with_categories=True) + assert len(messages) == 3 + assert list(messages) == [ + ('message', u'Hello World'), + ('error', u'Hello World'), + ('warning', flask.Markup(u'Testing')) + ] + return '' + + @app.route('/test_filter/') + def test_filter(): + messages = flask.get_flashed_messages( + category_filter=['message'], with_categories=True) + assert list(messages) == [('message', u'Hello World')] + return '' + + @app.route('/test_filters/') + def test_filters(): + messages = flask.get_flashed_messages( + category_filter=['message', 'warning'], with_categories=True) + assert list(messages) == [ + ('message', u'Hello World'), + ('warning', flask.Markup(u'Testing')) + ] + return '' + + @app.route('/test_filters_without_returning_categories/') + def test_filters2(): + messages = flask.get_flashed_messages( + category_filter=['message', 'warning']) + assert len(messages) == 2 + assert messages[0] == u'Hello World' + assert messages[1] == flask.Markup(u'Testing') + return '' + + # Create new test client on each test to clean flashed messages. + + c = app.test_client() + c.get('/') + c.get('/test/') + + c = app.test_client() + c.get('/') + c.get('/test_with_categories/') + + c = app.test_client() + c.get('/') + c.get('/test_filter/') + + c = app.test_client() + c.get('/') + c.get('/test_filters/') + + c = app.test_client() + c.get('/') + c.get('/test_filters_without_returning_categories/') + + +def test_request_processing(): + app = flask.Flask(__name__) + evts = [] + + @app.before_request + def before_request(): + evts.append('before') + + @app.after_request + def after_request(response): + response.data += b'|after' + evts.append('after') + return response + + @app.route('/') + def index(): + assert 'before' in evts + assert 'after' not in evts + return 'request' + assert 'after' not in evts + rv = app.test_client().get('/').data + assert 'after' in evts + assert rv == b'request|after' + + +def test_after_request_processing(): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/') + def index(): + @flask.after_this_request + def foo(response): + response.headers['X-Foo'] = 'a header' + return response + return 'Test' + c = app.test_client() + resp = c.get('/') + assert resp.status_code == 200 + assert resp.headers['X-Foo'] == 'a header' + + +def test_teardown_request_handler(): + called = [] + app = flask.Flask(__name__) + + @app.teardown_request + def teardown_request(exc): + called.append(True) + return "Ignored" + + @app.route('/') + def root(): + return "Response" + rv = app.test_client().get('/') + assert rv.status_code == 200 + assert b'Response' in rv.data + assert len(called) == 1 + + +def test_teardown_request_handler_debug_mode(): + called = [] + app = flask.Flask(__name__) + app.testing = True + + @app.teardown_request + def teardown_request(exc): + called.append(True) + return "Ignored" + + @app.route('/') + def root(): + return "Response" + rv = app.test_client().get('/') + assert rv.status_code == 200 + assert b'Response' in rv.data + assert len(called) == 1 + + +def test_teardown_request_handler_error(): + called = [] + app = flask.Flask(__name__) + app.config['LOGGER_HANDLER_POLICY'] = 'never' + + @app.teardown_request + def teardown_request1(exc): + assert type(exc) == ZeroDivisionError + called.append(True) + # This raises a new error and blows away sys.exc_info(), so we can + # test that all teardown_requests get passed the same original + # exception. + try: + raise TypeError() + except: + pass - def test_session_using_session_settings(self): - app = flask.Flask(__name__) - app.config.update( - SECRET_KEY='foo', - SERVER_NAME='www.example.com:8080', - APPLICATION_ROOT='/test', - SESSION_COOKIE_DOMAIN='.example.com', - SESSION_COOKIE_HTTPONLY=False, - SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_PATH='/' - ) - @app.route('/') - def index(): - flask.session['testing'] = 42 - return 'Hello World' - rv = app.test_client().get('/', 'http://www.example.com:8080/test/') - cookie = rv.headers['set-cookie'].lower() - assert 'domain=.example.com' in cookie - assert 'path=/' in cookie - assert 'secure' in cookie - assert 'httponly' not in cookie - - def test_missing_session(self): - app = flask.Flask(__name__) - def expect_exception(f, *args, **kwargs): - try: - f(*args, **kwargs) - except RuntimeError as e: - assert e.args and 'session is unavailable' in e.args[0] - else: - assert False, 'expected exception' - with app.test_request_context(): - assert flask.session.get('missing_key') is None - expect_exception(flask.session.__setitem__, 'foo', 42) - expect_exception(flask.session.pop, 'foo') + @app.teardown_request + def teardown_request2(exc): + assert type(exc) == ZeroDivisionError + called.append(True) + # This raises a new error and blows away sys.exc_info(), so we can + # test that all teardown_requests get passed the same original + # exception. + try: + raise TypeError() + except: + pass - def test_session_expiration(self): - permanent = True - app = flask.Flask(__name__) - app.secret_key = 'testkey' - @app.route('/') - def index(): - flask.session['test'] = 42 - flask.session.permanent = permanent - return '' - - @app.route('/test') - def test(): - return text_type(flask.session.permanent) - - client = app.test_client() - rv = client.get('/') - assert 'set-cookie' in rv.headers - match = re.search(r'\bexpires=([^;]+)(?i)', rv.headers['set-cookie']) - expires = parse_date(match.group()) - expected = datetime.utcnow() + app.permanent_session_lifetime - assert expires.year == expected.year - assert expires.month == expected.month - assert expires.day == expected.day - - rv = client.get('/test') - assert rv.data == b'True' - - permanent = False - rv = app.test_client().get('/') - assert 'set-cookie' in rv.headers - match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie']) - assert match is None - - def test_session_stored_last(self): - app = flask.Flask(__name__) - app.secret_key = 'development-key' - app.testing = True + @app.route('/') + def fails(): + 1 // 0 + rv = app.test_client().get('/') + assert rv.status_code == 500 + assert b'Internal Server Error' in rv.data + assert len(called) == 2 + + +def test_before_after_request_order(): + called = [] + app = flask.Flask(__name__) + + @app.before_request + def before1(): + called.append(1) + + @app.before_request + def before2(): + called.append(2) - @app.after_request - def modify_session(response): - flask.session['foo'] = 42 - return response - @app.route('/') - def dump_session_contents(): - return repr(flask.session.get('foo')) + @app.after_request + def after1(response): + called.append(4) + return response - c = app.test_client() - assert c.get('/').data == b'None' - assert c.get('/').data == b'42' + @app.after_request + def after2(response): + called.append(3) + return response - def test_session_special_types(self): - app = flask.Flask(__name__) - app.secret_key = 'development-key' - app.testing = True - now = datetime.utcnow().replace(microsecond=0) - the_uuid = uuid.uuid4() - - @app.after_request - def modify_session(response): - flask.session['m'] = flask.Markup('Hello!') - flask.session['u'] = the_uuid - flask.session['dt'] = now - flask.session['b'] = b'\xff' - flask.session['t'] = (1, 2, 3) - return response + @app.teardown_request + def finish1(exc): + called.append(6) + + @app.teardown_request + def finish2(exc): + called.append(5) + + @app.route('/') + def index(): + return '42' + rv = app.test_client().get('/') + assert rv.data == b'42' + assert called == [1, 2, 3, 4, 5, 6] - @app.route('/') - def dump_session_contents(): - return pickle.dumps(dict(flask.session)) - c = app.test_client() - c.get('/') - rv = pickle.loads(c.get('/').data) - assert rv['m'] == flask.Markup('Hello!') - assert type(rv['m']) == flask.Markup - assert rv['dt'] == now - assert rv['u'] == the_uuid - assert rv['b'] == b'\xff' - assert type(rv['b']) == bytes - assert rv['t'] == (1, 2, 3) - - def test_session_cookie_setting(self): - app = flask.Flask(__name__) - app.testing = True - app.secret_key = 'dev key' - is_permanent = True - - @app.route('/bump') - def bump(): - rv = flask.session['foo'] = flask.session.get('foo', 0) + 1 - flask.session.permanent = is_permanent - return str(rv) - - @app.route('/read') - def read(): - return str(flask.session.get('foo', 0)) - - def run_test(expect_header): - with app.test_client() as c: - assert c.get('/bump').data == b'1' - assert c.get('/bump').data == b'2' - assert c.get('/bump').data == b'3' - - rv = c.get('/read') - set_cookie = rv.headers.get('set-cookie') - assert (set_cookie is not None) == expect_header - assert rv.data == b'3' - - is_permanent = True - app.config['SESSION_REFRESH_EACH_REQUEST'] = True - run_test(expect_header=True) - - is_permanent = True - app.config['SESSION_REFRESH_EACH_REQUEST'] = False - run_test(expect_header=False) - - is_permanent = False - app.config['SESSION_REFRESH_EACH_REQUEST'] = True - run_test(expect_header=False) - - is_permanent = False - app.config['SESSION_REFRESH_EACH_REQUEST'] = False - run_test(expect_header=False) - - def test_flashes(self): - app = flask.Flask(__name__) - app.secret_key = 'testkey' +def test_error_handling(): + app = flask.Flask(__name__) + app.config['LOGGER_HANDLER_POLICY'] = 'never' + + @app.errorhandler(404) + def not_found(e): + return 'not found', 404 + + @app.errorhandler(500) + def internal_server_error(e): + return 'internal server error', 500 - with app.test_request_context(): - assert not flask.session.modified - flask.flash('Zap') - flask.session.modified = False - flask.flash('Zip') - assert flask.session.modified - assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] - - def test_extended_flashing(self): - # Be sure app.testing=True below, else tests can fail silently. - # - # Specifically, if app.testing is not set to True, the AssertionErrors - # in the view functions will cause a 500 response to the test client - # instead of propagating exceptions. + @app.errorhandler(Forbidden) + def forbidden(e): + return 'forbidden', 403 + + @app.route('/') + def index(): + flask.abort(404) + + @app.route('/error') + def error(): + 1 // 0 + + @app.route('/forbidden') + def error2(): + flask.abort(403) + c = app.test_client() + rv = c.get('/') + assert rv.status_code == 404 + assert rv.data == b'not found' + rv = c.get('/error') + assert rv.status_code == 500 + assert b'internal server error' == rv.data + rv = c.get('/forbidden') + assert rv.status_code == 403 + assert b'forbidden' == rv.data + + +def test_before_request_and_routing_errors(): + app = flask.Flask(__name__) - app = flask.Flask(__name__) - app.secret_key = 'testkey' - app.testing = True + @app.before_request + def attach_something(): + flask.g.something = 'value' + + @app.errorhandler(404) + def return_something(error): + return flask.g.something, 404 + rv = app.test_client().get('/') + assert rv.status_code == 404 + assert rv.data == b'value' + + +def test_user_error_handling(): + class MyException(Exception): + pass + + app = flask.Flask(__name__) + + @app.errorhandler(MyException) + def handle_my_exception(e): + assert isinstance(e, MyException) + return '42' + + @app.route('/') + def index(): + raise MyException() + + c = app.test_client() + assert c.get('/').data == b'42' + + +def test_http_error_subclass_handling(): + class ForbiddenSubclass(Forbidden): + pass + + app = flask.Flask(__name__) + + @app.errorhandler(ForbiddenSubclass) + def handle_forbidden_subclass(e): + assert isinstance(e, ForbiddenSubclass) + return 'banana' + + @app.errorhandler(403) + def handle_forbidden_subclass(e): + assert not isinstance(e, ForbiddenSubclass) + assert isinstance(e, Forbidden) + return 'apple' + + @app.route('/1') + def index1(): + raise ForbiddenSubclass() + + @app.route('/2') + def index2(): + flask.abort(403) - @app.route('/') - def index(): - flask.flash(u'Hello World') - flask.flash(u'Hello World', 'error') - flask.flash(flask.Markup(u'Testing'), 'warning') - return '' - - @app.route('/test/') - def test(): - messages = flask.get_flashed_messages() - assert list(messages) == [ - u'Hello World', - u'Hello World', - flask.Markup(u'Testing') - ] - return '' - - @app.route('/test_with_categories/') - def test_with_categories(): - messages = flask.get_flashed_messages(with_categories=True) - assert len(messages) == 3 - assert list(messages) == [ - ('message', u'Hello World'), - ('error', u'Hello World'), - ('warning', flask.Markup(u'Testing')) - ] - return '' - - @app.route('/test_filter/') - def test_filter(): - messages = flask.get_flashed_messages(category_filter=['message'], with_categories=True) - assert list(messages) == [('message', u'Hello World')] - return '' - - @app.route('/test_filters/') - def test_filters(): - messages = flask.get_flashed_messages(category_filter=['message', 'warning'], with_categories=True) - assert list(messages) == [ - ('message', u'Hello World'), - ('warning', flask.Markup(u'Testing')) - ] - return '' - - @app.route('/test_filters_without_returning_categories/') - def test_filters2(): - messages = flask.get_flashed_messages(category_filter=['message', 'warning']) - assert len(messages) == 2 - assert messages[0] == u'Hello World' - assert messages[1] == flask.Markup(u'Testing') - return '' - - # Create new test client on each test to clean flashed messages. + @app.route('/3') + def index3(): + raise Forbidden() + + c = app.test_client() + assert c.get('/1').data == b'banana' + assert c.get('/2').data == b'apple' + assert c.get('/3').data == b'apple' + + +def test_trapping_of_bad_request_key_errors(): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/fail') + def fail(): + flask.request.form['missing_key'] + c = app.test_client() + assert c.get('/fail').status_code == 400 + + app.config['TRAP_BAD_REQUEST_ERRORS'] = True + c = app.test_client() + try: + c.get('/fail') + except KeyError as e: + assert isinstance(e, BadRequest) + else: + assert False, 'Expected exception' + + +def test_trapping_of_all_http_exceptions(): + app = flask.Flask(__name__) + app.testing = True + app.config['TRAP_HTTP_EXCEPTIONS'] = True - c = app.test_client() - c.get('/') - c.get('/test/') + @app.route('/fail') + def fail(): + flask.abort(404) - c = app.test_client() - c.get('/') - c.get('/test_with_categories/') + c = app.test_client() + with pytest.raises(NotFound): + c.get('/fail') - c = app.test_client() - c.get('/') - c.get('/test_filter/') - c = app.test_client() - c.get('/') - c.get('/test_filters/') +def test_enctype_debug_helper(): + from flask.debughelpers import DebugFilesKeyError + app = flask.Flask(__name__) + app.debug = True - c = app.test_client() - c.get('/') - c.get('/test_filters_without_returning_categories/') + @app.route('/fail', methods=['POST']) + def index(): + return flask.request.files['foo'].filename - def test_request_processing(self): - app = flask.Flask(__name__) - evts = [] - @app.before_request - def before_request(): - evts.append('before') - @app.after_request - def after_request(response): - response.data += b'|after' - evts.append('after') - return response - @app.route('/') - def index(): - assert 'before' in evts - assert 'after' not in evts - return 'request' - assert 'after' not in evts - rv = app.test_client().get('/').data - assert 'after' in evts - assert rv == b'request|after' + # with statement is important because we leave an exception on the + # stack otherwise and we want to ensure that this is not the case + # to not negatively affect other tests. + with app.test_client() as c: + try: + c.post('/fail', data={'foo': 'index.txt'}) + except DebugFilesKeyError as e: + assert 'no file contents were transmitted' in str(e) + assert 'This was submitted: "index.txt"' in str(e) + else: + assert False, 'Expected exception' - def test_after_request_processing(self): - app = flask.Flask(__name__) - app.testing = True - @app.route('/') - def index(): - @flask.after_this_request - def foo(response): - response.headers['X-Foo'] = 'a header' - return response - return 'Test' - c = app.test_client() - resp = c.get('/') - assert resp.status_code == 200 - assert resp.headers['X-Foo'] == 'a header' - def test_teardown_request_handler(self): - called = [] - app = flask.Flask(__name__) - @app.teardown_request - def teardown_request(exc): - called.append(True) - return "Ignored" - @app.route('/') - def root(): - return "Response" - rv = app.test_client().get('/') +def test_response_creation(): + app = flask.Flask(__name__) + + @app.route('/unicode') + def from_unicode(): + return u'Hällo Wörld' + + @app.route('/string') + def from_string(): + return u'Hällo Wörld'.encode('utf-8') + + @app.route('/args') + def from_tuple(): + return 'Meh', 400, { + 'X-Foo': 'Testing', + 'Content-Type': 'text/plain; charset=utf-8' + } + + @app.route('/two_args') + def from_two_args_tuple(): + return 'Hello', { + 'X-Foo': 'Test', + 'Content-Type': 'text/plain; charset=utf-8' + } + + @app.route('/args_status') + def from_status_tuple(): + return 'Hi, status!', 400 + + @app.route('/args_header') + def from_response_instance_status_tuple(): + return flask.Response('Hello world', 404), { + "X-Foo": "Bar", + "X-Bar": "Foo" + } + + c = app.test_client() + assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8') + assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8') + rv = c.get('/args') + assert rv.data == b'Meh' + assert rv.headers['X-Foo'] == 'Testing' + assert rv.status_code == 400 + assert rv.mimetype == 'text/plain' + rv2 = c.get('/two_args') + assert rv2.data == b'Hello' + assert rv2.headers['X-Foo'] == 'Test' + assert rv2.status_code == 200 + assert rv2.mimetype == 'text/plain' + rv3 = c.get('/args_status') + assert rv3.data == b'Hi, status!' + assert rv3.status_code == 400 + assert rv3.mimetype == 'text/html' + rv4 = c.get('/args_header') + assert rv4.data == b'Hello world' + assert rv4.headers['X-Foo'] == 'Bar' + assert rv4.headers['X-Bar'] == 'Foo' + assert rv4.status_code == 404 + + +def test_make_response(): + app = flask.Flask(__name__) + with app.test_request_context(): + rv = flask.make_response() assert rv.status_code == 200 - assert b'Response' in rv.data - assert len(called) == 1 + assert rv.data == b'' + assert rv.mimetype == 'text/html' - def test_teardown_request_handler_debug_mode(self): - called = [] - app = flask.Flask(__name__) - app.testing = True - @app.teardown_request - def teardown_request(exc): - called.append(True) - return "Ignored" - @app.route('/') - def root(): - return "Response" - rv = app.test_client().get('/') + rv = flask.make_response('Awesome') assert rv.status_code == 200 - assert b'Response' in rv.data - assert len(called) == 1 + assert rv.data == b'Awesome' + assert rv.mimetype == 'text/html' - def test_teardown_request_handler_error(self): - called = [] - app = flask.Flask(__name__) - app.config['LOGGER_HANDLER_POLICY'] = 'never' - @app.teardown_request - def teardown_request1(exc): - assert type(exc) == ZeroDivisionError - called.append(True) - # This raises a new error and blows away sys.exc_info(), so we can - # test that all teardown_requests get passed the same original - # exception. - try: - raise TypeError() - except: - pass - @app.teardown_request - def teardown_request2(exc): - assert type(exc) == ZeroDivisionError - called.append(True) - # This raises a new error and blows away sys.exc_info(), so we can - # test that all teardown_requests get passed the same original - # exception. - try: - raise TypeError() - except: - pass - @app.route('/') - def fails(): - 1 // 0 - rv = app.test_client().get('/') - assert rv.status_code == 500 - assert b'Internal Server Error' in rv.data - assert len(called) == 2 + rv = flask.make_response('W00t', 404) + assert rv.status_code == 404 + assert rv.data == b'W00t' + assert rv.mimetype == 'text/html' - def test_before_after_request_order(self): - called = [] - app = flask.Flask(__name__) - @app.before_request - def before1(): - called.append(1) - @app.before_request - def before2(): - called.append(2) - @app.after_request - def after1(response): - called.append(4) - return response - @app.after_request - def after2(response): - called.append(3) - return response - @app.teardown_request - def finish1(exc): - called.append(6) - @app.teardown_request - def finish2(exc): - called.append(5) - @app.route('/') - def index(): - return '42' - rv = app.test_client().get('/') - assert rv.data == b'42' - assert called == [1, 2, 3, 4, 5, 6] - def test_error_handling(self): - app = flask.Flask(__name__) - app.config['LOGGER_HANDLER_POLICY'] = 'never' - @app.errorhandler(404) - def not_found(e): - return 'not found', 404 - @app.errorhandler(500) - def internal_server_error(e): - return 'internal server error', 500 - @app.errorhandler(Forbidden) - def forbidden(e): - return 'forbidden', 403 - @app.route('/') - def index(): - flask.abort(404) - @app.route('/error') - def error(): - 1 // 0 - @app.route('/forbidden') - def error2(): - flask.abort(403) - c = app.test_client() - rv = c.get('/') +def test_make_response_with_response_instance(): + app = flask.Flask(__name__) + with app.test_request_context(): + rv = flask.make_response( + flask.jsonify({'msg': 'W00t'}), 400) + assert rv.status_code == 400 + assert rv.data == b'{\n "msg": "W00t"\n}' + assert rv.mimetype == 'application/json' + + rv = flask.make_response( + flask.Response(''), 400) + assert rv.status_code == 400 + assert rv.data == b'' + assert rv.mimetype == 'text/html' + + rv = flask.make_response( + flask.Response('', headers={'Content-Type': 'text/html'}), + 400, [('X-Foo', 'bar')]) + assert rv.status_code == 400 + assert rv.headers['Content-Type'] == 'text/html' + assert rv.headers['X-Foo'] == 'bar' + + +def test_url_generation(): + app = flask.Flask(__name__) + + @app.route('/hello/', methods=['POST']) + def hello(): + pass + with app.test_request_context(): + assert flask.url_for('hello', name='test x') == '/hello/test%20x' + assert flask.url_for('hello', name='test x', _external=True) == \ + 'http://localhost/hello/test%20x' + + +def test_build_error_handler(): + app = flask.Flask(__name__) + + # Test base case, a URL which results in a BuildError. + with app.test_request_context(): + pytest.raises(BuildError, flask.url_for, 'spam') + + # Verify the error is re-raised if not the current exception. + try: + with app.test_request_context(): + flask.url_for('spam') + except BuildError as err: + error = err + try: + raise RuntimeError('Test case where BuildError is not current.') + except RuntimeError: + pytest.raises( + BuildError, app.handle_url_build_error, error, 'spam', {}) + + # Test a custom handler. + def handler(error, endpoint, values): + # Just a test. + return '/test_handler/' + app.url_build_error_handlers.append(handler) + with app.test_request_context(): + assert flask.url_for('spam') == '/test_handler/' + + +def test_custom_converters(): + from werkzeug.routing import BaseConverter + + class ListConverter(BaseConverter): + + def to_python(self, value): + return value.split(',') + + def to_url(self, value): + base_to_url = super(ListConverter, self).to_url + return ','.join(base_to_url(x) for x in value) + app = flask.Flask(__name__) + app.url_map.converters['list'] = ListConverter + + @app.route('/') + def index(args): + return '|'.join(args) + c = app.test_client() + assert c.get('/1,2,3').data == b'1|2|3' + + +def test_static_files(): + app = flask.Flask(__name__) + app.testing = True + rv = app.test_client().get('/static/index.html') + assert rv.status_code == 200 + assert rv.data.strip() == b'

Hello World!

' + with app.test_request_context(): + assert flask.url_for('static', filename='index.html') == \ + '/static/index.html' + rv.close() + + +def test_none_response(): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/') + def test(): + return None + try: + app.test_client().get('/') + except ValueError as e: + assert str(e) == 'View function did not return a response' + pass + else: + assert "Expected ValueError" + + +def test_request_locals(): + assert repr(flask.g) == '' + assert not flask.g + + +def test_test_app_proper_environ(): + app = flask.Flask(__name__) + app.config.update( + SERVER_NAME='localhost.localdomain:5000' + ) + + @app.route('/') + def index(): + return 'Foo' + + @app.route('/', subdomain='foo') + def subdomain(): + return 'Foo SubDomain' + + rv = app.test_client().get('/') + assert rv.data == b'Foo' + + rv = app.test_client().get('/', 'http://localhost.localdomain:5000') + assert rv.data == b'Foo' + + rv = app.test_client().get('/', 'https://localhost.localdomain:5000') + assert rv.data == b'Foo' + + app.config.update(SERVER_NAME='localhost.localdomain') + rv = app.test_client().get('/', 'https://localhost.localdomain') + assert rv.data == b'Foo' + + try: + app.config.update(SERVER_NAME='localhost.localdomain:443') + rv = app.test_client().get('/', 'https://localhost.localdomain') + # Werkzeug 0.8 assert rv.status_code == 404 - assert rv.data == b'not found' - rv = c.get('/error') - assert rv.status_code == 500 - assert b'internal server error' == rv.data - rv = c.get('/forbidden') - assert rv.status_code == 403 - assert b'forbidden' == rv.data - - def test_before_request_and_routing_errors(self): - app = flask.Flask(__name__) - @app.before_request - def attach_something(): - flask.g.something = 'value' - @app.errorhandler(404) - def return_something(error): - return flask.g.something, 404 - rv = app.test_client().get('/') + except ValueError as e: + # Werkzeug 0.7 + assert str(e) == ( + "the server name provided " + "('localhost.localdomain:443') does not match the " + "server name from the WSGI environment ('localhost.localdomain')" + ) + + try: + app.config.update(SERVER_NAME='localhost.localdomain') + rv = app.test_client().get('/', 'http://foo.localhost') + # Werkzeug 0.8 assert rv.status_code == 404 - assert rv.data == b'value' + except ValueError as e: + # Werkzeug 0.7 + assert str(e) == ( + "the server name provided " + "('localhost.localdomain') does not match the " + "server name from the WSGI environment ('foo.localhost')" + ) + + rv = app.test_client().get('/', 'http://foo.localhost.localdomain') + assert rv.data == b'Foo SubDomain' - def test_user_error_handling(self): - class MyException(Exception): - pass +def test_exception_propagation(): + def apprunner(configkey): app = flask.Flask(__name__) - @app.errorhandler(MyException) - def handle_my_exception(e): - assert isinstance(e, MyException) - return '42' + app.config['LOGGER_HANDLER_POLICY'] = 'never' + @app.route('/') def index(): - raise MyException() - + 1 // 0 c = app.test_client() - assert c.get('/').data == b'42' + if config_key is not None: + app.config[config_key] = True + try: + c.get('/') + except Exception: + pass + else: + assert False, 'expected exception' + else: + assert c.get('/').status_code == 500 + + # we have to run this test in an isolated thread because if the + # debug flag is set to true and an exception happens the context is + # not torn down. This causes other tests that run after this fail + # when they expect no exception on the stack. + for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None: + t = Thread(target=apprunner, args=(config_key,)) + t.start() + t.join() - def test_http_error_subclass_handling(self): - class ForbiddenSubclass(Forbidden): - pass - app = flask.Flask(__name__) - @app.errorhandler(ForbiddenSubclass) - def handle_forbidden_subclass(e): - assert isinstance(e, ForbiddenSubclass) - return 'banana' - @app.errorhandler(403) - def handle_forbidden_subclass(e): - assert not isinstance(e, ForbiddenSubclass) - assert isinstance(e, Forbidden) - return 'apple' - - @app.route('/1') - def index1(): - raise ForbiddenSubclass() - @app.route('/2') - def index2(): - flask.abort(403) - @app.route('/3') - def index3(): - raise Forbidden() +def test_max_content_length(): + app = flask.Flask(__name__) + app.config['MAX_CONTENT_LENGTH'] = 64 - c = app.test_client() - assert c.get('/1').data == b'banana' - assert c.get('/2').data == b'apple' - assert c.get('/3').data == b'apple' + @app.before_request + def always_first(): + flask.request.form['myfile'] + assert False - def test_trapping_of_bad_request_key_errors(self): - app = flask.Flask(__name__) - app.testing = True - @app.route('/fail') - def fail(): - flask.request.form['missing_key'] - c = app.test_client() - assert c.get('/fail').status_code == 400 + @app.route('/accept', methods=['POST']) + def accept_file(): + flask.request.form['myfile'] + assert False - app.config['TRAP_BAD_REQUEST_ERRORS'] = True - c = app.test_client() - try: - c.get('/fail') - except KeyError as e: - assert isinstance(e, BadRequest) - else: - assert False, 'Expected exception' + @app.errorhandler(413) + def catcher(error): + return '42' - def test_trapping_of_all_http_exceptions(self): - app = flask.Flask(__name__) - app.testing = True - app.config['TRAP_HTTP_EXCEPTIONS'] = True - @app.route('/fail') - def fail(): - flask.abort(404) + c = app.test_client() + rv = c.post('/accept', data={'myfile': 'foo' * 100}) + assert rv.data == b'42' - c = app.test_client() - try: - c.get('/fail') - except NotFound as e: - pass - else: - assert False, 'Expected exception' - def test_enctype_debug_helper(self): - from flask.debughelpers import DebugFilesKeyError - app = flask.Flask(__name__) - app.debug = True - @app.route('/fail', methods=['POST']) - def index(): - return flask.request.files['foo'].filename +def test_url_processors(): + app = flask.Flask(__name__) - # with statement is important because we leave an exception on the - # stack otherwise and we want to ensure that this is not the case - # to not negatively affect other tests. - with app.test_client() as c: - try: - c.post('/fail', data={'foo': 'index.txt'}) - except DebugFilesKeyError as e: - assert 'no file contents were transmitted' in str(e) - assert 'This was submitted: "index.txt"' in str(e) - else: - assert False, 'Expected exception' + @app.url_defaults + def add_language_code(endpoint, values): + if flask.g.lang_code is not None and \ + app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): + values.setdefault('lang_code', flask.g.lang_code) - def test_response_creation(self): - app = flask.Flask(__name__) - @app.route('/unicode') - def from_unicode(): - return u'Hällo Wörld' - @app.route('/string') - def from_string(): - return u'Hällo Wörld'.encode('utf-8') - @app.route('/args') - def from_tuple(): - return 'Meh', 400, { - 'X-Foo': 'Testing', - 'Content-Type': 'text/plain; charset=utf-8' - } - @app.route('/two_args') - def from_two_args_tuple(): - return 'Hello', { - 'X-Foo': 'Test', - 'Content-Type': 'text/plain; charset=utf-8' - } - @app.route('/args_status') - def from_status_tuple(): - return 'Hi, status!', 400 - @app.route('/args_header') - def from_response_instance_status_tuple(): - return flask.Response('Hello world', 404), { - "X-Foo": "Bar", - "X-Bar": "Foo" - } + @app.url_value_preprocessor + def pull_lang_code(endpoint, values): + flask.g.lang_code = values.pop('lang_code', None) - c = app.test_client() - assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8') - assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8') - rv = c.get('/args') - assert rv.data == b'Meh' - assert rv.headers['X-Foo'] == 'Testing' - assert rv.status_code == 400 - assert rv.mimetype == 'text/plain' - rv2 = c.get('/two_args') - assert rv2.data == b'Hello' - assert rv2.headers['X-Foo'] == 'Test' - assert rv2.status_code == 200 - assert rv2.mimetype == 'text/plain' - rv3 = c.get('/args_status') - assert rv3.data == b'Hi, status!' - assert rv3.status_code == 400 - assert rv3.mimetype == 'text/html' - rv4 = c.get('/args_header') - assert rv4.data == b'Hello world' - assert rv4.headers['X-Foo'] == 'Bar' - assert rv4.headers['X-Bar'] == 'Foo' - assert rv4.status_code == 404 - - def test_make_response(self): - app = flask.Flask(__name__) - with app.test_request_context(): - rv = flask.make_response() - assert rv.status_code == 200 - assert rv.data == b'' - assert rv.mimetype == 'text/html' - - rv = flask.make_response('Awesome') - assert rv.status_code == 200 - assert rv.data == b'Awesome' - assert rv.mimetype == 'text/html' - - rv = flask.make_response('W00t', 404) - assert rv.status_code == 404 - assert rv.data == b'W00t' - assert rv.mimetype == 'text/html' - - def test_make_response_with_response_instance(self): - app = flask.Flask(__name__) - with app.test_request_context(): - rv = flask.make_response( - flask.jsonify({'msg': 'W00t'}), 400) - assert rv.status_code == 400 - assert rv.data == b'{\n "msg": "W00t"\n}' - assert rv.mimetype == 'application/json' - - rv = flask.make_response( - flask.Response(''), 400) - assert rv.status_code == 400 - assert rv.data == b'' - assert rv.mimetype == 'text/html' - - rv = flask.make_response( - flask.Response('', headers={'Content-Type': 'text/html'}), - 400, [('X-Foo', 'bar')]) - assert rv.status_code == 400 - assert rv.headers['Content-Type'] == 'text/html' - assert rv.headers['X-Foo'] == 'bar' - - def test_url_generation(self): - app = flask.Flask(__name__) - @app.route('/hello/', methods=['POST']) - def hello(): - pass - with app.test_request_context(): - assert flask.url_for('hello', name='test x') == '/hello/test%20x' - assert flask.url_for('hello', name='test x', _external=True) == \ - 'http://localhost/hello/test%20x' + @app.route('//') + def index(): + return flask.url_for('about') - def test_build_error_handler(self): - app = flask.Flask(__name__) + @app.route('//about') + def about(): + return flask.url_for('something_else') - # Test base case, a URL which results in a BuildError. - with app.test_request_context(): - pytest.raises(BuildError, flask.url_for, 'spam') + @app.route('/foo') + def something_else(): + return flask.url_for('about', lang_code='en') - # Verify the error is re-raised if not the current exception. - try: - with app.test_request_context(): - flask.url_for('spam') - except BuildError as err: - error = err - try: - raise RuntimeError('Test case where BuildError is not current.') - except RuntimeError: - pytest.raises(BuildError, app.handle_url_build_error, error, 'spam', {}) - - # Test a custom handler. - def handler(error, endpoint, values): - # Just a test. - return '/test_handler/' - app.url_build_error_handlers.append(handler) - with app.test_request_context(): - assert flask.url_for('spam') == '/test_handler/' - - def test_custom_converters(self): - from werkzeug.routing import BaseConverter - class ListConverter(BaseConverter): - def to_python(self, value): - return value.split(',') - def to_url(self, value): - base_to_url = super(ListConverter, self).to_url - return ','.join(base_to_url(x) for x in value) - app = flask.Flask(__name__) - app.url_map.converters['list'] = ListConverter - @app.route('/') - def index(args): - return '|'.join(args) - c = app.test_client() - assert c.get('/1,2,3').data == b'1|2|3' + c = app.test_client() - def test_static_files(self): - app = flask.Flask(__name__) - app.testing = True - rv = app.test_client().get('/static/index.html') - assert rv.status_code == 200 - assert rv.data.strip() == b'

Hello World!

' - with app.test_request_context(): - assert flask.url_for('static', filename='index.html') == \ - '/static/index.html' - rv.close() + assert c.get('/de/').data == b'/de/about' + assert c.get('/de/about').data == b'/foo' + assert c.get('/foo').data == b'/en/about' - def test_none_response(self): - app = flask.Flask(__name__) - app.testing = True - @app.route('/') - def test(): - return None - try: - app.test_client().get('/') - except ValueError as e: - assert str(e) == 'View function did not return a response' - pass - else: - assert "Expected ValueError" - def test_request_locals(self): - assert repr(flask.g) == '' - assert not flask.g +def test_inject_blueprint_url_defaults(): + app = flask.Flask(__name__) + bp = flask.Blueprint('foo.bar.baz', __name__, + template_folder='template') - def test_test_app_proper_environ(self): - app = flask.Flask(__name__) - app.config.update( - SERVER_NAME='localhost.localdomain:5000' - ) - @app.route('/') - def index(): - return 'Foo' + @bp.url_defaults + def bp_defaults(endpoint, values): + values['page'] = 'login' - @app.route('/', subdomain='foo') - def subdomain(): - return 'Foo SubDomain' + @bp.route('/') + def view(page): + pass - rv = app.test_client().get('/') - assert rv.data == b'Foo' + app.register_blueprint(bp) - rv = app.test_client().get('/', 'http://localhost.localdomain:5000') - assert rv.data == b'Foo' + values = dict() + app.inject_url_defaults('foo.bar.baz.view', values) + expected = dict(page='login') + assert values == expected - rv = app.test_client().get('/', 'https://localhost.localdomain:5000') - assert rv.data == b'Foo' + with app.test_request_context('/somepage'): + url = flask.url_for('foo.bar.baz.view') + expected = '/login' + assert url == expected - app.config.update(SERVER_NAME='localhost.localdomain') - rv = app.test_client().get('/', 'https://localhost.localdomain') - assert rv.data == b'Foo' - try: - app.config.update(SERVER_NAME='localhost.localdomain:443') - rv = app.test_client().get('/', 'https://localhost.localdomain') - # Werkzeug 0.8 - assert rv.status_code == 404 - except ValueError as e: - # Werkzeug 0.7 - assert str(e) == ( - "the server name provided " - "('localhost.localdomain:443') does not match the " - "server name from the WSGI environment ('localhost.localdomain')" - ) +def test_nonascii_pathinfo(): + app = flask.Flask(__name__) + app.testing = True - try: - app.config.update(SERVER_NAME='localhost.localdomain') - rv = app.test_client().get('/', 'http://foo.localhost') - # Werkzeug 0.8 - assert rv.status_code == 404 - except ValueError as e: - # Werkzeug 0.7 - assert str(e) == ( - "the server name provided " - "('localhost.localdomain') does not match the " - "server name from the WSGI environment ('foo.localhost')" - ) - - rv = app.test_client().get('/', 'http://foo.localhost.localdomain') - assert rv.data == b'Foo SubDomain' - - def test_exception_propagation(self): - def apprunner(configkey): - app = flask.Flask(__name__) - app.config['LOGGER_HANDLER_POLICY'] = 'never' - @app.route('/') - def index(): - 1 // 0 - c = app.test_client() - if config_key is not None: - app.config[config_key] = True - try: - c.get('/') - except Exception: - pass - else: - assert False, 'expected exception' - else: - assert c.get('/').status_code == 500 - - # we have to run this test in an isolated thread because if the - # debug flag is set to true and an exception happens the context is - # not torn down. This causes other tests that run after this fail - # when they expect no exception on the stack. - for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None: - t = Thread(target=apprunner, args=(config_key,)) - t.start() - t.join() - - def test_max_content_length(self): - app = flask.Flask(__name__) - app.config['MAX_CONTENT_LENGTH'] = 64 - @app.before_request - def always_first(): - flask.request.form['myfile'] - assert False - @app.route('/accept', methods=['POST']) - def accept_file(): - flask.request.form['myfile'] - assert False - @app.errorhandler(413) - def catcher(error): - return '42' + @app.route(u'/киртест') + def index(): + return 'Hello World!' - c = app.test_client() - rv = c.post('/accept', data={'myfile': 'foo' * 100}) - assert rv.data == b'42' + c = app.test_client() + rv = c.get(u'/киртест') + assert rv.data == b'Hello World!' - def test_url_processors(self): - app = flask.Flask(__name__) - @app.url_defaults - def add_language_code(endpoint, values): - if flask.g.lang_code is not None and \ - app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): - values.setdefault('lang_code', flask.g.lang_code) +def test_debug_mode_complains_after_first_request(): + app = flask.Flask(__name__) + app.debug = True - @app.url_value_preprocessor - def pull_lang_code(endpoint, values): - flask.g.lang_code = values.pop('lang_code', None) + @app.route('/') + def index(): + return 'Awesome' + assert not app.got_first_request + assert app.test_client().get('/').data == b'Awesome' + try: + @app.route('/foo') + def broken(): + return 'Meh' + except AssertionError as e: + assert 'A setup function was called' in str(e) + else: + assert False, 'Expected exception' - @app.route('//') - def index(): - return flask.url_for('about') + app.debug = False - @app.route('//about') - def about(): - return flask.url_for('something_else') + @app.route('/foo') + def working(): + return 'Meh' + assert app.test_client().get('/foo').data == b'Meh' + assert app.got_first_request - @app.route('/foo') - def something_else(): - return flask.url_for('about', lang_code='en') - c = app.test_client() +def test_before_first_request_functions(): + got = [] + app = flask.Flask(__name__) - assert c.get('/de/').data == b'/de/about' - assert c.get('/de/about').data == b'/foo' - assert c.get('/foo').data == b'/en/about' + @app.before_first_request + def foo(): + got.append(42) + c = app.test_client() + c.get('/') + assert got == [42] + c.get('/') + assert got == [42] + assert app.got_first_request - def test_inject_blueprint_url_defaults(self): - app = flask.Flask(__name__) - bp = flask.Blueprint('foo.bar.baz', __name__, - template_folder='template') - @bp.url_defaults - def bp_defaults(endpoint, values): - values['page'] = 'login' - @bp.route('/') - def view(page): pass +def test_before_first_request_functions_concurrent(): + got = [] + app = flask.Flask(__name__) - app.register_blueprint(bp) + @app.before_first_request + def foo(): + time.sleep(0.2) + got.append(42) - values = dict() - app.inject_url_defaults('foo.bar.baz.view', values) - expected = dict(page='login') - assert values == expected + c = app.test_client() - with app.test_request_context('/somepage'): - url = flask.url_for('foo.bar.baz.view') - expected = '/login' - assert url == expected + def get_and_assert(): + c.get("/") + assert got == [42] - def test_nonascii_pathinfo(self): - app = flask.Flask(__name__) - app.testing = True + t = Thread(target=get_and_assert) + t.start() + get_and_assert() + t.join() + assert app.got_first_request - @app.route(u'/киртест') - def index(): - return 'Hello World!' - c = app.test_client() - rv = c.get(u'/киртест') - assert rv.data == b'Hello World!' +def test_routing_redirect_debugging(): + app = flask.Flask(__name__) + app.debug = True - def test_debug_mode_complains_after_first_request(self): - app = flask.Flask(__name__) - app.debug = True - @app.route('/') - def index(): - return 'Awesome' - assert not app.got_first_request - assert app.test_client().get('/').data == b'Awesome' + @app.route('/foo/', methods=['GET', 'POST']) + def foo(): + return 'success' + with app.test_client() as c: try: - @app.route('/foo') - def broken(): - return 'Meh' + c.post('/foo', data={}) except AssertionError as e: - assert 'A setup function was called' in str(e) + assert 'http://localhost/foo/' in str(e) + assert ('Make sure to directly send ' + 'your POST-request to this URL') in str(e) else: assert False, 'Expected exception' - app.debug = False - @app.route('/foo') - def working(): - return 'Meh' - assert app.test_client().get('/foo').data == b'Meh' - assert app.got_first_request + rv = c.get('/foo', data={}, follow_redirects=True) + assert rv.data == b'success' - def test_before_first_request_functions(self): - got = [] - app = flask.Flask(__name__) - @app.before_first_request - def foo(): - got.append(42) - c = app.test_client() - c.get('/') - assert got == [42] - c.get('/') - assert got == [42] - assert app.got_first_request + app.debug = False + with app.test_client() as c: + rv = c.post('/foo', data={}, follow_redirects=True) + assert rv.data == b'success' - def test_before_first_request_functions_concurrent(self): - got = [] - app = flask.Flask(__name__) - @app.before_first_request - def foo(): - time.sleep(0.2) - got.append(42) +def test_route_decorator_custom_endpoint(): + app = flask.Flask(__name__) + app.debug = True - c = app.test_client() - def get_and_assert(): - c.get("/") - assert got == [42] + @app.route('/foo/') + def foo(): + return flask.request.endpoint - t = Thread(target=get_and_assert) - t.start() - get_and_assert() - t.join() - assert app.got_first_request + @app.route('/bar/', endpoint='bar') + def for_bar(): + return flask.request.endpoint - def test_routing_redirect_debugging(self): - app = flask.Flask(__name__) - app.debug = True - @app.route('/foo/', methods=['GET', 'POST']) - def foo(): - return 'success' - with app.test_client() as c: - try: - c.post('/foo', data={}) - except AssertionError as e: - assert 'http://localhost/foo/' in str(e) - assert ('Make sure to directly send ' - 'your POST-request to this URL') in str(e) - else: - assert False, 'Expected exception' + @app.route('/bar/123', endpoint='123') + def for_bar_foo(): + return flask.request.endpoint - rv = c.get('/foo', data={}, follow_redirects=True) - assert rv.data == b'success' + with app.test_request_context(): + assert flask.url_for('foo') == '/foo/' + assert flask.url_for('bar') == '/bar/' + assert flask.url_for('123') == '/bar/123' - app.debug = False - with app.test_client() as c: - rv = c.post('/foo', data={}, follow_redirects=True) - assert rv.data == b'success' + c = app.test_client() + assert c.get('/foo/').data == b'foo' + assert c.get('/bar/').data == b'bar' + assert c.get('/bar/123').data == b'123' - def test_route_decorator_custom_endpoint(self): - app = flask.Flask(__name__) - app.debug = True - @app.route('/foo/') - def foo(): - return flask.request.endpoint +def test_preserve_only_once(): + app = flask.Flask(__name__) + app.debug = True - @app.route('/bar/', endpoint='bar') - def for_bar(): - return flask.request.endpoint + @app.route('/fail') + def fail_func(): + 1 // 0 - @app.route('/bar/123', endpoint='123') - def for_bar_foo(): - return flask.request.endpoint + c = app.test_client() + for x in range(3): + with pytest.raises(ZeroDivisionError): + c.get('/fail') - with app.test_request_context(): - assert flask.url_for('foo') == '/foo/' - assert flask.url_for('bar') == '/bar/' - assert flask.url_for('123') == '/bar/123' + assert flask._request_ctx_stack.top is not None + assert flask._app_ctx_stack.top is not None + # implicit appctx disappears too + flask._request_ctx_stack.top.pop() + assert flask._request_ctx_stack.top is None + assert flask._app_ctx_stack.top is None - c = app.test_client() - assert c.get('/foo/').data == b'foo' - assert c.get('/bar/').data == b'bar' - assert c.get('/bar/123').data == b'123' - def test_preserve_only_once(self): - app = flask.Flask(__name__) - app.debug = True +def test_preserve_remembers_exception(): + app = flask.Flask(__name__) + app.debug = True + errors = [] - @app.route('/fail') - def fail_func(): - 1 // 0 + @app.route('/fail') + def fail_func(): + 1 // 0 - c = app.test_client() - for x in range(3): - with pytest.raises(ZeroDivisionError): - c.get('/fail') - - assert flask._request_ctx_stack.top is not None - assert flask._app_ctx_stack.top is not None - # implicit appctx disappears too - flask._request_ctx_stack.top.pop() - assert flask._request_ctx_stack.top is None - assert flask._app_ctx_stack.top is None - - def test_preserve_remembers_exception(self): - app = flask.Flask(__name__) - app.debug = True - errors = [] + @app.route('/success') + def success_func(): + return 'Okay' - @app.route('/fail') - def fail_func(): - 1 // 0 + @app.teardown_request + def teardown_handler(exc): + errors.append(exc) - @app.route('/success') - def success_func(): - return 'Okay' + c = app.test_client() - @app.teardown_request - def teardown_handler(exc): - errors.append(exc) + # After this failure we did not yet call the teardown handler + with pytest.raises(ZeroDivisionError): + c.get('/fail') + assert errors == [] - c = app.test_client() + # But this request triggers it, and it's an error + c.get('/success') + assert len(errors) == 2 + assert isinstance(errors[0], ZeroDivisionError) - # After this failure we did not yet call the teardown handler - with pytest.raises(ZeroDivisionError): - c.get('/fail') - assert errors == [] + # At this point another request does nothing. + c.get('/success') + assert len(errors) == 3 + assert errors[1] is None - # But this request triggers it, and it's an error - c.get('/success') - assert len(errors) == 2 - assert isinstance(errors[0], ZeroDivisionError) - # At this point another request does nothing. - c.get('/success') - assert len(errors) == 3 - assert errors[1] == None +def test_get_method_on_g(): + app = flask.Flask(__name__) + app.testing = True - def test_get_method_on_g(self): - app = flask.Flask(__name__) - app.testing = True + with app.app_context(): + assert flask.g.get('x') is None + assert flask.g.get('x', 11) == 11 + flask.g.x = 42 + assert flask.g.get('x') == 42 + assert flask.g.x == 42 - with app.app_context(): - assert flask.g.get('x') == None - assert flask.g.get('x', 11) == 11 - flask.g.x = 42 - assert flask.g.get('x') == 42 - assert flask.g.x == 42 - def test_g_iteration_protocol(self): - app = flask.Flask(__name__) - app.testing = True +def test_g_iteration_protocol(): + app = flask.Flask(__name__) + app.testing = True - with app.app_context(): - flask.g.foo = 23 - flask.g.bar = 42 - assert 'foo' in flask.g - assert 'foos' not in flask.g - assert sorted(flask.g) == ['bar', 'foo'] + with app.app_context(): + flask.g.foo = 23 + flask.g.bar = 42 + assert 'foo' in flask.g + assert 'foos' not in flask.g + assert sorted(flask.g) == ['bar', 'foo'] -class TestSubdomain(object): +def test_subdomain_basic_support(): + app = flask.Flask(__name__) + app.config['SERVER_NAME'] = 'localhost' - def test_basic_support(self): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost' - @app.route('/') - def normal_index(): - return 'normal index' - @app.route('/', subdomain='test') - def test_index(): - return 'test index' + @app.route('/') + def normal_index(): + return 'normal index' - c = app.test_client() - rv = c.get('/', 'http://localhost/') - assert rv.data == b'normal index' + @app.route('/', subdomain='test') + def test_index(): + return 'test index' - rv = c.get('/', 'http://test.localhost/') - assert rv.data == b'test index' + c = app.test_client() + rv = c.get('/', 'http://localhost/') + assert rv.data == b'normal index' - def test_subdomain_matching(self): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost' - @app.route('/', subdomain='') - def index(user): - return 'index for %s' % user + rv = c.get('/', 'http://test.localhost/') + assert rv.data == b'test index' - c = app.test_client() - rv = c.get('/', 'http://mitsuhiko.localhost/') - assert rv.data == b'index for mitsuhiko' - def test_subdomain_matching_with_ports(self): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost:3000' - @app.route('/', subdomain='') - def index(user): - return 'index for %s' % user +def test_subdomain_matching(): + app = flask.Flask(__name__) + app.config['SERVER_NAME'] = 'localhost' - c = app.test_client() - rv = c.get('/', 'http://mitsuhiko.localhost:3000/') - assert rv.data == b'index for mitsuhiko' + @app.route('/', subdomain='') + def index(user): + return 'index for %s' % user - def test_multi_route_rules(self): - app = flask.Flask(__name__) + c = app.test_client() + rv = c.get('/', 'http://mitsuhiko.localhost/') + assert rv.data == b'index for mitsuhiko' - @app.route('/') - @app.route('//') - def index(test='a'): - return test - rv = app.test_client().open('/') - assert rv.data == b'a' - rv = app.test_client().open('/b/') - assert rv.data == b'b' +def test_subdomain_matching_with_ports(): + app = flask.Flask(__name__) + app.config['SERVER_NAME'] = 'localhost:3000' - def test_multi_route_class_views(self): - class View(object): - def __init__(self, app): - app.add_url_rule('/', 'index', self.index) - app.add_url_rule('//', 'index', self.index) + @app.route('/', subdomain='') + def index(user): + return 'index for %s' % user - def index(self, test='a'): - return test + c = app.test_client() + rv = c.get('/', 'http://mitsuhiko.localhost:3000/') + assert rv.data == b'index for mitsuhiko' - app = flask.Flask(__name__) - _ = View(app) - rv = app.test_client().open('/') - assert rv.data == b'a' - rv = app.test_client().open('/b/') - assert rv.data == b'b' + +def test_multi_route_rules(): + app = flask.Flask(__name__) + + @app.route('/') + @app.route('//') + def index(test='a'): + return test + + rv = app.test_client().open('/') + assert rv.data == b'a' + rv = app.test_client().open('/b/') + assert rv.data == b'b' + + +def test_multi_route_class_views(): + class View(object): + + def __init__(self, app): + app.add_url_rule('/', 'index', self.index) + app.add_url_rule('//', 'index', self.index) + + def index(self, test='a'): + return test + + app = flask.Flask(__name__) + _ = View(app) + rv = app.test_client().open('/') + assert rv.data == b'a' + rv = app.test_client().open('/b/') + assert rv.data == b'b'