basic.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254
  1. # -*- coding: utf-8 -*-
  2. """
  3. flask.testsuite.basic
  4. ~~~~~~~~~~~~~~~~~~~~~
  5. The basic functionality.
  6. :copyright: (c) 2011 by Armin Ronacher.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import re
  10. import uuid
  11. import flask
  12. import pickle
  13. import unittest
  14. from datetime import datetime
  15. from threading import Thread
  16. from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
  17. from flask._compat import text_type
  18. from werkzeug.exceptions import BadRequest, NotFound
  19. from werkzeug.http import parse_date
  20. from werkzeug.routing import BuildError
  21. class BasicFunctionalityTestCase(FlaskTestCase):
  22. def test_options_work(self):
  23. app = flask.Flask(__name__)
  24. @app.route('/', methods=['GET', 'POST'])
  25. def index():
  26. return 'Hello World'
  27. rv = app.test_client().open('/', method='OPTIONS')
  28. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
  29. self.assert_equal(rv.data, b'')
  30. def test_options_on_multiple_rules(self):
  31. app = flask.Flask(__name__)
  32. @app.route('/', methods=['GET', 'POST'])
  33. def index():
  34. return 'Hello World'
  35. @app.route('/', methods=['PUT'])
  36. def index_put():
  37. return 'Aha!'
  38. rv = app.test_client().open('/', method='OPTIONS')
  39. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'])
  40. def test_options_handling_disabled(self):
  41. app = flask.Flask(__name__)
  42. def index():
  43. return 'Hello World!'
  44. index.provide_automatic_options = False
  45. app.route('/')(index)
  46. rv = app.test_client().open('/', method='OPTIONS')
  47. self.assert_equal(rv.status_code, 405)
  48. app = flask.Flask(__name__)
  49. def index2():
  50. return 'Hello World!'
  51. index2.provide_automatic_options = True
  52. app.route('/', methods=['OPTIONS'])(index2)
  53. rv = app.test_client().open('/', method='OPTIONS')
  54. self.assert_equal(sorted(rv.allow), ['OPTIONS'])
  55. def test_request_dispatching(self):
  56. app = flask.Flask(__name__)
  57. @app.route('/')
  58. def index():
  59. return flask.request.method
  60. @app.route('/more', methods=['GET', 'POST'])
  61. def more():
  62. return flask.request.method
  63. c = app.test_client()
  64. self.assert_equal(c.get('/').data, b'GET')
  65. rv = c.post('/')
  66. self.assert_equal(rv.status_code, 405)
  67. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS'])
  68. rv = c.head('/')
  69. self.assert_equal(rv.status_code, 200)
  70. self.assert_false(rv.data) # head truncates
  71. self.assert_equal(c.post('/more').data, b'POST')
  72. self.assert_equal(c.get('/more').data, b'GET')
  73. rv = c.delete('/more')
  74. self.assert_equal(rv.status_code, 405)
  75. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
  76. def test_url_mapping(self):
  77. app = flask.Flask(__name__)
  78. def index():
  79. return flask.request.method
  80. def more():
  81. return flask.request.method
  82. app.add_url_rule('/', 'index', index)
  83. app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'])
  84. c = app.test_client()
  85. self.assert_equal(c.get('/').data, b'GET')
  86. rv = c.post('/')
  87. self.assert_equal(rv.status_code, 405)
  88. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS'])
  89. rv = c.head('/')
  90. self.assert_equal(rv.status_code, 200)
  91. self.assert_false(rv.data) # head truncates
  92. self.assert_equal(c.post('/more').data, b'POST')
  93. self.assert_equal(c.get('/more').data, b'GET')
  94. rv = c.delete('/more')
  95. self.assert_equal(rv.status_code, 405)
  96. self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
  97. def test_werkzeug_routing(self):
  98. from werkzeug.routing import Submount, Rule
  99. app = flask.Flask(__name__)
  100. app.url_map.add(Submount('/foo', [
  101. Rule('/bar', endpoint='bar'),
  102. Rule('/', endpoint='index')
  103. ]))
  104. def bar():
  105. return 'bar'
  106. def index():
  107. return 'index'
  108. app.view_functions['bar'] = bar
  109. app.view_functions['index'] = index
  110. c = app.test_client()
  111. self.assert_equal(c.get('/foo/').data, b'index')
  112. self.assert_equal(c.get('/foo/bar').data, b'bar')
  113. def test_endpoint_decorator(self):
  114. from werkzeug.routing import Submount, Rule
  115. app = flask.Flask(__name__)
  116. app.url_map.add(Submount('/foo', [
  117. Rule('/bar', endpoint='bar'),
  118. Rule('/', endpoint='index')
  119. ]))
  120. @app.endpoint('bar')
  121. def bar():
  122. return 'bar'
  123. @app.endpoint('index')
  124. def index():
  125. return 'index'
  126. c = app.test_client()
  127. self.assert_equal(c.get('/foo/').data, b'index')
  128. self.assert_equal(c.get('/foo/bar').data, b'bar')
  129. def test_session(self):
  130. app = flask.Flask(__name__)
  131. app.secret_key = 'testkey'
  132. @app.route('/set', methods=['POST'])
  133. def set():
  134. flask.session['value'] = flask.request.form['value']
  135. return 'value set'
  136. @app.route('/get')
  137. def get():
  138. return flask.session['value']
  139. c = app.test_client()
  140. self.assert_equal(c.post('/set', data={'value': '42'}).data, b'value set')
  141. self.assert_equal(c.get('/get').data, b'42')
  142. def test_session_using_server_name(self):
  143. app = flask.Flask(__name__)
  144. app.config.update(
  145. SECRET_KEY='foo',
  146. SERVER_NAME='example.com'
  147. )
  148. @app.route('/')
  149. def index():
  150. flask.session['testing'] = 42
  151. return 'Hello World'
  152. rv = app.test_client().get('/', 'http://example.com/')
  153. self.assert_in('domain=.example.com', rv.headers['set-cookie'].lower())
  154. self.assert_in('httponly', rv.headers['set-cookie'].lower())
  155. def test_session_using_server_name_and_port(self):
  156. app = flask.Flask(__name__)
  157. app.config.update(
  158. SECRET_KEY='foo',
  159. SERVER_NAME='example.com:8080'
  160. )
  161. @app.route('/')
  162. def index():
  163. flask.session['testing'] = 42
  164. return 'Hello World'
  165. rv = app.test_client().get('/', 'http://example.com:8080/')
  166. self.assert_in('domain=.example.com', rv.headers['set-cookie'].lower())
  167. self.assert_in('httponly', rv.headers['set-cookie'].lower())
  168. def test_session_using_server_name_port_and_path(self):
  169. app = flask.Flask(__name__)
  170. app.config.update(
  171. SECRET_KEY='foo',
  172. SERVER_NAME='example.com:8080',
  173. APPLICATION_ROOT='/foo'
  174. )
  175. @app.route('/')
  176. def index():
  177. flask.session['testing'] = 42
  178. return 'Hello World'
  179. rv = app.test_client().get('/', 'http://example.com:8080/foo')
  180. self.assert_in('domain=example.com', rv.headers['set-cookie'].lower())
  181. self.assert_in('path=/foo', rv.headers['set-cookie'].lower())
  182. self.assert_in('httponly', rv.headers['set-cookie'].lower())
  183. def test_session_using_application_root(self):
  184. class PrefixPathMiddleware(object):
  185. def __init__(self, app, prefix):
  186. self.app = app
  187. self.prefix = prefix
  188. def __call__(self, environ, start_response):
  189. environ['SCRIPT_NAME'] = self.prefix
  190. return self.app(environ, start_response)
  191. app = flask.Flask(__name__)
  192. app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar')
  193. app.config.update(
  194. SECRET_KEY='foo',
  195. APPLICATION_ROOT='/bar'
  196. )
  197. @app.route('/')
  198. def index():
  199. flask.session['testing'] = 42
  200. return 'Hello World'
  201. rv = app.test_client().get('/', 'http://example.com:8080/')
  202. self.assert_in('path=/bar', rv.headers['set-cookie'].lower())
  203. def test_session_using_session_settings(self):
  204. app = flask.Flask(__name__)
  205. app.config.update(
  206. SECRET_KEY='foo',
  207. SERVER_NAME='www.example.com:8080',
  208. APPLICATION_ROOT='/test',
  209. SESSION_COOKIE_DOMAIN='.example.com',
  210. SESSION_COOKIE_HTTPONLY=False,
  211. SESSION_COOKIE_SECURE=True,
  212. SESSION_COOKIE_PATH='/'
  213. )
  214. @app.route('/')
  215. def index():
  216. flask.session['testing'] = 42
  217. return 'Hello World'
  218. rv = app.test_client().get('/', 'http://www.example.com:8080/test/')
  219. cookie = rv.headers['set-cookie'].lower()
  220. self.assert_in('domain=.example.com', cookie)
  221. self.assert_in('path=/', cookie)
  222. self.assert_in('secure', cookie)
  223. self.assert_not_in('httponly', cookie)
  224. def test_missing_session(self):
  225. app = flask.Flask(__name__)
  226. def expect_exception(f, *args, **kwargs):
  227. try:
  228. f(*args, **kwargs)
  229. except RuntimeError as e:
  230. self.assert_true(e.args and 'session is unavailable' in e.args[0])
  231. else:
  232. self.assert_true(False, 'expected exception')
  233. with app.test_request_context():
  234. self.assert_true(flask.session.get('missing_key') is None)
  235. expect_exception(flask.session.__setitem__, 'foo', 42)
  236. expect_exception(flask.session.pop, 'foo')
  237. def test_session_expiration(self):
  238. permanent = True
  239. app = flask.Flask(__name__)
  240. app.secret_key = 'testkey'
  241. @app.route('/')
  242. def index():
  243. flask.session['test'] = 42
  244. flask.session.permanent = permanent
  245. return ''
  246. @app.route('/test')
  247. def test():
  248. return text_type(flask.session.permanent)
  249. client = app.test_client()
  250. rv = client.get('/')
  251. self.assert_in('set-cookie', rv.headers)
  252. match = re.search(r'\bexpires=([^;]+)(?i)', rv.headers['set-cookie'])
  253. expires = parse_date(match.group())
  254. expected = datetime.utcnow() + app.permanent_session_lifetime
  255. self.assert_equal(expires.year, expected.year)
  256. self.assert_equal(expires.month, expected.month)
  257. self.assert_equal(expires.day, expected.day)
  258. rv = client.get('/test')
  259. self.assert_equal(rv.data, b'True')
  260. permanent = False
  261. rv = app.test_client().get('/')
  262. self.assert_in('set-cookie', rv.headers)
  263. match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
  264. self.assert_true(match is None)
  265. def test_session_stored_last(self):
  266. app = flask.Flask(__name__)
  267. app.secret_key = 'development-key'
  268. app.testing = True
  269. @app.after_request
  270. def modify_session(response):
  271. flask.session['foo'] = 42
  272. return response
  273. @app.route('/')
  274. def dump_session_contents():
  275. return repr(flask.session.get('foo'))
  276. c = app.test_client()
  277. self.assert_equal(c.get('/').data, b'None')
  278. self.assert_equal(c.get('/').data, b'42')
  279. def test_session_special_types(self):
  280. app = flask.Flask(__name__)
  281. app.secret_key = 'development-key'
  282. app.testing = True
  283. now = datetime.utcnow().replace(microsecond=0)
  284. the_uuid = uuid.uuid4()
  285. @app.after_request
  286. def modify_session(response):
  287. flask.session['m'] = flask.Markup('Hello!')
  288. flask.session['u'] = the_uuid
  289. flask.session['dt'] = now
  290. flask.session['b'] = b'\xff'
  291. flask.session['t'] = (1, 2, 3)
  292. return response
  293. @app.route('/')
  294. def dump_session_contents():
  295. return pickle.dumps(dict(flask.session))
  296. c = app.test_client()
  297. c.get('/')
  298. rv = pickle.loads(c.get('/').data)
  299. self.assert_equal(rv['m'], flask.Markup('Hello!'))
  300. self.assert_equal(type(rv['m']), flask.Markup)
  301. self.assert_equal(rv['dt'], now)
  302. self.assert_equal(rv['u'], the_uuid)
  303. self.assert_equal(rv['b'], b'\xff')
  304. self.assert_equal(type(rv['b']), bytes)
  305. self.assert_equal(rv['t'], (1, 2, 3))
  306. def test_flashes(self):
  307. app = flask.Flask(__name__)
  308. app.secret_key = 'testkey'
  309. with app.test_request_context():
  310. self.assert_false(flask.session.modified)
  311. flask.flash('Zap')
  312. flask.session.modified = False
  313. flask.flash('Zip')
  314. self.assert_true(flask.session.modified)
  315. self.assert_equal(list(flask.get_flashed_messages()), ['Zap', 'Zip'])
  316. def test_extended_flashing(self):
  317. # Be sure app.testing=True below, else tests can fail silently.
  318. #
  319. # Specifically, if app.testing is not set to True, the AssertionErrors
  320. # in the view functions will cause a 500 response to the test client
  321. # instead of propagating exceptions.
  322. app = flask.Flask(__name__)
  323. app.secret_key = 'testkey'
  324. app.testing = True
  325. @app.route('/')
  326. def index():
  327. flask.flash(u'Hello World')
  328. flask.flash(u'Hello World', 'error')
  329. flask.flash(flask.Markup(u'<em>Testing</em>'), 'warning')
  330. return ''
  331. @app.route('/test/')
  332. def test():
  333. messages = flask.get_flashed_messages()
  334. self.assert_equal(len(messages), 3)
  335. self.assert_equal(messages[0], u'Hello World')
  336. self.assert_equal(messages[1], u'Hello World')
  337. self.assert_equal(messages[2], flask.Markup(u'<em>Testing</em>'))
  338. return ''
  339. @app.route('/test_with_categories/')
  340. def test_with_categories():
  341. messages = flask.get_flashed_messages(with_categories=True)
  342. self.assert_equal(len(messages), 3)
  343. self.assert_equal(messages[0], ('message', u'Hello World'))
  344. self.assert_equal(messages[1], ('error', u'Hello World'))
  345. self.assert_equal(messages[2], ('warning', flask.Markup(u'<em>Testing</em>')))
  346. return ''
  347. @app.route('/test_filter/')
  348. def test_filter():
  349. messages = flask.get_flashed_messages(category_filter=['message'], with_categories=True)
  350. self.assert_equal(len(messages), 1)
  351. self.assert_equal(messages[0], ('message', u'Hello World'))
  352. return ''
  353. @app.route('/test_filters/')
  354. def test_filters():
  355. messages = flask.get_flashed_messages(category_filter=['message', 'warning'], with_categories=True)
  356. self.assert_equal(len(messages), 2)
  357. self.assert_equal(messages[0], ('message', u'Hello World'))
  358. self.assert_equal(messages[1], ('warning', flask.Markup(u'<em>Testing</em>')))
  359. return ''
  360. @app.route('/test_filters_without_returning_categories/')
  361. def test_filters2():
  362. messages = flask.get_flashed_messages(category_filter=['message', 'warning'])
  363. self.assert_equal(len(messages), 2)
  364. self.assert_equal(messages[0], u'Hello World')
  365. self.assert_equal(messages[1], flask.Markup(u'<em>Testing</em>'))
  366. return ''
  367. # Create new test client on each test to clean flashed messages.
  368. c = app.test_client()
  369. c.get('/')
  370. c.get('/test/')
  371. c = app.test_client()
  372. c.get('/')
  373. c.get('/test_with_categories/')
  374. c = app.test_client()
  375. c.get('/')
  376. c.get('/test_filter/')
  377. c = app.test_client()
  378. c.get('/')
  379. c.get('/test_filters/')
  380. c = app.test_client()
  381. c.get('/')
  382. c.get('/test_filters_without_returning_categories/')
  383. def test_request_processing(self):
  384. app = flask.Flask(__name__)
  385. evts = []
  386. @app.before_request
  387. def before_request():
  388. evts.append('before')
  389. @app.after_request
  390. def after_request(response):
  391. response.data += b'|after'
  392. evts.append('after')
  393. return response
  394. @app.route('/')
  395. def index():
  396. self.assert_in('before', evts)
  397. self.assert_not_in('after', evts)
  398. return 'request'
  399. self.assert_not_in('after', evts)
  400. rv = app.test_client().get('/').data
  401. self.assert_in('after', evts)
  402. self.assert_equal(rv, b'request|after')
  403. def test_after_request_processing(self):
  404. app = flask.Flask(__name__)
  405. app.testing = True
  406. @app.route('/')
  407. def index():
  408. @flask.after_this_request
  409. def foo(response):
  410. response.headers['X-Foo'] = 'a header'
  411. return response
  412. return 'Test'
  413. c = app.test_client()
  414. resp = c.get('/')
  415. self.assertEqual(resp.status_code, 200)
  416. self.assertEqual(resp.headers['X-Foo'], 'a header')
  417. def test_teardown_request_handler(self):
  418. called = []
  419. app = flask.Flask(__name__)
  420. @app.teardown_request
  421. def teardown_request(exc):
  422. called.append(True)
  423. return "Ignored"
  424. @app.route('/')
  425. def root():
  426. return "Response"
  427. rv = app.test_client().get('/')
  428. self.assert_equal(rv.status_code, 200)
  429. self.assert_in(b'Response', rv.data)
  430. self.assert_equal(len(called), 1)
  431. def test_teardown_request_handler_debug_mode(self):
  432. called = []
  433. app = flask.Flask(__name__)
  434. app.testing = True
  435. @app.teardown_request
  436. def teardown_request(exc):
  437. called.append(True)
  438. return "Ignored"
  439. @app.route('/')
  440. def root():
  441. return "Response"
  442. rv = app.test_client().get('/')
  443. self.assert_equal(rv.status_code, 200)
  444. self.assert_in(b'Response', rv.data)
  445. self.assert_equal(len(called), 1)
  446. def test_teardown_request_handler_error(self):
  447. called = []
  448. app = flask.Flask(__name__)
  449. @app.teardown_request
  450. def teardown_request1(exc):
  451. self.assert_equal(type(exc), ZeroDivisionError)
  452. called.append(True)
  453. # This raises a new error and blows away sys.exc_info(), so we can
  454. # test that all teardown_requests get passed the same original
  455. # exception.
  456. try:
  457. raise TypeError()
  458. except:
  459. pass
  460. @app.teardown_request
  461. def teardown_request2(exc):
  462. self.assert_equal(type(exc), ZeroDivisionError)
  463. called.append(True)
  464. # This raises a new error and blows away sys.exc_info(), so we can
  465. # test that all teardown_requests get passed the same original
  466. # exception.
  467. try:
  468. raise TypeError()
  469. except:
  470. pass
  471. @app.route('/')
  472. def fails():
  473. 1 // 0
  474. rv = app.test_client().get('/')
  475. self.assert_equal(rv.status_code, 500)
  476. self.assert_in(b'Internal Server Error', rv.data)
  477. self.assert_equal(len(called), 2)
  478. def test_before_after_request_order(self):
  479. called = []
  480. app = flask.Flask(__name__)
  481. @app.before_request
  482. def before1():
  483. called.append(1)
  484. @app.before_request
  485. def before2():
  486. called.append(2)
  487. @app.after_request
  488. def after1(response):
  489. called.append(4)
  490. return response
  491. @app.after_request
  492. def after2(response):
  493. called.append(3)
  494. return response
  495. @app.teardown_request
  496. def finish1(exc):
  497. called.append(6)
  498. @app.teardown_request
  499. def finish2(exc):
  500. called.append(5)
  501. @app.route('/')
  502. def index():
  503. return '42'
  504. rv = app.test_client().get('/')
  505. self.assert_equal(rv.data, b'42')
  506. self.assert_equal(called, [1, 2, 3, 4, 5, 6])
  507. def test_error_handling(self):
  508. app = flask.Flask(__name__)
  509. @app.errorhandler(404)
  510. def not_found(e):
  511. return 'not found', 404
  512. @app.errorhandler(500)
  513. def internal_server_error(e):
  514. return 'internal server error', 500
  515. @app.route('/')
  516. def index():
  517. flask.abort(404)
  518. @app.route('/error')
  519. def error():
  520. 1 // 0
  521. c = app.test_client()
  522. rv = c.get('/')
  523. self.assert_equal(rv.status_code, 404)
  524. self.assert_equal(rv.data, b'not found')
  525. rv = c.get('/error')
  526. self.assert_equal(rv.status_code, 500)
  527. self.assert_equal(b'internal server error', rv.data)
  528. def test_before_request_and_routing_errors(self):
  529. app = flask.Flask(__name__)
  530. @app.before_request
  531. def attach_something():
  532. flask.g.something = 'value'
  533. @app.errorhandler(404)
  534. def return_something(error):
  535. return flask.g.something, 404
  536. rv = app.test_client().get('/')
  537. self.assert_equal(rv.status_code, 404)
  538. self.assert_equal(rv.data, b'value')
  539. def test_user_error_handling(self):
  540. class MyException(Exception):
  541. pass
  542. app = flask.Flask(__name__)
  543. @app.errorhandler(MyException)
  544. def handle_my_exception(e):
  545. self.assert_true(isinstance(e, MyException))
  546. return '42'
  547. @app.route('/')
  548. def index():
  549. raise MyException()
  550. c = app.test_client()
  551. self.assert_equal(c.get('/').data, b'42')
  552. def test_trapping_of_bad_request_key_errors(self):
  553. app = flask.Flask(__name__)
  554. app.testing = True
  555. @app.route('/fail')
  556. def fail():
  557. flask.request.form['missing_key']
  558. c = app.test_client()
  559. self.assert_equal(c.get('/fail').status_code, 400)
  560. app.config['TRAP_BAD_REQUEST_ERRORS'] = True
  561. c = app.test_client()
  562. try:
  563. c.get('/fail')
  564. except KeyError as e:
  565. self.assert_true(isinstance(e, BadRequest))
  566. else:
  567. self.fail('Expected exception')
  568. def test_trapping_of_all_http_exceptions(self):
  569. app = flask.Flask(__name__)
  570. app.testing = True
  571. app.config['TRAP_HTTP_EXCEPTIONS'] = True
  572. @app.route('/fail')
  573. def fail():
  574. flask.abort(404)
  575. c = app.test_client()
  576. try:
  577. c.get('/fail')
  578. except NotFound as e:
  579. pass
  580. else:
  581. self.fail('Expected exception')
  582. def test_enctype_debug_helper(self):
  583. from flask.debughelpers import DebugFilesKeyError
  584. app = flask.Flask(__name__)
  585. app.debug = True
  586. @app.route('/fail', methods=['POST'])
  587. def index():
  588. return flask.request.files['foo'].filename
  589. # with statement is important because we leave an exception on the
  590. # stack otherwise and we want to ensure that this is not the case
  591. # to not negatively affect other tests.
  592. with app.test_client() as c:
  593. try:
  594. c.post('/fail', data={'foo': 'index.txt'})
  595. except DebugFilesKeyError as e:
  596. self.assert_in('no file contents were transmitted', str(e))
  597. self.assert_in('This was submitted: "index.txt"', str(e))
  598. else:
  599. self.fail('Expected exception')
  600. def test_response_creation(self):
  601. app = flask.Flask(__name__)
  602. @app.route('/unicode')
  603. def from_unicode():
  604. return u'Hällo Wörld'
  605. @app.route('/string')
  606. def from_string():
  607. return u'Hällo Wörld'.encode('utf-8')
  608. @app.route('/args')
  609. def from_tuple():
  610. return 'Meh', 400, {
  611. 'X-Foo': 'Testing',
  612. 'Content-Type': 'text/plain; charset=utf-8'
  613. }
  614. c = app.test_client()
  615. self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8'))
  616. self.assert_equal(c.get('/string').data, u'Hällo Wörld'.encode('utf-8'))
  617. rv = c.get('/args')
  618. self.assert_equal(rv.data, b'Meh')
  619. self.assert_equal(rv.headers['X-Foo'], 'Testing')
  620. self.assert_equal(rv.status_code, 400)
  621. self.assert_equal(rv.mimetype, 'text/plain')
  622. def test_make_response(self):
  623. app = flask.Flask(__name__)
  624. with app.test_request_context():
  625. rv = flask.make_response()
  626. self.assert_equal(rv.status_code, 200)
  627. self.assert_equal(rv.data, b'')
  628. self.assert_equal(rv.mimetype, 'text/html')
  629. rv = flask.make_response('Awesome')
  630. self.assert_equal(rv.status_code, 200)
  631. self.assert_equal(rv.data, b'Awesome')
  632. self.assert_equal(rv.mimetype, 'text/html')
  633. rv = flask.make_response('W00t', 404)
  634. self.assert_equal(rv.status_code, 404)
  635. self.assert_equal(rv.data, b'W00t')
  636. self.assert_equal(rv.mimetype, 'text/html')
  637. def test_make_response_with_response_instance(self):
  638. app = flask.Flask(__name__)
  639. with app.test_request_context():
  640. rv = flask.make_response(
  641. flask.jsonify({'msg': 'W00t'}), 400)
  642. self.assertEqual(rv.status_code, 400)
  643. self.assertEqual(rv.data, b'{\n "msg": "W00t"\n}')
  644. self.assertEqual(rv.mimetype, 'application/json')
  645. rv = flask.make_response(
  646. flask.Response(''), 400)
  647. self.assertEqual(rv.status_code, 400)
  648. self.assertEqual(rv.data, b'')
  649. self.assertEqual(rv.mimetype, 'text/html')
  650. rv = flask.make_response(
  651. flask.Response('', headers={'Content-Type': 'text/html'}),
  652. 400, [('X-Foo', 'bar')])
  653. self.assertEqual(rv.status_code, 400)
  654. self.assertEqual(rv.headers['Content-Type'], 'text/html')
  655. self.assertEqual(rv.headers['X-Foo'], 'bar')
  656. def test_url_generation(self):
  657. app = flask.Flask(__name__)
  658. @app.route('/hello/<name>', methods=['POST'])
  659. def hello():
  660. pass
  661. with app.test_request_context():
  662. self.assert_equal(flask.url_for('hello', name='test x'), '/hello/test%20x')
  663. self.assert_equal(flask.url_for('hello', name='test x', _external=True),
  664. 'http://localhost/hello/test%20x')
  665. def test_build_error_handler(self):
  666. app = flask.Flask(__name__)
  667. # Test base case, a URL which results in a BuildError.
  668. with app.test_request_context():
  669. self.assertRaises(BuildError, flask.url_for, 'spam')
  670. # Verify the error is re-raised if not the current exception.
  671. try:
  672. with app.test_request_context():
  673. flask.url_for('spam')
  674. except BuildError as err:
  675. error = err
  676. try:
  677. raise RuntimeError('Test case where BuildError is not current.')
  678. except RuntimeError:
  679. self.assertRaises(BuildError, app.handle_url_build_error, error, 'spam', {})
  680. # Test a custom handler.
  681. def handler(error, endpoint, values):
  682. # Just a test.
  683. return '/test_handler/'
  684. app.url_build_error_handlers.append(handler)
  685. with app.test_request_context():
  686. self.assert_equal(flask.url_for('spam'), '/test_handler/')
  687. def test_custom_converters(self):
  688. from werkzeug.routing import BaseConverter
  689. class ListConverter(BaseConverter):
  690. def to_python(self, value):
  691. return value.split(',')
  692. def to_url(self, value):
  693. base_to_url = super(ListConverter, self).to_url
  694. return ','.join(base_to_url(x) for x in value)
  695. app = flask.Flask(__name__)
  696. app.url_map.converters['list'] = ListConverter
  697. @app.route('/<list:args>')
  698. def index(args):
  699. return '|'.join(args)
  700. c = app.test_client()
  701. self.assert_equal(c.get('/1,2,3').data, b'1|2|3')
  702. def test_static_files(self):
  703. app = flask.Flask(__name__)
  704. app.testing = True
  705. rv = app.test_client().get('/static/index.html')
  706. self.assert_equal(rv.status_code, 200)
  707. self.assert_equal(rv.data.strip(), b'<h1>Hello World!</h1>')
  708. with app.test_request_context():
  709. self.assert_equal(flask.url_for('static', filename='index.html'),
  710. '/static/index.html')
  711. rv.close()
  712. def test_none_response(self):
  713. app = flask.Flask(__name__)
  714. @app.route('/')
  715. def test():
  716. return None
  717. try:
  718. app.test_client().get('/')
  719. except ValueError as e:
  720. self.assert_equal(str(e), 'View function did not return a response')
  721. pass
  722. else:
  723. self.assert_true("Expected ValueError")
  724. def test_request_locals(self):
  725. self.assert_equal(repr(flask.g), '<LocalProxy unbound>')
  726. self.assertFalse(flask.g)
  727. def test_test_app_proper_environ(self):
  728. app = flask.Flask(__name__)
  729. app.config.update(
  730. SERVER_NAME='localhost.localdomain:5000'
  731. )
  732. @app.route('/')
  733. def index():
  734. return 'Foo'
  735. @app.route('/', subdomain='foo')
  736. def subdomain():
  737. return 'Foo SubDomain'
  738. rv = app.test_client().get('/')
  739. self.assert_equal(rv.data, b'Foo')
  740. rv = app.test_client().get('/', 'http://localhost.localdomain:5000')
  741. self.assert_equal(rv.data, b'Foo')
  742. rv = app.test_client().get('/', 'https://localhost.localdomain:5000')
  743. self.assert_equal(rv.data, b'Foo')
  744. app.config.update(SERVER_NAME='localhost.localdomain')
  745. rv = app.test_client().get('/', 'https://localhost.localdomain')
  746. self.assert_equal(rv.data, b'Foo')
  747. try:
  748. app.config.update(SERVER_NAME='localhost.localdomain:443')
  749. rv = app.test_client().get('/', 'https://localhost.localdomain')
  750. # Werkzeug 0.8
  751. self.assert_equal(rv.status_code, 404)
  752. except ValueError as e:
  753. # Werkzeug 0.7
  754. self.assert_equal(str(e), "the server name provided " +
  755. "('localhost.localdomain:443') does not match the " + \
  756. "server name from the WSGI environment ('localhost.localdomain')")
  757. try:
  758. app.config.update(SERVER_NAME='localhost.localdomain')
  759. rv = app.test_client().get('/', 'http://foo.localhost')
  760. # Werkzeug 0.8
  761. self.assert_equal(rv.status_code, 404)
  762. except ValueError as e:
  763. # Werkzeug 0.7
  764. self.assert_equal(str(e), "the server name provided " + \
  765. "('localhost.localdomain') does not match the " + \
  766. "server name from the WSGI environment ('foo.localhost')")
  767. rv = app.test_client().get('/', 'http://foo.localhost.localdomain')
  768. self.assert_equal(rv.data, b'Foo SubDomain')
  769. def test_exception_propagation(self):
  770. def apprunner(configkey):
  771. app = flask.Flask(__name__)
  772. @app.route('/')
  773. def index():
  774. 1 // 0
  775. c = app.test_client()
  776. if config_key is not None:
  777. app.config[config_key] = True
  778. try:
  779. resp = c.get('/')
  780. except Exception:
  781. pass
  782. else:
  783. self.fail('expected exception')
  784. else:
  785. self.assert_equal(c.get('/').status_code, 500)
  786. # we have to run this test in an isolated thread because if the
  787. # debug flag is set to true and an exception happens the context is
  788. # not torn down. This causes other tests that run after this fail
  789. # when they expect no exception on the stack.
  790. for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None:
  791. t = Thread(target=apprunner, args=(config_key,))
  792. t.start()
  793. t.join()
  794. def test_max_content_length(self):
  795. app = flask.Flask(__name__)
  796. app.config['MAX_CONTENT_LENGTH'] = 64
  797. @app.before_request
  798. def always_first():
  799. flask.request.form['myfile']
  800. self.assert_true(False)
  801. @app.route('/accept', methods=['POST'])
  802. def accept_file():
  803. flask.request.form['myfile']
  804. self.assert_true(False)
  805. @app.errorhandler(413)
  806. def catcher(error):
  807. return '42'
  808. c = app.test_client()
  809. rv = c.post('/accept', data={'myfile': 'foo' * 100})
  810. self.assert_equal(rv.data, b'42')
  811. def test_url_processors(self):
  812. app = flask.Flask(__name__)
  813. @app.url_defaults
  814. def add_language_code(endpoint, values):
  815. if flask.g.lang_code is not None and \
  816. app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
  817. values.setdefault('lang_code', flask.g.lang_code)
  818. @app.url_value_preprocessor
  819. def pull_lang_code(endpoint, values):
  820. flask.g.lang_code = values.pop('lang_code', None)
  821. @app.route('/<lang_code>/')
  822. def index():
  823. return flask.url_for('about')
  824. @app.route('/<lang_code>/about')
  825. def about():
  826. return flask.url_for('something_else')
  827. @app.route('/foo')
  828. def something_else():
  829. return flask.url_for('about', lang_code='en')
  830. c = app.test_client()
  831. self.assert_equal(c.get('/de/').data, b'/de/about')
  832. self.assert_equal(c.get('/de/about').data, b'/foo')
  833. self.assert_equal(c.get('/foo').data, b'/en/about')
  834. def test_inject_blueprint_url_defaults(self):
  835. app = flask.Flask(__name__)
  836. bp = flask.Blueprint('foo.bar.baz', __name__,
  837. template_folder='template')
  838. @bp.url_defaults
  839. def bp_defaults(endpoint, values):
  840. values['page'] = 'login'
  841. @bp.route('/<page>')
  842. def view(page): pass
  843. app.register_blueprint(bp)
  844. values = dict()
  845. app.inject_url_defaults('foo.bar.baz.view', values)
  846. expected = dict(page='login')
  847. self.assert_equal(values, expected)
  848. with app.test_request_context('/somepage'):
  849. url = flask.url_for('foo.bar.baz.view')
  850. expected = '/login'
  851. self.assert_equal(url, expected)
  852. def test_nonascii_pathinfo(self):
  853. app = flask.Flask(__name__)
  854. app.testing = True
  855. @app.route(u'/киртест')
  856. def index():
  857. return 'Hello World!'
  858. c = app.test_client()
  859. rv = c.get(u'/киртест')
  860. self.assert_equal(rv.data, b'Hello World!')
  861. def test_debug_mode_complains_after_first_request(self):
  862. app = flask.Flask(__name__)
  863. app.debug = True
  864. @app.route('/')
  865. def index():
  866. return 'Awesome'
  867. self.assert_false(app.got_first_request)
  868. self.assert_equal(app.test_client().get('/').data, b'Awesome')
  869. try:
  870. @app.route('/foo')
  871. def broken():
  872. return 'Meh'
  873. except AssertionError as e:
  874. self.assert_in('A setup function was called', str(e))
  875. else:
  876. self.fail('Expected exception')
  877. app.debug = False
  878. @app.route('/foo')
  879. def working():
  880. return 'Meh'
  881. self.assert_equal(app.test_client().get('/foo').data, b'Meh')
  882. self.assert_true(app.got_first_request)
  883. def test_before_first_request_functions(self):
  884. got = []
  885. app = flask.Flask(__name__)
  886. @app.before_first_request
  887. def foo():
  888. got.append(42)
  889. c = app.test_client()
  890. c.get('/')
  891. self.assert_equal(got, [42])
  892. c.get('/')
  893. self.assert_equal(got, [42])
  894. self.assert_true(app.got_first_request)
  895. def test_routing_redirect_debugging(self):
  896. app = flask.Flask(__name__)
  897. app.debug = True
  898. @app.route('/foo/', methods=['GET', 'POST'])
  899. def foo():
  900. return 'success'
  901. with app.test_client() as c:
  902. try:
  903. c.post('/foo', data={})
  904. except AssertionError as e:
  905. self.assert_in('http://localhost/foo/', str(e))
  906. self.assert_in('Make sure to directly send your POST-request '
  907. 'to this URL', str(e))
  908. else:
  909. self.fail('Expected exception')
  910. rv = c.get('/foo', data={}, follow_redirects=True)
  911. self.assert_equal(rv.data, b'success')
  912. app.debug = False
  913. with app.test_client() as c:
  914. rv = c.post('/foo', data={}, follow_redirects=True)
  915. self.assert_equal(rv.data, b'success')
  916. def test_route_decorator_custom_endpoint(self):
  917. app = flask.Flask(__name__)
  918. app.debug = True
  919. @app.route('/foo/')
  920. def foo():
  921. return flask.request.endpoint
  922. @app.route('/bar/', endpoint='bar')
  923. def for_bar():
  924. return flask.request.endpoint
  925. @app.route('/bar/123', endpoint='123')
  926. def for_bar_foo():
  927. return flask.request.endpoint
  928. with app.test_request_context():
  929. assert flask.url_for('foo') == '/foo/'
  930. assert flask.url_for('bar') == '/bar/'
  931. assert flask.url_for('123') == '/bar/123'
  932. c = app.test_client()
  933. self.assertEqual(c.get('/foo/').data, b'foo')
  934. self.assertEqual(c.get('/bar/').data, b'bar')
  935. self.assertEqual(c.get('/bar/123').data, b'123')
  936. def test_preserve_only_once(self):
  937. app = flask.Flask(__name__)
  938. app.debug = True
  939. @app.route('/fail')
  940. def fail_func():
  941. 1 // 0
  942. c = app.test_client()
  943. for x in range(3):
  944. with self.assert_raises(ZeroDivisionError):
  945. c.get('/fail')
  946. self.assert_true(flask._request_ctx_stack.top is not None)
  947. self.assert_true(flask._app_ctx_stack.top is not None)
  948. # implicit appctx disappears too
  949. flask._request_ctx_stack.top.pop()
  950. self.assert_true(flask._request_ctx_stack.top is None)
  951. self.assert_true(flask._app_ctx_stack.top is None)
  952. def test_preserve_remembers_exception(self):
  953. app = flask.Flask(__name__)
  954. app.debug = True
  955. errors = []
  956. @app.route('/fail')
  957. def fail_func():
  958. 1 // 0
  959. @app.route('/success')
  960. def success_func():
  961. return 'Okay'
  962. @app.teardown_request
  963. def teardown_handler(exc):
  964. errors.append(exc)
  965. c = app.test_client()
  966. # After this failure we did not yet call the teardown handler
  967. with self.assert_raises(ZeroDivisionError):
  968. c.get('/fail')
  969. self.assert_equal(errors, [])
  970. # But this request triggers it, and it's an error
  971. c.get('/success')
  972. self.assert_equal(len(errors), 2)
  973. self.assert_true(isinstance(errors[0], ZeroDivisionError))
  974. # At this point another request does nothing.
  975. c.get('/success')
  976. self.assert_equal(len(errors), 3)
  977. self.assert_equal(errors[1], None)
  978. def test_get_method_on_g(self):
  979. app = flask.Flask(__name__)
  980. app.testing = True
  981. with app.app_context():
  982. self.assert_equal(flask.g.get('x'), None)
  983. self.assert_equal(flask.g.get('x', 11), 11)
  984. flask.g.x = 42
  985. self.assert_equal(flask.g.get('x'), 42)
  986. self.assert_equal(flask.g.x, 42)
  987. def test_g_iteration_protocol(self):
  988. app = flask.Flask(__name__)
  989. app.testing = True
  990. with app.app_context():
  991. flask.g.foo = 23
  992. flask.g.bar = 42
  993. self.assert_equal('foo' in flask.g, True)
  994. self.assert_equal('foos' in flask.g, False)
  995. self.assert_equal(sorted(flask.g), ['bar', 'foo'])
  996. class SubdomainTestCase(FlaskTestCase):
  997. def test_basic_support(self):
  998. app = flask.Flask(__name__)
  999. app.config['SERVER_NAME'] = 'localhost'
  1000. @app.route('/')
  1001. def normal_index():
  1002. return 'normal index'
  1003. @app.route('/', subdomain='test')
  1004. def test_index():
  1005. return 'test index'
  1006. c = app.test_client()
  1007. rv = c.get('/', 'http://localhost/')
  1008. self.assert_equal(rv.data, b'normal index')
  1009. rv = c.get('/', 'http://test.localhost/')
  1010. self.assert_equal(rv.data, b'test index')
  1011. @emits_module_deprecation_warning
  1012. def test_module_static_path_subdomain(self):
  1013. app = flask.Flask(__name__)
  1014. app.config['SERVER_NAME'] = 'example.com'
  1015. from subdomaintestmodule import mod
  1016. app.register_module(mod)
  1017. c = app.test_client()
  1018. rv = c.get('/static/hello.txt', 'http://foo.example.com/')
  1019. rv.direct_passthrough = False
  1020. self.assert_equal(rv.data.strip(), b'Hello Subdomain')
  1021. rv.close()
  1022. def test_subdomain_matching(self):
  1023. app = flask.Flask(__name__)
  1024. app.config['SERVER_NAME'] = 'localhost'
  1025. @app.route('/', subdomain='<user>')
  1026. def index(user):
  1027. return 'index for %s' % user
  1028. c = app.test_client()
  1029. rv = c.get('/', 'http://mitsuhiko.localhost/')
  1030. self.assert_equal(rv.data, b'index for mitsuhiko')
  1031. def test_subdomain_matching_with_ports(self):
  1032. app = flask.Flask(__name__)
  1033. app.config['SERVER_NAME'] = 'localhost:3000'
  1034. @app.route('/', subdomain='<user>')
  1035. def index(user):
  1036. return 'index for %s' % user
  1037. c = app.test_client()
  1038. rv = c.get('/', 'http://mitsuhiko.localhost:3000/')
  1039. self.assert_equal(rv.data, b'index for mitsuhiko')
  1040. @emits_module_deprecation_warning
  1041. def test_module_subdomain_support(self):
  1042. app = flask.Flask(__name__)
  1043. mod = flask.Module(__name__, 'test', subdomain='testing')
  1044. app.config['SERVER_NAME'] = 'localhost'
  1045. @mod.route('/test')
  1046. def test():
  1047. return 'Test'
  1048. @mod.route('/outside', subdomain='xtesting')
  1049. def bar():
  1050. return 'Outside'
  1051. app.register_module(mod)
  1052. c = app.test_client()
  1053. rv = c.get('/test', 'http://testing.localhost/')
  1054. self.assert_equal(rv.data, b'Test')
  1055. rv = c.get('/outside', 'http://xtesting.localhost/')
  1056. self.assert_equal(rv.data, b'Outside')
  1057. def test_multi_route_rules(self):
  1058. app = flask.Flask(__name__)
  1059. @app.route('/')
  1060. @app.route('/<test>/')
  1061. def index(test='a'):
  1062. return test
  1063. rv = app.test_client().open('/')
  1064. self.assert_equal(rv.data, b'a')
  1065. rv = app.test_client().open('/b/')
  1066. self.assert_equal(rv.data, b'b')
  1067. def test_multi_route_class_views(self):
  1068. class View(object):
  1069. def __init__(self, app):
  1070. app.add_url_rule('/', 'index', self.index)
  1071. app.add_url_rule('/<test>/', 'index', self.index)
  1072. def index(self, test='a'):
  1073. return test
  1074. app = flask.Flask(__name__)
  1075. _ = View(app)
  1076. rv = app.test_client().open('/')
  1077. self.assert_equal(rv.data, b'a')
  1078. rv = app.test_client().open('/b/')
  1079. self.assert_equal(rv.data, b'b')
  1080. def suite():
  1081. suite = unittest.TestSuite()
  1082. suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
  1083. suite.addTest(unittest.makeSuite(SubdomainTestCase))
  1084. return suite