# -*- coding: utf-8 -*- """ Flask Tests ~~~~~~~~~~~ Tests Flask itself. The majority of Flask is already tested as part of Werkzeug. :copyright: (c) 2010 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from __future__ import with_statement import os import re import sys import flask import unittest from threading import Thread from logging import StreamHandler from contextlib import contextmanager from datetime import datetime from werkzeug import parse_date, parse_options_header from werkzeug.exceptions import NotFound from jinja2 import TemplateNotFound from cStringIO import StringIO example_path = os.path.join(os.path.dirname(__file__), '..', 'examples') sys.path.append(os.path.join(example_path, 'flaskr')) sys.path.append(os.path.join(example_path, 'minitwit')) def has_encoding(name): try: import codecs codecs.lookup(name) return True except LookupError: return False # config keys used for the ConfigTestCase TEST_KEY = 'foo' SECRET_KEY = 'devkey' @contextmanager def catch_warnings(): """Catch warnings in a with block in a list""" import warnings # make sure deprecation warnings are active in tests warnings.simplefilter('default', category=DeprecationWarning) filters = warnings.filters warnings.filters = filters[:] old_showwarning = warnings.showwarning log = [] def showwarning(message, category, filename, lineno, file=None, line=None): log.append(locals()) try: warnings.showwarning = showwarning yield log finally: warnings.filters = filters warnings.showwarning = old_showwarning @contextmanager def catch_stderr(): """Catch stderr in a StringIO""" old_stderr = sys.stderr sys.stderr = rv = StringIO() try: yield rv finally: sys.stderr = old_stderr class ContextTestCase(unittest.TestCase): def test_context_binding(self): app = flask.Flask(__name__) @app.route('/') def index(): return 'Hello %s!' % flask.request.args['name'] @app.route('/meh') def meh(): return flask.request.url with app.test_request_context('/?name=World'): assert index() == 'Hello World!' with app.test_request_context('/meh'): assert meh() == 'http://localhost/meh' assert flask._request_ctx_stack.top is None def test_context_test(self): app = flask.Flask(__name__) assert not flask.request assert not flask.has_request_context() ctx = app.test_request_context() ctx.push() try: assert flask.request assert flask.has_request_context() finally: ctx.pop() def test_manual_context_binding(self): app = flask.Flask(__name__) @app.route('/') def index(): return 'Hello %s!' % flask.request.args['name'] ctx = app.test_request_context('/?name=World') ctx.push() assert index() == 'Hello World!' ctx.pop() try: index() except RuntimeError: pass else: assert 0, 'expected runtime error' def test_test_client_context_binding(self): app = flask.Flask(__name__) @app.route('/') def index(): flask.g.value = 42 return 'Hello World!' @app.route('/other') def other(): 1/0 with app.test_client() as c: resp = c.get('/') assert flask.g.value == 42 assert resp.data == 'Hello World!' assert resp.status_code == 200 resp = c.get('/other') assert not hasattr(flask.g, 'value') assert 'Internal Server Error' in resp.data assert resp.status_code == 500 flask.g.value = 23 try: flask.g.value except (AttributeError, RuntimeError): pass else: raise AssertionError('some kind of exception expected') class BasicFunctionalityTestCase(unittest.TestCase): 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 == '' 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_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 c = app.test_client() assert c.get('/').data == '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 == 'POST' assert c.get('/more').data == 'GET' rv = c.delete('/more') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] def test_url_mapping(self): app = flask.Flask(__name__) 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 == '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 == 'POST' assert c.get('/more').data == '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 c = app.test_client() assert c.get('/foo/').data == 'index' assert c.get('/foo/bar').data == 'bar' 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.endpoint('bar') def bar(): return 'bar' @app.endpoint('index') def index(): return 'index' c = app.test_client() assert c.get('/foo/').data == 'index' assert c.get('/foo/bar').data == 'bar' 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'] c = app.test_client() assert c.post('/set', data={'value': '42'}).data == 'value set' assert c.get('/get').data == '42' 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() def test_missing_session(self): app = flask.Flask(__name__) def expect_exception(f, *args, **kwargs): try: f(*args, **kwargs) except RuntimeError, 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(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 unicode(flask.session.permanent) client = app.test_client() rv = client.get('/') assert 'set-cookie' in rv.headers match = re.search(r'\bexpires=([^;]+)', 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 == '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_flashes(self): 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(self): app = flask.Flask(__name__) app.secret_key = 'testkey' @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(with_categories=True) assert len(messages) == 3 assert messages[0] == ('message', u'Hello World') assert messages[1] == ('error', u'Hello World') assert messages[2] == ('warning', flask.Markup(u'Testing')) return '' messages = flask.get_flashed_messages() assert len(messages) == 3 assert messages[0] == u'Hello World' assert messages[1] == u'Hello World' assert messages[2] == flask.Markup(u'Testing') c = app.test_client() c.get('/') c.get('/test') 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 += '|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 == 'request|after' def test_after_request_errors(self): app = flask.Flask(__name__) called = [] @app.after_request def after_request(response): called.append(True) return response @app.route('/') def fails(): 1/0 rv = app.test_client().get('/') assert rv.status_code == 500 assert 'Internal Server Error' in rv.data assert len(called) == 1 def test_after_request_handler_error(self): called = [] app = flask.Flask(__name__) @app.after_request def after_request(response): called.append(True) 1/0 return response @app.route('/') def fails(): 1/0 rv = app.test_client().get('/') assert rv.status_code == 500 assert 'Internal Server Error' in rv.data assert len(called) == 1 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('/') assert rv.status_code == 200 assert 'Response' in rv.data assert len(called) == 1 def test_teardown_request_handler_debug_mode(self): called = [] app = flask.Flask(__name__) app.debug = 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 'Response' in rv.data assert len(called) == 1 def test_teardown_request_handler_error(self): called = [] app = flask.Flask(__name__) @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 'Internal Server Error' in rv.data assert len(called) == 2 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 == '42' assert called == [1, 2, 3, 4, 5, 6] def test_error_handling(self): app = flask.Flask(__name__) @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.route('/') def index(): flask.abort(404) @app.route('/error') def error(): 1 // 0 c = app.test_client() rv = c.get('/') assert rv.status_code == 404 assert rv.data == 'not found' rv = c.get('/error') assert rv.status_code == 500 assert 'internal server error' == rv.data 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'}, 'text/plain' 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 == 'Meh' assert rv.headers['X-Foo'] == 'Testing' assert rv.status_code == 400 assert rv.mimetype == 'text/plain' 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 == '' assert rv.mimetype == 'text/html' rv = flask.make_response('Awesome') assert rv.status_code == 200 assert rv.data == 'Awesome' assert rv.mimetype == 'text/html' rv = flask.make_response('W00t', 404) assert rv.status_code == 404 assert rv.data == 'W00t' assert rv.mimetype == 'text/html' 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' 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 == '1|2|3' def test_static_files(self): app = flask.Flask(__name__) rv = app.test_client().get('/static/index.html') assert rv.status_code == 200 assert rv.data.strip() == '

Hello World!

' with app.test_request_context(): assert flask.url_for('static', filename='index.html') \ == '/static/index.html' def test_none_response(self): app = flask.Flask(__name__) @app.route('/') def test(): return None try: app.test_client().get('/') except ValueError, e: assert str(e) == 'View function did not return a response' pass else: assert "Expected ValueError" def test_request_locals(self): self.assertEqual(repr(flask.g), '') self.assertFalse(flask.g) def test_proper_test_request_context(self): app = flask.Flask(__name__) app.config.update( SERVER_NAME='localhost.localdomain:5000' ) @app.route('/') def index(): return None @app.route('/', subdomain='foo') def sub(): return None with app.test_request_context('/'): assert flask.url_for('index', _external=True) == 'http://localhost.localdomain:5000/' with app.test_request_context('/'): assert flask.url_for('sub', _external=True) == 'http://foo.localhost.localdomain:5000/' try: with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}): pass except Exception, e: assert isinstance(e, ValueError) assert str(e) == "the server name provided " + \ "('localhost.localdomain:5000') does not match the " + \ "server name from the WSGI environment ('localhost')", str(e) try: app.config.update(SERVER_NAME='localhost') with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}): pass except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) try: app.config.update(SERVER_NAME='localhost:80') with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}): pass except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) 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' @app.route('/', subdomain='foo') def subdomain(): return 'Foo SubDomain' try: rv = app.test_client().get('/') assert rv.data == 'Foo' except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) try: rv = app.test_client().get('/', 'http://localhost.localdomain:5000') assert rv.data == 'Foo' except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) try: rv = app.test_client().get('/', 'https://localhost.localdomain:5000') assert rv.data == 'Foo' except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) try: app.config.update(SERVER_NAME='localhost.localdomain') rv = app.test_client().get('/', 'https://localhost.localdomain') assert rv.data == 'Foo' except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) try: app.config.update(SERVER_NAME='localhost.localdomain:443') rv = app.test_client().get('/', 'https://localhost.localdomain') assert rv.data == 'Foo' except ValueError, e: assert str(e) == "the server name provided " + \ "('localhost.localdomain:443') does not match the " + \ "server name from the WSGI environment ('localhost.localdomain')", str(e) try: app.config.update(SERVER_NAME='localhost.localdomain') app.test_client().get('/', 'http://foo.localhost') except ValueError, e: assert str(e) == "the server name provided " + \ "('localhost.localdomain') does not match the " + \ "server name from the WSGI environment ('foo.localhost')", str(e) try: rv = app.test_client().get('/', 'http://foo.localhost.localdomain') assert rv.data == 'Foo SubDomain' except ValueError, e: raise ValueError( "No ValueError exception should have been raised \"%s\"" % e ) def test_exception_propagation(self): def apprunner(configkey): app = flask.Flask(__name__) @app.route('/') def index(): 1/0 c = app.test_client() if config_key is not None: app.config[config_key] = True try: resp = c.get('/') except Exception: pass else: self.fail('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() class JSONTestCase(unittest.TestCase): def test_jsonify(self): d = dict(a=23, b=42, c=[1, 2, 3]) 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_json_attr(self): app = flask.Flask(__name__) @app.route('/add', methods=['POST']) def add(): return unicode(flask.request.json['a'] + flask.request.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 == '3' def test_template_escaping(self): app = flask.Flask(__name__) render = flask.render_template_string with app.test_request_context(): rv = render('{{ ""|tojson|safe }}') assert rv == '"<\\/script>"' rv = render('{{ "<\0/script>"|tojson|safe }}') assert rv == '"<\\u0000\\/script>"' def test_modified_url_encoding(self): class ModifiedRequest(flask.Request): url_charset = 'euc-kr' app = flask.Flask(__name__) 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 class TemplatingTestCase(unittest.TestCase): def test_context_processing(self): app = flask.Flask(__name__) @app.context_processor def context_processor(): return {'injected_value': 42} @app.route('/') def index(): return flask.render_template('context_template.html', value=23) rv = app.test_client().get('/') assert rv.data == '

23|42' def test_original_win(self): app = flask.Flask(__name__) @app.route('/') def index(): return flask.render_template_string('{{ config }}', config=42) rv = app.test_client().get('/') assert rv.data == '42' def test_standard_context(self): app = flask.Flask(__name__) app.secret_key = 'development key' @app.route('/') def index(): flask.g.foo = 23 flask.session['test'] = 'aha' return flask.render_template_string(''' {{ request.args.foo }} {{ g.foo }} {{ config.DEBUG }} {{ session.test }} ''') rv = app.test_client().get('/?foo=42') assert rv.data.split() == ['42', '23', 'False', 'aha'] def test_escaping(self): text = '

Hello World!' app = flask.Flask(__name__) @app.route('/') def index(): return flask.render_template('escaping_template.html', text=text, html=flask.Markup(text)) lines = app.test_client().get('/').data.splitlines() assert lines == [ '<p>Hello World!', '

Hello World!', '

Hello World!', '

Hello World!', '<p>Hello World!', '

Hello World!' ] def test_no_escaping(self): app = flask.Flask(__name__) with app.test_request_context(): assert flask.render_template_string('{{ foo }}', foo='') == '' assert flask.render_template('mail.txt', foo='') \ == ' Mail' def test_macros(self): app = flask.Flask(__name__) with app.test_request_context(): macro = flask.get_template_attribute('_macro.html', 'hello') assert macro('World') == 'Hello World!' def test_template_filter(self): app = flask.Flask(__name__) @app.template_filter() def my_reverse(s): return s[::-1] assert 'my_reverse' in app.jinja_env.filters.keys() assert app.jinja_env.filters['my_reverse'] == my_reverse assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' def test_template_filter_with_name(self): app = flask.Flask(__name__) @app.template_filter('strrev') def my_reverse(s): return s[::-1] assert 'strrev' in app.jinja_env.filters.keys() assert app.jinja_env.filters['strrev'] == my_reverse assert app.jinja_env.filters['strrev']('abcd') == 'dcba' def test_template_filter_with_template(self): app = flask.Flask(__name__) @app.template_filter() def super_reverse(s): return s[::-1] @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') rv = app.test_client().get('/') assert rv.data == 'dcba' def test_template_filter_with_name_and_template(self): app = flask.Flask(__name__) @app.template_filter('super_reverse') def my_reverse(s): return s[::-1] @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') rv = app.test_client().get('/') assert rv.data == 'dcba' def test_custom_template_loader(self): class MyFlask(flask.Flask): def create_jinja_loader(self): from jinja2 import DictLoader return DictLoader({'index.html': 'Hello Custom World!'}) app = MyFlask(__name__) @app.route('/') def index(): return flask.render_template('index.html') c = app.test_client() rv = c.get('/') assert rv.data == 'Hello Custom World!' class ModuleTestCase(unittest.TestCase): def test_basic_module(self): app = flask.Flask(__name__) admin = flask.Module(__name__, 'admin', url_prefix='/admin') @admin.route('/') def admin_index(): return 'admin index' @admin.route('/login') def admin_login(): return 'admin login' @admin.route('/logout') def admin_logout(): return 'admin logout' @app.route('/') def index(): return 'the index' app.register_module(admin) c = app.test_client() assert c.get('/').data == 'the index' assert c.get('/admin/').data == 'admin index' assert c.get('/admin/login').data == 'admin login' assert c.get('/admin/logout').data == 'admin logout' def test_default_endpoint_name(self): app = flask.Flask(__name__) mod = flask.Module(__name__, 'frontend') def index(): return 'Awesome' mod.add_url_rule('/', view_func=index) app.register_module(mod) rv = app.test_client().get('/') assert rv.data == 'Awesome' with app.test_request_context(): assert flask.url_for('frontend.index') == '/' def test_request_processing(self): catched = [] app = flask.Flask(__name__) admin = flask.Module(__name__, 'admin', url_prefix='/admin') @admin.before_request def before_admin_request(): catched.append('before-admin') @admin.after_request def after_admin_request(response): catched.append('after-admin') return response @admin.route('/') def admin_index(): return 'the admin' @app.before_request def before_request(): catched.append('before-app') @app.after_request def after_request(response): catched.append('after-app') return response @app.route('/') def index(): return 'the index' app.register_module(admin) c = app.test_client() assert c.get('/').data == 'the index' assert catched == ['before-app', 'after-app'] del catched[:] assert c.get('/admin/').data == 'the admin' assert catched == ['before-app', 'before-admin', 'after-admin', 'after-app'] def test_context_processors(self): app = flask.Flask(__name__) admin = flask.Module(__name__, 'admin', url_prefix='/admin') @app.context_processor def inject_all_regualr(): return {'a': 1} @admin.context_processor def inject_admin(): return {'b': 2} @admin.app_context_processor def inject_all_module(): return {'c': 3} @app.route('/') def index(): return flask.render_template_string('{{ a }}{{ b }}{{ c }}') @admin.route('/') def admin_index(): return flask.render_template_string('{{ a }}{{ b }}{{ c }}') app.register_module(admin) c = app.test_client() assert c.get('/').data == '13' assert c.get('/admin/').data == '123' def test_late_binding(self): app = flask.Flask(__name__) admin = flask.Module(__name__, 'admin') @admin.route('/') def index(): return '42' app.register_module(admin, url_prefix='/admin') assert app.test_client().get('/admin/').data == '42' def test_error_handling(self): app = flask.Flask(__name__) admin = flask.Module(__name__, 'admin') @admin.app_errorhandler(404) def not_found(e): return 'not found', 404 @admin.app_errorhandler(500) def internal_server_error(e): return 'internal server error', 500 @admin.route('/') def index(): flask.abort(404) @admin.route('/error') def error(): 1 // 0 app.register_module(admin) c = app.test_client() rv = c.get('/') assert rv.status_code == 404 assert rv.data == 'not found' rv = c.get('/error') assert rv.status_code == 500 assert 'internal server error' == rv.data def test_templates_and_static(self): from moduleapp import app c = app.test_client() rv = c.get('/') assert rv.data == 'Hello from the Frontend' rv = c.get('/admin/') assert rv.data == 'Hello from the Admin' rv = c.get('/admin/index2') assert rv.data == 'Hello from the Admin' rv = c.get('/admin/static/test.txt') assert rv.data.strip() == 'Admin File' rv = c.get('/admin/static/css/test.css') assert rv.data.strip() == '/* nested file */' with app.test_request_context(): assert flask.url_for('admin.static', filename='test.txt') \ == '/admin/static/test.txt' with app.test_request_context(): try: flask.render_template('missing.html') except TemplateNotFound, e: assert e.name == 'missing.html' else: assert 0, 'expected exception' with flask.Flask(__name__).test_request_context(): assert flask.render_template('nested/nested.txt') == 'I\'m nested' def test_safe_access(self): from moduleapp import app with app.test_request_context(): f = app.view_functions['admin.static'] try: f('/etc/passwd') except NotFound: pass else: assert 0, 'expected exception' try: f('../__init__.py') except NotFound: pass else: assert 0, 'expected exception' # testcase for a security issue that may exist on windows systems import os import ntpath old_path = os.path os.path = ntpath try: try: f('..\\__init__.py') except NotFound: pass else: assert 0, 'expected exception' finally: os.path = old_path def test_endpoint_decorator(self): from werkzeug.routing import Submount, Rule from flask import Module app = flask.Flask(__name__) app.url_map.add(Submount('/foo', [ Rule('/bar', endpoint='bar'), Rule('/', endpoint='index') ])) module = Module(__name__, __name__) @module.endpoint('bar') def bar(): return 'bar' @module.endpoint('index') def index(): return 'index' app.register_module(module) c = app.test_client() assert c.get('/foo/').data == 'index' assert c.get('/foo/bar').data == 'bar' class SendfileTestCase(unittest.TestCase): def test_send_file_regular(self): app = flask.Flask(__name__) with app.test_request_context(): rv = flask.send_file('static/index.html') assert rv.direct_passthrough assert rv.mimetype == 'text/html' with app.open_resource('static/index.html') as f: assert rv.data == f.read() def test_send_file_xsendfile(self): app = flask.Flask(__name__) app.use_x_sendfile = True with app.test_request_context(): rv = flask.send_file('static/index.html') assert rv.direct_passthrough assert 'x-sendfile' in rv.headers assert rv.headers['x-sendfile'] == \ os.path.join(app.root_path, 'static/index.html') assert rv.mimetype == 'text/html' def test_send_file_object(self): app = flask.Flask(__name__) with catch_warnings() as captured: with app.test_request_context(): f = open(os.path.join(app.root_path, 'static/index.html')) rv = flask.send_file(f) with app.open_resource('static/index.html') as f: assert rv.data == f.read() assert rv.mimetype == 'text/html' # mimetypes + etag assert len(captured) == 2 app.use_x_sendfile = True with catch_warnings() as captured: with app.test_request_context(): f = open(os.path.join(app.root_path, 'static/index.html')) rv = flask.send_file(f) assert rv.mimetype == 'text/html' assert 'x-sendfile' in rv.headers assert rv.headers['x-sendfile'] == \ os.path.join(app.root_path, 'static/index.html') # mimetypes + etag assert len(captured) == 2 app.use_x_sendfile = False with app.test_request_context(): with catch_warnings() as captured: f = StringIO('Test') rv = flask.send_file(f) assert rv.data == 'Test' assert rv.mimetype == 'application/octet-stream' # etags assert len(captured) == 1 with catch_warnings() as captured: f = StringIO('Test') rv = flask.send_file(f, mimetype='text/plain') assert rv.data == 'Test' assert rv.mimetype == 'text/plain' # etags assert len(captured) == 1 app.use_x_sendfile = True with catch_warnings() as captured: with app.test_request_context(): f = StringIO('Test') rv = flask.send_file(f) assert 'x-sendfile' not in rv.headers # etags assert len(captured) == 1 def test_attachment(self): app = flask.Flask(__name__) with catch_warnings() as captured: with app.test_request_context(): f = open(os.path.join(app.root_path, 'static/index.html')) rv = flask.send_file(f, as_attachment=True) value, options = parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' # mimetypes + etag assert len(captured) == 2 with app.test_request_context(): assert options['filename'] == 'index.html' rv = flask.send_file('static/index.html', as_attachment=True) value, options = parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' assert options['filename'] == 'index.html' with app.test_request_context(): rv = flask.send_file(StringIO('Test'), as_attachment=True, attachment_filename='index.txt', add_etags=False) assert rv.mimetype == 'text/plain' value, options = parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' assert options['filename'] == 'index.txt' class LoggingTestCase(unittest.TestCase): def test_logger_cache(self): app = flask.Flask(__name__) logger1 = app.logger assert app.logger is logger1 assert logger1.name == __name__ app.logger_name = __name__ + '/test_logger_cache' assert app.logger is not logger1 def test_debug_log(self): app = flask.Flask(__name__) app.debug = True @app.route('/') def index(): app.logger.warning('the standard library is dead') app.logger.debug('this is a debug statement') return '' @app.route('/exc') def exc(): 1/0 c = app.test_client() with catch_stderr() as err: c.get('/') out = err.getvalue() assert 'WARNING in flask_tests [' in out assert 'flask_tests.py' in out assert 'the standard library is dead' in out assert 'this is a debug statement' in out with catch_stderr() as err: try: c.get('/exc') except ZeroDivisionError: pass else: assert False, 'debug log ate the exception' def test_exception_logging(self): out = StringIO() app = flask.Flask(__name__) app.logger_name = 'flask_tests/test_exception_logging' app.logger.addHandler(StreamHandler(out)) @app.route('/') def index(): 1/0 rv = app.test_client().get('/') assert rv.status_code == 500 assert 'Internal Server Error' in rv.data err = out.getvalue() assert 'Exception on / [GET]' in err assert 'Traceback (most recent call last):' in err assert '1/0' in err assert 'ZeroDivisionError:' in err def test_processor_exceptions(self): app = flask.Flask(__name__) @app.before_request def before_request(): if trigger == 'before': 1/0 @app.after_request def after_request(response): if trigger == 'after': 1/0 return response @app.route('/') def index(): return 'Foo' @app.errorhandler(500) def internal_server_error(e): return 'Hello Server Error', 500 for trigger in 'before', 'after': rv = app.test_client().get('/') assert rv.status_code == 500 assert rv.data == 'Hello Server Error' class ConfigTestCase(unittest.TestCase): def common_object_test(self, app): assert app.secret_key == 'devkey' assert app.config['TEST_KEY'] == 'foo' assert 'ConfigTestCase' not in app.config def test_config_from_file(self): app = flask.Flask(__name__) app.config.from_pyfile('flask_tests.py') self.common_object_test(app) def test_config_from_object(self): app = flask.Flask(__name__) app.config.from_object(__name__) self.common_object_test(app) def test_config_from_class(self): class Base(object): TEST_KEY = 'foo' class Test(Base): SECRET_KEY = 'devkey' app = flask.Flask(__name__) app.config.from_object(Test) self.common_object_test(app) def test_config_from_envvar(self): import os env = os.environ try: os.environ = {} app = flask.Flask(__name__) try: app.config.from_envvar('FOO_SETTINGS') except RuntimeError, e: assert "'FOO_SETTINGS' is not set" in str(e) else: assert 0, 'expected exception' assert not app.config.from_envvar('FOO_SETTINGS', silent=True) os.environ = {'FOO_SETTINGS': 'flask_tests.py'} assert app.config.from_envvar('FOO_SETTINGS') self.common_object_test(app) finally: os.environ = env def test_config_missing(self): app = flask.Flask(__name__) try: app.config.from_pyfile('missing.cfg') except IOError, e: msg = str(e) assert msg.startswith('[Errno 2] Unable to load configuration ' 'file (No such file or directory):') assert msg.endswith("missing.cfg'") else: assert 0, 'expected config' assert not app.config.from_pyfile('missing.cfg', silent=True) class SubdomainTestCase(unittest.TestCase): 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' c = app.test_client() rv = c.get('/', 'http://localhost/') assert rv.data == 'normal index' rv = c.get('/', 'http://test.localhost/') assert rv.data == 'test index' def test_module_static_path_subdomain(self): app = flask.Flask(__name__) app.config['SERVER_NAME'] = 'example.com' from subdomaintestmodule import mod app.register_module(mod) c = app.test_client() rv = c.get('/static/hello.txt', 'http://foo.example.com/') assert rv.data.strip() == 'Hello Subdomain' 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 c = app.test_client() rv = c.get('/', 'http://mitsuhiko.localhost/') assert rv.data == 'index for mitsuhiko' def test_module_subdomain_support(self): app = flask.Flask(__name__) mod = flask.Module(__name__, 'test', subdomain='testing') app.config['SERVER_NAME'] = 'localhost' @mod.route('/test') def test(): return 'Test' @mod.route('/outside', subdomain='xtesting') def bar(): return 'Outside' app.register_module(mod) c = app.test_client() rv = c.get('/test', 'http://testing.localhost/') assert rv.data == 'Test' rv = c.get('/outside', 'http://xtesting.localhost/') assert rv.data == 'Outside' class TestSignals(unittest.TestCase): def test_template_rendered(self): app = flask.Flask(__name__) @app.route('/') def index(): return flask.render_template('simple_template.html', whiskey=42) recorded = [] def record(sender, template, context): recorded.append((template, context)) flask.template_rendered.connect(record, app) try: app.test_client().get('/') assert len(recorded) == 1 template, context = recorded[0] assert template.name == 'simple_template.html' assert context['whiskey'] == 42 finally: flask.template_rendered.disconnect(record, app) def test_request_signals(self): app = flask.Flask(__name__) calls = [] def before_request_signal(sender): calls.append('before-signal') def after_request_signal(sender, response): assert response.data == 'stuff' calls.append('after-signal') @app.before_request def before_request_handler(): calls.append('before-handler') @app.after_request def after_request_handler(response): calls.append('after-handler') response.data = 'stuff' return response @app.route('/') def index(): calls.append('handler') return 'ignored anyway' flask.request_started.connect(before_request_signal, app) flask.request_finished.connect(after_request_signal, app) try: rv = app.test_client().get('/') assert rv.data == 'stuff' assert calls == ['before-signal', 'before-handler', 'handler', 'after-handler', 'after-signal'] finally: flask.request_started.disconnect(before_request_signal, app) flask.request_finished.disconnect(after_request_signal, app) def test_request_exception_signal(self): app = flask.Flask(__name__) recorded = [] @app.route('/') def index(): 1/0 def record(sender, exception): recorded.append(exception) flask.got_request_exception.connect(record, app) try: assert app.test_client().get('/').status_code == 500 assert len(recorded) == 1 assert isinstance(recorded[0], ZeroDivisionError) finally: flask.got_request_exception.disconnect(record, app) def suite(): from minitwit_tests import MiniTwitTestCase from flaskr_tests import FlaskrTestCase suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ContextTestCase)) suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase)) suite.addTest(unittest.makeSuite(TemplatingTestCase)) suite.addTest(unittest.makeSuite(ModuleTestCase)) suite.addTest(unittest.makeSuite(SendfileTestCase)) suite.addTest(unittest.makeSuite(LoggingTestCase)) suite.addTest(unittest.makeSuite(ConfigTestCase)) suite.addTest(unittest.makeSuite(SubdomainTestCase)) if flask.json_available: suite.addTest(unittest.makeSuite(JSONTestCase)) if flask.signals_available: suite.addTest(unittest.makeSuite(TestSignals)) suite.addTest(unittest.makeSuite(MiniTwitTestCase)) suite.addTest(unittest.makeSuite(FlaskrTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite')