refactor(tests): Migrate to pytest (#1057)

Convert tests to pytest
Remove ddt and testtols dependencies

Closes falconry/falcon#844
This commit is contained in:
Serge
2017-05-25 20:32:55 +03:00
committed by John Vrbanac
parent b7cc80b8ed
commit 04d73f5256
32 changed files with 2384 additions and 2212 deletions

View File

@@ -11,7 +11,7 @@ Reference
simulate_request, simulate_get, simulate_head, simulate_post, simulate_request, simulate_get, simulate_head, simulate_post,
simulate_put, simulate_options, simulate_patch, simulate_delete, simulate_put, simulate_options, simulate_patch, simulate_delete,
TestClient, TestCase, SimpleTestResource, StartResponseMock, TestClient, TestCase, SimpleTestResource, StartResponseMock,
capture_responder_args, rand_string, create_environ capture_responder_args, rand_string, create_environ, redirected
Deprecated Deprecated
---------- ----------

View File

@@ -246,7 +246,7 @@ class Cookie(object):
def simulate_request(app, method='GET', path='/', query_string=None, def simulate_request(app, method='GET', path='/', query_string=None,
headers=None, body=None, file_wrapper=None, headers=None, body=None, file_wrapper=None, wsgierrors=None,
params=None, params_csv=True, protocol='http'): params=None, params_csv=True, protocol='http'):
"""Simulates a request to a WSGI application. """Simulates a request to a WSGI application.
@@ -286,6 +286,8 @@ def simulate_request(app, method='GET', path='/', query_string=None,
environ (default: ``None``). This can be used to test environ (default: ``None``). This can be used to test
high-performance file transmission when `resp.stream` is high-performance file transmission when `resp.stream` is
set to a file-like object. set to a file-like object.
wsgierrors (io): The stream to use as *wsgierrors*
(default ``sys.stderr``)
Returns: Returns:
:py:class:`~.Result`: The result of the request :py:class:`~.Result`: The result of the request
@@ -323,6 +325,7 @@ def simulate_request(app, method='GET', path='/', query_string=None,
headers=headers, headers=headers,
body=body, body=body,
file_wrapper=file_wrapper, file_wrapper=file_wrapper,
wsgierrors=wsgierrors,
) )
srmock = StartResponseMock() srmock = StartResponseMock()

View File

@@ -24,6 +24,7 @@ directly from the `testing` package::
""" """
import cgi import cgi
import contextlib
import io import io
import random import random
import sys import sys
@@ -206,6 +207,24 @@ def create_environ(path='/', query_string='', protocol='HTTP/1.1',
return env return env
@contextlib.contextmanager
def redirected(stdout=sys.stdout, stderr=sys.stderr):
"""
A context manager to temporarily redirect stdout or stderr
e.g.:
with redirected(stderr=os.devnull):
...
"""
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = stdout, stderr
try:
yield
finally:
sys.stderr, sys.stdout = old_stderr, old_stdout
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# Private # Private
# --------------------------------------------------------------------- # ---------------------------------------------------------------------

View File

@@ -1,5 +1,4 @@
coverage>=4.1 coverage>=4.1
ddt
pytest>=3.0.1 pytest>=3.0.1
pytest-cov pytest-cov
pytest-xdist pytest-xdist

View File

@@ -107,8 +107,7 @@ setup(
install_requires=REQUIRES, install_requires=REQUIRES,
cmdclass=cmdclass, cmdclass=cmdclass,
ext_modules=ext_modules, ext_modules=ext_modules,
tests_require=['ddt', 'testtools', 'requests', 'pyyaml', 'pytest', tests_require=['testtools', 'requests', 'pyyaml', 'pytest', 'pytest-runner'],
'pytest-runner'],
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'falcon-bench = falcon.cmd.bench:main', 'falcon-bench = falcon.cmd.bench:main',

View File

@@ -1,4 +1,7 @@
import functools import functools
import pytest
try: try:
import ujson as json import ujson as json
except ImportError: except ImportError:
@@ -8,6 +11,31 @@ import falcon
from falcon import testing from falcon import testing
# --------------------------------------------------------------------
# Fixtures
# --------------------------------------------------------------------
@pytest.fixture
def wrapped_resource_aware():
return ClassResourceWithAwareHooks()
@pytest.fixture
def wrapped_resource():
return WrappedClassResource()
@pytest.fixture
def client():
app = falcon.API()
resource = WrappedRespondersResource()
app.add_route('/', resource)
return testing.TestClient(app)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Hooks # Hooks
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@@ -165,70 +193,64 @@ class ClassResourceWithAwareHooks(object):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
class TestHooks(testing.TestCase): class TestHooks(object):
def setUp(self): def test_output_validator(self, client):
super(TestHooks, self).setUp() result = client.simulate_get()
assert result.status_code == 723
assert result.text == json.dumps({'title': 'Tricky'})
self.resource = WrappedRespondersResource() def test_serializer(self, client):
self.api.add_route('/', self.resource) result = client.simulate_put()
assert result.text == json.dumps({'animal': 'falcon'})
self.wrapped_resource = WrappedClassResource() def test_hook_as_callable_class(self, client):
self.api.add_route('/wrapped', self.wrapped_resource) result = client.simulate_post()
assert 'smart' == result.text
self.wrapped_resource_aware = ClassResourceWithAwareHooks() def test_wrapped_resource(self, client, wrapped_resource):
self.api.add_route('/wrapped_aware', self.wrapped_resource_aware) client.app.add_route('/wrapped', wrapped_resource)
result = client.simulate_get('/wrapped')
assert result.status_code == 200
assert result.text == 'fluffy and cute'
def test_output_validator(self): result = client.simulate_head('/wrapped')
result = self.simulate_get() assert result.status_code == 200
self.assertEqual(result.status_code, 723) assert result.headers['X-Fluffiness'] == 'fluffy'
self.assertEqual(result.text, json.dumps({'title': 'Tricky'})) assert result.headers['X-Cuteness'] == 'cute'
def test_serializer(self): result = client.simulate_post('/wrapped')
result = self.simulate_put() assert result.status_code == 405
self.assertEqual(result.text, json.dumps({'animal': 'falcon'}))
def test_hook_as_callable_class(self): result = client.simulate_patch('/wrapped')
result = self.simulate_post() assert result.status_code == 405
self.assertEqual('smart', result.text)
def test_wrapped_resource(self):
result = self.simulate_get('/wrapped')
self.assertEqual(result.status_code, 200)
self.assertEqual(result.text, 'fluffy and cute', )
result = self.simulate_head('/wrapped')
self.assertEqual(result.status_code, 200)
self.assertEqual(result.headers['X-Fluffiness'], 'fluffy')
self.assertEqual(result.headers['X-Cuteness'], 'cute')
result = self.simulate_post('/wrapped')
self.assertEqual(result.status_code, 405)
result = self.simulate_patch('/wrapped')
self.assertEqual(result.status_code, 405)
# Decorator should not affect the default on_options responder # Decorator should not affect the default on_options responder
result = self.simulate_options('/wrapped') result = client.simulate_options('/wrapped')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertFalse(result.text) assert not result.text
def test_wrapped_resource_with_hooks_aware_of_resource(self): def test_wrapped_resource_with_hooks_aware_of_resource(self, client, wrapped_resource_aware):
client.app.add_route('/wrapped_aware', wrapped_resource_aware)
expected = 'fluffy and cute' expected = 'fluffy and cute'
result = self.simulate_get('/wrapped_aware') result = client.simulate_get('/wrapped_aware')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertEqual(expected, result.text) assert expected == result.text
for test in (self.simulate_head, self.simulate_put, self.simulate_post): for test in (
result = test('/wrapped_aware') client.simulate_head,
self.assertEqual(result.status_code, 200) client.simulate_put,
self.assertEqual(self.wrapped_resource_aware.resp.body, expected) client.simulate_post,
):
result = test(path='/wrapped_aware')
assert result.status_code == 200
assert wrapped_resource_aware.resp.body == expected
result = self.simulate_patch('/wrapped_aware') result = client.simulate_patch('/wrapped_aware')
self.assertEqual(result.status_code, 405) assert result.status_code == 405
# Decorator should not affect the default on_options responder # Decorator should not affect the default on_options responder
result = self.simulate_options('/wrapped_aware') result = client.simulate_options('/wrapped_aware')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertFalse(result.text) assert not result.text

View File

@@ -1,5 +1,8 @@
import functools import functools
import io import io
import pytest
try: try:
import ujson as json import ujson as json
except ImportError: except ImportError:
@@ -176,92 +179,107 @@ class ZooResource(object):
self.fish = fish self.fish = fish
class TestHooks(testing.TestCase): @pytest.fixture
def wrapped_aware_resource():
return ClassResourceWithAwareHooks()
def setUp(self):
super(TestHooks, self).setUp()
self.resource = WrappedRespondersResource() @pytest.fixture
self.api.add_route('/', self.resource) def wrapped_resource():
return WrappedClassResource()
self.field_resource = TestFieldResource()
self.api.add_route('/queue/{id}/messages', self.field_resource)
self.wrapped_resource = WrappedClassResource() @pytest.fixture
self.api.add_route('/wrapped', self.wrapped_resource) def field_resource():
return TestFieldResource()
self.wrapped_aware_resource = ClassResourceWithAwareHooks()
self.api.add_route('/wrapped_aware', self.wrapped_aware_resource)
def test_multiple_resource_hooks(self): @pytest.fixture
def resource():
return WrappedRespondersResource()
@pytest.fixture
def client(resource):
app = falcon.API()
app.add_route('/', resource)
return testing.TestClient(app)
class TestHooks(object):
def test_multiple_resource_hooks(self, client):
zoo_resource = ZooResource() zoo_resource = ZooResource()
self.api.add_route('/', zoo_resource) client.app.add_route('/', zoo_resource)
result = self.simulate_get('/') result = client.simulate_get('/')
self.assertEqual('not fluffy', result.headers['X-Frogs']) assert 'not fluffy' == result.headers['X-Frogs']
self.assertEqual('fluffy', result.headers['X-Bunnies']) assert 'fluffy' == result.headers['X-Bunnies']
self.assertEqual('fluffy', zoo_resource.bunnies) assert 'fluffy' == zoo_resource.bunnies
self.assertEqual('not fluffy', zoo_resource.frogs) assert 'not fluffy' == zoo_resource.frogs
self.assertEqual('slippery', zoo_resource.fish) assert 'slippery' == zoo_resource.fish
def test_input_validator(self): def test_input_validator(self, client):
result = self.simulate_put('/') result = client.simulate_put('/')
self.assertEqual(result.status_code, 400) assert result.status_code == 400
def test_param_validator(self): def test_param_validator(self, client):
result = self.simulate_get('/', query_string='limit=10', body='{}') result = client.simulate_get('/', query_string='limit=10', body='{}')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
result = self.simulate_get('/', query_string='limit=101') result = client.simulate_get('/', query_string='limit=101')
self.assertEqual(result.status_code, 400) assert result.status_code == 400
def test_field_validator(self): def test_field_validator(self, client, field_resource):
result = self.simulate_get('/queue/10/messages') client.app.add_route('/queue/{id}/messages', field_resource)
self.assertEqual(result.status_code, 200) result = client.simulate_get('/queue/10/messages')
self.assertEqual(self.field_resource.id, 10) assert result.status_code == 200
assert field_resource.id == 10
result = self.simulate_get('/queue/bogus/messages') result = client.simulate_get('/queue/bogus/messages')
self.assertEqual(result.status_code, 400) assert result.status_code == 400
def test_parser(self): def test_parser(self, client, resource):
self.simulate_get('/', body=json.dumps({'animal': 'falcon'})) client.simulate_get('/', body=json.dumps({'animal': 'falcon'}))
self.assertEqual(self.resource.doc, {'animal': 'falcon'}) assert resource.doc == {'animal': 'falcon'}
def test_wrapped_resource(self): def test_wrapped_resource(self, client, wrapped_resource):
result = self.simulate_patch('/wrapped') client.app.add_route('/wrapped', wrapped_resource)
self.assertEqual(result.status_code, 405) result = client.simulate_patch('/wrapped')
assert result.status_code == 405
result = self.simulate_get('/wrapped', query_string='limit=10') result = client.simulate_get('/wrapped', query_string='limit=10')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertEqual('fuzzy', self.wrapped_resource.bunnies) assert 'fuzzy' == wrapped_resource.bunnies
result = self.simulate_head('/wrapped') result = client.simulate_head('/wrapped')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertEqual('fuzzy', self.wrapped_resource.bunnies) assert 'fuzzy' == wrapped_resource.bunnies
result = self.simulate_post('/wrapped') result = client.simulate_post('/wrapped')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertEqual('slippery', self.wrapped_resource.fish) assert 'slippery' == wrapped_resource.fish
result = self.simulate_get('/wrapped', query_string='limit=101') result = client.simulate_get('/wrapped', query_string='limit=101')
self.assertEqual(result.status_code, 400) assert result.status_code == 400
self.assertEqual(self.wrapped_resource.bunnies, 'fuzzy') assert wrapped_resource.bunnies == 'fuzzy'
def test_wrapped_resource_with_hooks_aware_of_resource(self): def test_wrapped_resource_with_hooks_aware_of_resource(self, client, wrapped_aware_resource):
result = self.simulate_patch('/wrapped_aware') client.app.add_route('/wrapped_aware', wrapped_aware_resource)
self.assertEqual(result.status_code, 405)
result = self.simulate_get('/wrapped_aware', query_string='limit=10') result = client.simulate_patch('/wrapped_aware')
self.assertEqual(result.status_code, 200) assert result.status_code == 405
self.assertEqual(self.wrapped_aware_resource.bunnies, 'fuzzy')
result = client.simulate_get('/wrapped_aware', query_string='limit=10')
assert result.status_code == 200
assert wrapped_aware_resource.bunnies == 'fuzzy'
for method in ('HEAD', 'PUT', 'POST'): for method in ('HEAD', 'PUT', 'POST'):
result = self.simulate_request(method, '/wrapped_aware') result = client.simulate_request(method, '/wrapped_aware')
self.assertEqual(result.status_code, 200) assert result.status_code == 200
self.assertEqual(self.wrapped_aware_resource.bunnies, 'fuzzy') assert wrapped_aware_resource.bunnies == 'fuzzy'
result = self.simulate_get('/wrapped_aware', query_string='limit=101') result = client.simulate_get('/wrapped_aware', query_string='limit=101')
self.assertEqual(result.status_code, 400) assert result.status_code == 400
self.assertEqual(self.wrapped_aware_resource.bunnies, 'fuzzy') assert wrapped_aware_resource.bunnies == 'fuzzy'

View File

@@ -1,7 +1,3 @@
import sys
import testtools
try: try:
from StringIO import StringIO from StringIO import StringIO
except ImportError: except ImportError:
@@ -9,45 +5,30 @@ except ImportError:
from falcon import API from falcon import API
from falcon.cmd import print_routes from falcon.cmd import print_routes
from falcon.testing import redirected
_api = API() _api = API()
_api.add_route('/test', None) _api.add_route('/test', None)
STDOUT = sys.stdout
class TestPrintRoutes(testtools.TestCase):
def setUp(self):
"""Capture stdout"""
super(TestPrintRoutes, self).setUp()
self.output = StringIO()
sys.stdout = self.output
def tearDown(self):
"""Reset stdout"""
super(TestPrintRoutes, self).tearDown()
self.output.close()
del self.output
sys.stdout = STDOUT
class TestPrintRoutes(object):
def test_traverse_with_verbose(self): def test_traverse_with_verbose(self):
"""Ensure traverse finds the proper routes and adds verbose output.""" """Ensure traverse finds the proper routes and adds verbose output."""
print_routes.traverse( output = StringIO()
_api._router._roots, with redirected(stdout=output):
verbose=True) print_routes.traverse(_api._router._roots, verbose=True)
route, options = self.output.getvalue().strip().split('\n') route, options = output.getvalue().strip().split('\n')
self.assertEquals('-> /test', route) assert '-> /test' == route
self.assertTrue('OPTIONS' in options) assert 'OPTIONS' in options
self.assertTrue('falcon/responders.py:' in options) assert 'falcon/responders.py:' in options
def test_traverse(self): def test_traverse(self):
"""Ensure traverse finds the proper routes.""" """Ensure traverse finds the proper routes."""
print_routes.traverse( output = StringIO()
_api._router._roots, with redirected(stdout=output):
verbose=False) print_routes.traverse(_api._router._roots, verbose=False)
route = self.output.getvalue().strip() route = output.getvalue().strip()
self.assertEquals('-> /test', route) assert '-> /test' == route

View File

@@ -2,7 +2,7 @@ import falcon
from falcon import testing from falcon import testing
class TestCustomRouter(testing.TestBase): class TestCustomRouter(object):
def test_custom_router_add_route_should_be_used(self): def test_custom_router_add_route_should_be_used(self):
check = [] check = []
@@ -14,11 +14,11 @@ class TestCustomRouter(testing.TestBase):
def find(self, uri): def find(self, uri):
pass pass
api = falcon.API(router=CustomRouter()) app = falcon.API(router=CustomRouter())
api.add_route('/test', 'resource') app.add_route('/test', 'resource')
self.assertEqual(len(check), 1) assert len(check) == 1
self.assertIn('/test', check) assert '/test' in check
def test_custom_router_find_should_be_used(self): def test_custom_router_find_should_be_used(self):
@@ -46,22 +46,24 @@ class TestCustomRouter(testing.TestBase):
return None return None
router = CustomRouter() router = CustomRouter()
self.api = falcon.API(router=router) app = falcon.API(router=router)
body = self.simulate_request('/test/42') client = testing.TestClient(app)
self.assertEqual(body, [b'{"uri_template": "/test/{id}"}'])
body = self.simulate_request('/test/42/no-uri-template') response = client.simulate_request(path='/test/42')
self.assertEqual(body, [b'{"uri_template": "None"}']) assert response.content == b'{"uri_template": "/test/{id}"}'
body = self.simulate_request('/test/42/uri-template/backwards-compat') response = client.simulate_request(path='/test/42/no-uri-template')
self.assertEqual(body, [b'{"uri_template": "None"}']) assert response.content == b'{"uri_template": "None"}'
response = client.simulate_request(path='/test/42/uri-template/backwards-compat')
assert response.content == b'{"uri_template": "None"}'
for uri in ('/404', '/404/backwards-compat'): for uri in ('/404', '/404/backwards-compat'):
body = self.simulate_request(uri) response = client.simulate_request(path=uri)
self.assertFalse(body) assert not response.content
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_404
self.assertTrue(router.reached_backwards_compat) assert router.reached_backwards_compat
def test_can_pass_additional_params_to_add_route(self): def test_can_pass_additional_params_to_add_route(self):
@@ -75,17 +77,17 @@ class TestCustomRouter(testing.TestBase):
def find(self, uri): def find(self, uri):
pass pass
api = falcon.API(router=CustomRouter()) app = falcon.API(router=CustomRouter())
api.add_route('/test', 'resource', name='my-url-name') app.add_route('/test', 'resource', name='my-url-name')
self.assertEqual(len(check), 1) assert len(check) == 1
self.assertIn('my-url-name', check) assert 'my-url-name' in check
# Also as arg. # Also as arg.
api.add_route('/test', 'resource', 'my-url-name-arg') app.add_route('/test', 'resource', 'my-url-name-arg')
self.assertEqual(len(check), 2) assert len(check) == 2
self.assertIn('my-url-name-arg', check) assert 'my-url-name-arg' in check
def test_custom_router_takes_req_positional_argument(self): def test_custom_router_takes_req_positional_argument(self):
def responder(req, resp): def responder(req, resp):
@@ -97,9 +99,10 @@ class TestCustomRouter(testing.TestBase):
return responder, {'GET': responder}, {}, None return responder, {'GET': responder}, {}, None
router = CustomRouter() router = CustomRouter()
self.api = falcon.API(router=router) app = falcon.API(router=router)
body = self.simulate_request('/test') client = testing.TestClient(app)
self.assertEqual(body[0], b'OK') response = client.simulate_request(path='/test')
assert response.content == b'OK'
def test_custom_router_takes_req_keyword_argument(self): def test_custom_router_takes_req_keyword_argument(self):
def responder(req, resp): def responder(req, resp):
@@ -111,6 +114,7 @@ class TestCustomRouter(testing.TestBase):
return responder, {'GET': responder}, {}, None return responder, {'GET': responder}, {}, None
router = CustomRouter() router = CustomRouter()
self.api = falcon.API(router=router) app = falcon.API(router=router)
body = self.simulate_request('/test') client = testing.TestClient(app)
self.assertEqual(body[0], b'OK') response = client.simulate_request(path='/test')
assert response.content == b'OK'

View File

@@ -1,8 +1,7 @@
import mimeparse import mimeparse
import testtools
class TestDeps(testtools.TestCase): class TestDeps(object):
def test_mimeparse_correct_package(self): def test_mimeparse_correct_package(self):
"""Ensure we are dealing with python-mimeparse, not mimeparse.""" """Ensure we are dealing with python-mimeparse, not mimeparse."""
@@ -15,4 +14,4 @@ class TestDeps(testtools.TestCase):
# NOTE(kgriffs): python-mimeparse starts at version 1.5.2, # NOTE(kgriffs): python-mimeparse starts at version 1.5.2,
# whereas the mimeparse package is at version 0.1.4 at the time # whereas the mimeparse package is at version 0.1.4 at the time
# of this writing. # of this writing.
self.assertGreater(int(tokens[0]), 0, msg) assert int(tokens[0]) > 0, msg

View File

@@ -1,389 +1,379 @@
import testtools
import falcon import falcon
import falcon.status_codes as status import falcon.status_codes as status
class TestError(testtools.TestCase): class TestError(object):
def test_http_bad_request_no_title_and_desc(self): def test_http_bad_request_no_title_and_desc(self):
try: try:
raise falcon.HTTPBadRequest() raise falcon.HTTPBadRequest()
except falcon.HTTPBadRequest as e: except falcon.HTTPBadRequest as e:
self.assertEqual(status.HTTP_400, e.title, assert status.HTTP_400 == e.title, \
'The title should be ' + status.HTTP_400 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_400 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_bad_request_with_title_and_desc(self): def test_http_bad_request_with_title_and_desc(self):
try: try:
raise falcon.HTTPBadRequest(title='Test', description='Testdescription') raise falcon.HTTPBadRequest(title='Test', description='Testdescription')
except falcon.HTTPBadRequest as e: except falcon.HTTPBadRequest as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_unauthorized_no_title_and_desc_and_challenges(self): def test_http_unauthorized_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnauthorized() raise falcon.HTTPUnauthorized()
except falcon.HTTPUnauthorized as e: except falcon.HTTPUnauthorized as e:
self.assertEqual(status.HTTP_401, e.title, assert status.HTTP_401 == e.title, \
'The title should be ' + status.HTTP_401 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_401 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
self.assertNotIn('WWW-Authenticate', e.headers, assert 'WWW-Authenticate' not in e.headers, \
'Challenges should not be found in headers') 'Challenges should not be found in headers'
def test_http_unauthorized_with_title_and_desc_and_challenges(self): def test_http_unauthorized_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnauthorized(title='Test', description='Testdescription', raise falcon.HTTPUnauthorized(title='Test', description='Testdescription',
challenges=['Testch']) challenges=['Testch'])
except falcon.HTTPUnauthorized as e: except falcon.HTTPUnauthorized as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
self.assertEqual('Testch', e.headers['WWW-Authenticate'], 'Challenges should be None') assert 'Testch' == e.headers['WWW-Authenticate'], \
'Challenges should be None'
def test_http_forbidden_no_title_and_desc_and_challenges(self): def test_http_forbidden_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPForbidden() raise falcon.HTTPForbidden()
except falcon.HTTPForbidden as e: except falcon.HTTPForbidden as e:
self.assertEqual(status.HTTP_403, e.title, assert status.HTTP_403 == e.title, \
'The title should be ' + status.HTTP_403 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_403 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_forbidden_with_title_and_desc_and_challenges(self): def test_http_forbidden_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPForbidden(title='Test', description='Testdescription') raise falcon.HTTPForbidden(title='Test', description='Testdescription')
except falcon.HTTPForbidden as e: except falcon.HTTPForbidden as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_not_acceptable_no_title_and_desc_and_challenges(self): def test_http_not_acceptable_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPNotAcceptable() raise falcon.HTTPNotAcceptable()
except falcon.HTTPNotAcceptable as e: except falcon.HTTPNotAcceptable as e:
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_not_acceptable_with_title_and_desc_and_challenges(self): def test_http_not_acceptable_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPNotAcceptable(description='Testdescription') raise falcon.HTTPNotAcceptable(description='Testdescription')
except falcon.HTTPNotAcceptable as e: except falcon.HTTPNotAcceptable as e:
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_conflict_no_title_and_desc_and_challenges(self): def test_http_conflict_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPConflict() raise falcon.HTTPConflict()
except falcon.HTTPConflict as e: except falcon.HTTPConflict as e:
self.assertEqual(status.HTTP_409, e.title, assert status.HTTP_409 == e.title, \
'The title should be ' + status.HTTP_409 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_409 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_conflict_with_title_and_desc_and_challenges(self): def test_http_conflict_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPConflict(title='Test', description='Testdescription') raise falcon.HTTPConflict(title='Test', description='Testdescription')
except falcon.HTTPConflict as e: except falcon.HTTPConflict as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_length_required_no_title_and_desc_and_challenges(self): def test_http_length_required_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLengthRequired() raise falcon.HTTPLengthRequired()
except falcon.HTTPLengthRequired as e: except falcon.HTTPLengthRequired as e:
self.assertEqual(status.HTTP_411, e.title, assert status.HTTP_411 == e.title, \
'The title should be ' + status.HTTP_411 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_411 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_length_required_with_title_and_desc_and_challenges(self): def test_http_length_required_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLengthRequired(title='Test', description='Testdescription') raise falcon.HTTPLengthRequired(title='Test', description='Testdescription')
except falcon.HTTPLengthRequired as e: except falcon.HTTPLengthRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_precondition_failed_no_title_and_desc_and_challenges(self): def test_http_precondition_failed_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPPreconditionFailed() raise falcon.HTTPPreconditionFailed()
except falcon.HTTPPreconditionFailed as e: except falcon.HTTPPreconditionFailed as e:
self.assertEqual(status.HTTP_412, e.title, assert status.HTTP_412 == e.title, \
'The title should be ' + status.HTTP_412 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_412 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_precondition_failed_with_title_and_desc_and_challenges(self): def test_http_precondition_failed_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPPreconditionFailed(title='Test', description='Testdescription') raise falcon.HTTPPreconditionFailed(title='Test', description='Testdescription')
except falcon.HTTPPreconditionFailed as e: except falcon.HTTPPreconditionFailed as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_request_entity_too_large_no_title_and_desc_and_challenges(self): def test_http_request_entity_too_large_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPRequestEntityTooLarge() raise falcon.HTTPRequestEntityTooLarge()
except falcon.HTTPRequestEntityTooLarge as e: except falcon.HTTPRequestEntityTooLarge as e:
self.assertEqual(status.HTTP_413, e.title, assert status.HTTP_413 == e.title, \
'The title should be ' + status.HTTP_413 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_413 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers') assert 'Retry-After' not in e.headers, 'Retry-After should not be in the headers'
def test_http_request_entity_too_large_with_title_and_desc_and_challenges(self): def test_http_request_entity_too_large_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPRequestEntityTooLarge(title='Test', description='Testdescription', raise falcon.HTTPRequestEntityTooLarge(title='Test', description='Testdescription',
retry_after=123) retry_after=123)
except falcon.HTTPRequestEntityTooLarge as e: except falcon.HTTPRequestEntityTooLarge as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123') assert '123' == e.headers['Retry-After'], \
'Retry-After should be 123'
def test_http_uri_too_long_no_title_and_desc_and_challenges(self): def test_http_uri_too_long_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUriTooLong() raise falcon.HTTPUriTooLong()
except falcon.HTTPUriTooLong as e: except falcon.HTTPUriTooLong as e:
self.assertEqual(status.HTTP_414, e.title, assert status.HTTP_414 == e.title, \
'The title should be ' + status.HTTP_414 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_414 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_uri_too_long_with_title_and_desc_and_challenges(self): def test_http_uri_too_long_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUriTooLong(title='Test', description='Testdescription') raise falcon.HTTPUriTooLong(title='Test', description='Testdescription')
except falcon.HTTPUriTooLong as e: except falcon.HTTPUriTooLong as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_unsupported_media_type_no_title_and_desc_and_challenges(self): def test_http_unsupported_media_type_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnsupportedMediaType() raise falcon.HTTPUnsupportedMediaType()
except falcon.HTTPUnsupportedMediaType as e: except falcon.HTTPUnsupportedMediaType as e:
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_unsupported_media_type_with_title_and_desc_and_challenges(self): def test_http_unsupported_media_type_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnsupportedMediaType(description='Testdescription') raise falcon.HTTPUnsupportedMediaType(description='Testdescription')
except falcon.HTTPUnsupportedMediaType as e: except falcon.HTTPUnsupportedMediaType as e:
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_unprocessable_entity_no_title_and_desc_and_challenges(self): def test_http_unprocessable_entity_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnprocessableEntity() raise falcon.HTTPUnprocessableEntity()
except falcon.HTTPUnprocessableEntity as e: except falcon.HTTPUnprocessableEntity as e:
self.assertEqual(status.HTTP_422, e.title, assert status.HTTP_422 == e.title, \
'The title should be ' + status.HTTP_422 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_422 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_unprocessable_with_title_and_desc_and_challenges(self): def test_http_unprocessable_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnprocessableEntity(title='Test', description='Testdescription') raise falcon.HTTPUnprocessableEntity(title='Test', description='Testdescription')
except falcon.HTTPUnprocessableEntity as e: except falcon.HTTPUnprocessableEntity as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_locked_no_title_and_desc_and_challenges(self): def test_http_locked_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLocked() raise falcon.HTTPLocked()
except falcon.HTTPLocked as e: except falcon.HTTPLocked as e:
self.assertEqual(status.HTTP_423, e.title, assert status.HTTP_423 == e.title, \
'The title should be ' + status.HTTP_423 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_423 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_locked_with_title_and_desc_and_challenges(self): def test_http_locked_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLocked(title='Test', description='Testdescription') raise falcon.HTTPLocked(title='Test', description='Testdescription')
except falcon.HTTPLocked as e: except falcon.HTTPLocked as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_failed_dependency_no_title_and_desc_and_challenges(self): def test_http_failed_dependency_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPFailedDependency() raise falcon.HTTPFailedDependency()
except falcon.HTTPFailedDependency as e: except falcon.HTTPFailedDependency as e:
self.assertEqual(status.HTTP_424, e.title, assert status.HTTP_424 == e.title, \
'The title should be ' + status.HTTP_424 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_424 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_failed_dependency_with_title_and_desc_and_challenges(self): def test_http_failed_dependency_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPFailedDependency(title='Test', description='Testdescription') raise falcon.HTTPFailedDependency(title='Test', description='Testdescription')
except falcon.HTTPFailedDependency as e: except falcon.HTTPFailedDependency as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_precondition_required_no_title_and_desc_and_challenges(self): def test_http_precondition_required_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPPreconditionRequired() raise falcon.HTTPPreconditionRequired()
except falcon.HTTPPreconditionRequired as e: except falcon.HTTPPreconditionRequired as e:
self.assertEqual(status.HTTP_428, e.title, assert status.HTTP_428 == e.title, \
'The title should be ' + status.HTTP_428 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_428 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_precondition_required_with_title_and_desc_and_challenges(self): def test_http_precondition_required_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPPreconditionRequired(title='Test', description='Testdescription') raise falcon.HTTPPreconditionRequired(title='Test', description='Testdescription')
except falcon.HTTPPreconditionRequired as e: except falcon.HTTPPreconditionRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_too_many_requests_no_title_and_desc_and_challenges(self): def test_http_too_many_requests_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPTooManyRequests() raise falcon.HTTPTooManyRequests()
except falcon.HTTPTooManyRequests as e: except falcon.HTTPTooManyRequests as e:
self.assertEqual(status.HTTP_429, e.title, assert status.HTTP_429 == e.title, \
'The title should be ' + status.HTTP_429 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_429 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers') assert 'Retry-After' not in e.headers, 'Retry-After should not be in the headers'
def test_http_too_many_requests_with_title_and_desc_and_challenges(self): def test_http_too_many_requests_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPTooManyRequests(title='Test', description='Testdescription', raise falcon.HTTPTooManyRequests(title='Test', description='Testdescription',
retry_after=123) retry_after=123)
except falcon.HTTPTooManyRequests as e: except falcon.HTTPTooManyRequests as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123') assert '123' == e.headers['Retry-After'], 'Retry-After should be 123'
def test_http_request_header_fields_too_large_no_title_and_desc_and_challenges(self): def test_http_request_header_fields_too_large_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPRequestHeaderFieldsTooLarge() raise falcon.HTTPRequestHeaderFieldsTooLarge()
except falcon.HTTPRequestHeaderFieldsTooLarge as e: except falcon.HTTPRequestHeaderFieldsTooLarge as e:
self.assertEqual(status.HTTP_431, e.title, assert status.HTTP_431 == e.title, \
'The title should be ' + status.HTTP_431 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_431 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_request_header_fields_too_large_with_title_and_desc_and_challenges(self): def test_http_request_header_fields_too_large_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPRequestHeaderFieldsTooLarge(title='Test', raise falcon.HTTPRequestHeaderFieldsTooLarge(title='Test',
description='Testdescription') description='Testdescription')
except falcon.HTTPRequestHeaderFieldsTooLarge as e: except falcon.HTTPRequestHeaderFieldsTooLarge as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_unavailable_for_legal_reasons_no_title_and_desc_and_challenges(self): def test_http_unavailable_for_legal_reasons_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnavailableForLegalReasons() raise falcon.HTTPUnavailableForLegalReasons()
except falcon.HTTPUnavailableForLegalReasons as e: except falcon.HTTPUnavailableForLegalReasons as e:
self.assertEqual(status.HTTP_451, e.title, assert status.HTTP_451 == e.title, \
'The title should be ' + status.HTTP_451 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_451 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_unavailable_for_legal_reasons_with_title_and_desc_and_challenges(self): def test_http_unavailable_for_legal_reasons_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPUnavailableForLegalReasons(title='Test', raise falcon.HTTPUnavailableForLegalReasons(title='Test',
description='Testdescription') description='Testdescription')
except falcon.HTTPUnavailableForLegalReasons as e: except falcon.HTTPUnavailableForLegalReasons as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, \
'Description should be "Testdescription"') 'Description should be "Testdescription"'
def test_http_internal_server_error_entity_no_title_and_desc_and_challenges(self): def test_http_internal_server_error_entity_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPInternalServerError() raise falcon.HTTPInternalServerError()
except falcon.HTTPInternalServerError as e: except falcon.HTTPInternalServerError as e:
self.assertEqual(status.HTTP_500, e.title, assert status.HTTP_500 == e.title, \
'The title should be ' + status.HTTP_500 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_500 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_internal_server_error_with_title_and_desc_and_challenges(self): def test_http_internal_server_error_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPInternalServerError(title='Test', description='Testdescription') raise falcon.HTTPInternalServerError(title='Test', description='Testdescription')
except falcon.HTTPInternalServerError as e: except falcon.HTTPInternalServerError as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_bad_gateway_entity_no_title_and_desc_and_challenges(self): def test_http_bad_gateway_entity_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPBadGateway() raise falcon.HTTPBadGateway()
except falcon.HTTPBadGateway as e: except falcon.HTTPBadGateway as e:
self.assertEqual(status.HTTP_502, e.title, assert status.HTTP_502 == e.title, \
'The title should be ' + status.HTTP_502 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_502 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_bad_gateway_entity_with_title_and_desc_and_challenges(self): def test_http_bad_gateway_entity_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPBadGateway(title='Test', description='Testdescription') raise falcon.HTTPBadGateway(title='Test', description='Testdescription')
except falcon.HTTPBadGateway as e: except falcon.HTTPBadGateway as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_service_unavailable_no_title_and_desc_and_challenges(self): def test_http_service_unavailable_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPServiceUnavailable() raise falcon.HTTPServiceUnavailable()
except falcon.HTTPServiceUnavailable as e: except falcon.HTTPServiceUnavailable as e:
self.assertEqual(status.HTTP_503, e.title, assert status.HTTP_503 == e.title, \
'The title should be ' + status.HTTP_503 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_503 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers') assert 'Retry-After' not in e.headers, 'Retry-After should not be in the headers'
def test_http_service_unavailable_with_title_and_desc_and_challenges(self): def test_http_service_unavailable_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPServiceUnavailable(title='Test', description='Testdescription', raise falcon.HTTPServiceUnavailable(title='Test', description='Testdescription',
retry_after=123) retry_after=123)
except falcon.HTTPServiceUnavailable as e: except falcon.HTTPServiceUnavailable as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test', e.title == 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"') assert '123', e.headers['Retry-After'] == 'Retry-After should be 123'
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123')
def test_http_insufficient_storage_no_title_and_desc_and_challenges(self): def test_http_insufficient_storage_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPInsufficientStorage() raise falcon.HTTPInsufficientStorage()
except falcon.HTTPInsufficientStorage as e: except falcon.HTTPInsufficientStorage as e:
self.assertEqual(status.HTTP_507, e.title, assert status.HTTP_507 == e.title, \
'The title should be ' + status.HTTP_507 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_507 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_insufficient_storage_with_title_and_desc_and_challenges(self): def test_http_insufficient_storage_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPInsufficientStorage(title='Test', description='Testdescription') raise falcon.HTTPInsufficientStorage(title='Test', description='Testdescription')
except falcon.HTTPInsufficientStorage as e: except falcon.HTTPInsufficientStorage as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_loop_detected_no_title_and_desc_and_challenges(self): def test_http_loop_detected_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLoopDetected() raise falcon.HTTPLoopDetected()
except falcon.HTTPLoopDetected as e: except falcon.HTTPLoopDetected as e:
self.assertEqual(status.HTTP_508, e.title, assert status.HTTP_508 == e.title, \
'The title should be ' + status.HTTP_508 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_508 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_loop_detected_with_title_and_desc_and_challenges(self): def test_http_loop_detected_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPLoopDetected(title='Test', description='Testdescription') raise falcon.HTTPLoopDetected(title='Test', description='Testdescription')
except falcon.HTTPLoopDetected as e: except falcon.HTTPLoopDetected as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_network_authentication_required_no_title_and_desc_and_challenges(self): def test_http_network_authentication_required_no_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPNetworkAuthenticationRequired() raise falcon.HTTPNetworkAuthenticationRequired()
except falcon.HTTPNetworkAuthenticationRequired as e: except falcon.HTTPNetworkAuthenticationRequired as e:
self.assertEqual(status.HTTP_511, e.title, assert status.HTTP_511 == e.title, \
'The title should be ' + status.HTTP_511 + ', but it is: ' + e.title) 'The title should be ' + status.HTTP_511 + ', but it is: ' + e.title
self.assertEqual(None, e.description, 'The description should be None') assert e.description is None, 'The description should be None'
def test_http_network_authentication_required_with_title_and_desc_and_challenges(self): def test_http_network_authentication_required_with_title_and_desc_and_challenges(self):
try: try:
raise falcon.HTTPNetworkAuthenticationRequired(title='Test', raise falcon.HTTPNetworkAuthenticationRequired(title='Test',
description='Testdescription') description='Testdescription')
except falcon.HTTPNetworkAuthenticationRequired as e: except falcon.HTTPNetworkAuthenticationRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"') assert 'Test' == e.title, 'Title should be "Test"'
self.assertEqual('Testdescription', e.description, assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
'Description should be "Testdescription"')
def test_http_error_repr(self): def test_http_error_repr(self):
error = falcon.HTTPBadRequest() error = falcon.HTTPBadRequest()
_repr = '<%s: %s>' % (error.__class__.__name__, error.status) _repr = '<%s: %s>' % (error.__class__.__name__, error.status)
self.assertEqual(error.__repr__(), _repr) assert error.__repr__() == _repr

View File

@@ -1,3 +1,5 @@
import pytest
import falcon import falcon
from falcon import testing from falcon import testing
@@ -40,70 +42,75 @@ class ErroredClassResource(object):
raise CustomException('CustomException') raise CustomException('CustomException')
class TestErrorHandler(testing.TestCase): @pytest.fixture
def client():
app = falcon.API()
app.add_route('/', ErroredClassResource())
return testing.TestClient(app)
def setUp(self):
super(TestErrorHandler, self).setUp()
self.api.add_route('/', ErroredClassResource())
def test_caught_error(self): class TestErrorHandler(object):
self.api.add_error_handler(Exception, capture_error)
result = self.simulate_get() def test_caught_error(self, client):
self.assertEqual(result.text, 'error: Plain Exception') client.app.add_error_handler(Exception, capture_error)
result = self.simulate_head() result = client.simulate_get()
self.assertEqual(result.status_code, 723) assert result.text == 'error: Plain Exception'
self.assertFalse(result.content)
def test_uncaught_error(self): result = client.simulate_head()
self.api.add_error_handler(CustomException, capture_error) assert result.status_code == 723
self.assertRaises(Exception, self.simulate_get) assert not result.content
def test_uncaught_error_else(self): def test_uncaught_error(self, client):
self.assertRaises(Exception, self.simulate_get) client.app.add_error_handler(CustomException, capture_error)
with pytest.raises(Exception):
client.simulate_get()
def test_converted_error(self): def test_uncaught_error_else(self, client):
self.api.add_error_handler(CustomException) with pytest.raises(Exception):
client.simulate_get()
result = self.simulate_delete() def test_converted_error(self, client):
self.assertEqual(result.status_code, 792) client.app.add_error_handler(CustomException)
self.assertEqual(result.json[u'title'], u'Internet crashed!')
def test_handle_not_defined(self): result = client.simulate_delete()
self.assertRaises(AttributeError, assert result.status_code == 792
self.api.add_error_handler, CustomBaseException) assert result.json[u'title'] == u'Internet crashed!'
def test_subclass_error(self): def test_handle_not_defined(self, client):
self.api.add_error_handler(CustomBaseException, capture_error) with pytest.raises(AttributeError):
client.app.add_error_handler(CustomBaseException)
result = self.simulate_delete() def test_subclass_error(self, client):
self.assertEqual(result.status_code, 723) client.app.add_error_handler(CustomBaseException, capture_error)
self.assertEqual(result.text, 'error: CustomException')
def test_error_order_duplicate(self): result = client.simulate_delete()
self.api.add_error_handler(Exception, capture_error) assert result.status_code == 723
self.api.add_error_handler(Exception, handle_error_first) assert result.text == 'error: CustomException'
result = self.simulate_get() def test_error_order_duplicate(self, client):
self.assertEqual(result.text, 'first error handler') client.app.add_error_handler(Exception, capture_error)
client.app.add_error_handler(Exception, handle_error_first)
def test_error_order_subclass(self): result = client.simulate_get()
self.api.add_error_handler(Exception, capture_error) assert result.text == 'first error handler'
self.api.add_error_handler(CustomException, handle_error_first)
result = self.simulate_delete() def test_error_order_subclass(self, client):
self.assertEqual(result.status_code, 200) client.app.add_error_handler(Exception, capture_error)
self.assertEqual(result.text, 'first error handler') client.app.add_error_handler(CustomException, handle_error_first)
result = self.simulate_get() result = client.simulate_delete()
self.assertEqual(result.status_code, 723) assert result.status_code == 200
self.assertEqual(result.text, 'error: Plain Exception') assert result.text == 'first error handler'
def test_error_order_subclass_masked(self): result = client.simulate_get()
self.api.add_error_handler(CustomException, handle_error_first) assert result.status_code == 723
self.api.add_error_handler(Exception, capture_error) assert result.text == 'error: Plain Exception'
result = self.simulate_delete() def test_error_order_subclass_masked(self, client):
self.assertEqual(result.status_code, 723) client.app.add_error_handler(CustomException, handle_error_first)
self.assertEqual(result.text, 'error: CustomException') client.app.add_error_handler(Exception, capture_error)
result = client.simulate_delete()
assert result.status_code == 723
assert result.text == 'error: CustomException'

View File

@@ -1,13 +1,22 @@
from collections import defaultdict from collections import defaultdict
from datetime import datetime from datetime import datetime
import ddt import pytest
import six import six
import falcon import falcon
from falcon import testing from falcon import testing
SAMPLE_BODY = testing.rand_string(0, 128 * 1024)
@pytest.fixture
def client():
app = falcon.API()
return testing.TestClient(app)
class XmlResource(object): class XmlResource(object):
def __init__(self, content_type): def __init__(self, content_type):
self.content_type = content_type self.content_type = content_type
@@ -169,78 +178,78 @@ class AppendHeaderResource(object):
resp.append_header('X-Things', 'thing-1') resp.append_header('X-Things', 'thing-1')
@ddt.ddt class TestHeaders(object):
class TestHeaders(testing.TestCase):
def setUp(self): def test_content_length(self, client):
super(TestHeaders, self).setUp() resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
result = client.simulate_get()
self.sample_body = testing.rand_string(0, 128 * 1024) content_length = str(len(SAMPLE_BODY))
self.resource = testing.SimpleTestResource(body=self.sample_body) assert result.headers['Content-Length'] == content_length
self.api.add_route('/', self.resource)
def test_content_length(self): def test_default_value(self, client):
result = self.simulate_get() resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
client.simulate_get()
content_length = str(len(self.sample_body)) req = resource.captured_req
self.assertEqual(result.headers['Content-Length'], content_length)
def test_default_value(self):
self.simulate_get()
req = self.resource.captured_req
value = req.get_header('X-Not-Found') or '876' value = req.get_header('X-Not-Found') or '876'
self.assertEqual(value, '876') assert value == '876'
value = req.get_header('X-Not-Found', default='some-value') value = req.get_header('X-Not-Found', default='some-value')
self.assertEqual(value, 'some-value') assert value == 'some-value'
def test_required_header(self): def test_required_header(self, client):
self.simulate_get() resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
client.simulate_get()
try: try:
req = self.resource.captured_req req = resource.captured_req
req.get_header('X-Not-Found', required=True) req.get_header('X-Not-Found', required=True)
self.fail('falcon.HTTPMissingHeader not raised') pytest.fail('falcon.HTTPMissingHeader not raised')
except falcon.HTTPMissingHeader as ex: except falcon.HTTPMissingHeader as ex:
self.assertIsInstance(ex, falcon.HTTPBadRequest) assert isinstance(ex, falcon.HTTPBadRequest)
self.assertEqual(ex.title, 'Missing header value') assert ex.title == 'Missing header value'
expected_desc = 'The X-Not-Found header is required.' expected_desc = 'The X-Not-Found header is required.'
self.assertEqual(ex.description, expected_desc) assert ex.description == expected_desc
@ddt.data(falcon.HTTP_204, falcon.HTTP_304) @pytest.mark.parametrize('status', (falcon.HTTP_204, falcon.HTTP_304))
def test_no_content_length(self, status): def test_no_content_length(self, client, status):
self.api.add_route('/xxx', testing.SimpleTestResource(status=status)) client.app.add_route('/xxx', testing.SimpleTestResource(status=status))
result = self.simulate_get('/xxx') result = client.simulate_get('/xxx')
self.assertNotIn('Content-Length', result.headers) assert 'Content-Length' not in result.headers
self.assertFalse(result.content) assert not result.content
def test_content_header_missing(self): def test_content_header_missing(self, client):
environ = testing.create_environ() environ = testing.create_environ()
req = falcon.Request(environ) req = falcon.Request(environ)
for header in ('Content-Type', 'Content-Length'): for header in ('Content-Type', 'Content-Length'):
self.assertIs(req.get_header(header), None) assert req.get_header(header) is None
def test_passthrough_request_headers(self): def test_passthrough_request_headers(self, client):
resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
request_headers = { request_headers = {
'X-Auth-Token': 'Setec Astronomy', 'X-Auth-Token': 'Setec Astronomy',
'Content-Type': 'text/plain; charset=utf-8' 'Content-Type': 'text/plain; charset=utf-8'
} }
self.simulate_get(headers=request_headers) client.simulate_get(headers=request_headers)
for name, expected_value in request_headers.items(): for name, expected_value in request_headers.items():
actual_value = self.resource.captured_req.get_header(name) actual_value = resource.captured_req.get_header(name)
self.assertEqual(actual_value, expected_value) assert actual_value == expected_value
self.simulate_get(headers=self.resource.captured_req.headers) client.simulate_get(headers=resource.captured_req.headers)
# Compare the request HTTP headers with the original headers # Compare the request HTTP headers with the original headers
for name, expected_value in request_headers.items(): for name, expected_value in request_headers.items():
actual_value = self.resource.captured_req.get_header(name) actual_value = resource.captured_req.get_header(name)
self.assertEqual(actual_value, expected_value) assert actual_value == expected_value
def test_headers_as_list(self): def test_headers_as_list(self, client):
headers = [ headers = [
('Client-ID', '692ba466-74bb-11e3-bf3f-7567c531c7ca'), ('Client-ID', '692ba466-74bb-11e3-bf3f-7567c531c7ca'),
('Accept', 'audio/*; q=0.2, audio/basic') ('Accept', 'audio/*; q=0.2, audio/basic')
@@ -251,217 +260,213 @@ class TestHeaders(testing.TestCase):
req = falcon.Request(environ) req = falcon.Request(environ)
for name, value in headers: for name, value in headers:
self.assertIn((name.upper(), value), req.headers.items()) assert (name.upper(), value) in req.headers.items()
# Functional test # Functional test
self.api.add_route('/', testing.SimpleTestResource(headers=headers)) client.app.add_route('/', testing.SimpleTestResource(headers=headers))
result = self.simulate_get() result = client.simulate_get()
for name, value in headers: for name, value in headers:
self.assertEqual(result.headers[name], value) assert result.headers[name] == value
def test_default_media_type(self): def test_default_media_type(self, client):
resource = testing.SimpleTestResource(body='Hello world!') resource = testing.SimpleTestResource(body='Hello world!')
self._check_header(resource, 'Content-Type', falcon.DEFAULT_MEDIA_TYPE) self._check_header(client, resource, 'Content-Type', falcon.DEFAULT_MEDIA_TYPE)
@ddt.data( @pytest.mark.parametrize('content_type,body', [
('text/plain; charset=UTF-8', u'Hello Unicode! \U0001F638'), ('text/plain; charset=UTF-8', u'Hello Unicode! \U0001F638'),
# NOTE(kgriffs): This only works because the client defaults to # NOTE(kgriffs): This only works because the client defaults to
# ISO-8859-1 IFF the media type is 'text'. # ISO-8859-1 IFF the media type is 'text'.
('text/plain', 'Hello ISO-8859-1!'), ('text/plain', 'Hello ISO-8859-1!'),
) ])
@ddt.unpack def test_override_default_media_type(self, client, content_type, body):
def test_override_default_media_type(self, content_type, body): client.app = falcon.API(media_type=content_type)
self.api = falcon.API(media_type=content_type) client.app.add_route('/', testing.SimpleTestResource(body=body))
self.api.add_route('/', testing.SimpleTestResource(body=body)) result = client.simulate_get()
result = self.simulate_get()
self.assertEqual(result.text, body) assert result.text == body
self.assertEqual(result.headers['Content-Type'], content_type) assert result.headers['Content-Type'] == content_type
def test_override_default_media_type_missing_encoding(self): def test_override_default_media_type_missing_encoding(self, client):
body = u'{"msg": "Hello Unicode! \U0001F638"}' body = u'{"msg": "Hello Unicode! \U0001F638"}'
self.api = falcon.API(media_type='application/json') client.app = falcon.API(media_type='application/json')
self.api.add_route('/', testing.SimpleTestResource(body=body)) client.app.add_route('/', testing.SimpleTestResource(body=body))
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.content, body.encode('utf-8')) assert result.content == body.encode('utf-8')
self.assertIsInstance(result.text, six.text_type) assert isinstance(result.text, six.text_type)
self.assertEqual(result.text, body) assert result.text == body
self.assertEqual(result.json, {u'msg': u'Hello Unicode! \U0001F638'}) assert result.json == {u'msg': u'Hello Unicode! \U0001F638'}
def test_response_header_helpers_on_get(self): def test_response_header_helpers_on_get(self, client):
last_modified = datetime(2013, 1, 1, 10, 30, 30) last_modified = datetime(2013, 1, 1, 10, 30, 30)
resource = HeaderHelpersResource(last_modified) resource = HeaderHelpersResource(last_modified)
self.api.add_route('/', resource) client.app.add_route('/', resource)
result = self.simulate_get() result = client.simulate_get()
resp = resource.resp resp = resource.resp
content_type = 'x-falcon/peregrine' content_type = 'x-falcon/peregrine'
self.assertEqual(resp.content_type, content_type) assert resp.content_type == content_type
self.assertEqual(result.headers['Content-Type'], content_type) assert result.headers['Content-Type'] == content_type
cache_control = ('public, private, no-cache, no-store, ' cache_control = ('public, private, no-cache, no-store, '
'must-revalidate, proxy-revalidate, max-age=3600, ' 'must-revalidate, proxy-revalidate, max-age=3600, '
's-maxage=60, no-transform') 's-maxage=60, no-transform')
self.assertEqual(resp.cache_control, cache_control) assert resp.cache_control == cache_control
self.assertEqual(result.headers['Cache-Control'], cache_control) assert result.headers['Cache-Control'] == cache_control
etag = 'fa0d1a60ef6616bb28038515c8ea4cb2' etag = 'fa0d1a60ef6616bb28038515c8ea4cb2'
self.assertEqual(resp.etag, etag) assert resp.etag == etag
self.assertEqual(result.headers['Etag'], etag) assert result.headers['Etag'] == etag
lm_date = 'Tue, 01 Jan 2013 10:30:30 GMT' lm_date = 'Tue, 01 Jan 2013 10:30:30 GMT'
self.assertEqual(resp.last_modified, lm_date) assert resp.last_modified == lm_date
self.assertEqual(result.headers['Last-Modified'], lm_date) assert result.headers['Last-Modified'] == lm_date
self.assertEqual(resp.retry_after, '3601') assert resp.retry_after == '3601'
self.assertEqual(result.headers['Retry-After'], '3601') assert result.headers['Retry-After'] == '3601'
self.assertEqual(resp.location, '/things/87') assert resp.location == '/things/87'
self.assertEqual(result.headers['Location'], '/things/87') assert result.headers['Location'] == '/things/87'
self.assertEqual(resp.content_location, '/things/78') assert resp.content_location == '/things/78'
self.assertEqual(result.headers['Content-Location'], '/things/78') assert result.headers['Content-Location'] == '/things/78'
content_range = 'bytes 0-499/10240' content_range = 'bytes 0-499/10240'
self.assertEqual(resp.content_range, content_range) assert resp.content_range == content_range
self.assertEqual(result.headers['Content-Range'], content_range) assert result.headers['Content-Range'] == content_range
resp.content_range = (1, 499, 10 * 1024, u'bytes') resp.content_range = (1, 499, 10 * 1024, u'bytes')
self.assertIsInstance(resp.content_range, str) assert isinstance(resp.content_range, str)
self.assertEqual(resp.content_range, 'bytes 1-499/10240') assert resp.content_range == 'bytes 1-499/10240'
self.assertEqual(resp.accept_ranges, 'bytes') assert resp.accept_ranges == 'bytes'
self.assertEqual(result.headers['Accept-Ranges'], 'bytes') assert result.headers['Accept-Ranges'] == 'bytes'
req_headers = {'Range': 'items=0-25'} req_headers = {'Range': 'items=0-25'}
result = self.simulate_get(headers=req_headers) result = client.simulate_get(headers=req_headers)
self.assertEqual(result.headers['Content-Range'], 'items 0-25/100') assert result.headers['Content-Range'] == 'items 0-25/100'
# Check for duplicate headers # Check for duplicate headers
hist = defaultdict(lambda: 0) hist = defaultdict(lambda: 0)
for name, value in result.headers.items(): for name, value in result.headers.items():
hist[name] += 1 hist[name] += 1
self.assertEqual(1, hist[name]) assert 1 == hist[name]
def test_unicode_location_headers(self): def test_unicode_location_headers(self, client):
self.api.add_route('/', LocationHeaderUnicodeResource()) client.app.add_route('/', LocationHeaderUnicodeResource())
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.headers['Location'], '/%C3%A7runchy/bacon') assert result.headers['Location'] == '/%C3%A7runchy/bacon'
self.assertEqual(result.headers['Content-Location'], 'ab%C3%A7') assert result.headers['Content-Location'] == 'ab%C3%A7'
# Test with the values swapped # Test with the values swapped
result = self.simulate_head() result = client.simulate_head()
self.assertEqual(result.headers['Content-Location'], assert result.headers['Content-Location'] == '/%C3%A7runchy/bacon'
'/%C3%A7runchy/bacon') assert result.headers['Location'] == 'ab%C3%A7'
self.assertEqual(result.headers['Location'], 'ab%C3%A7')
def test_unicode_headers_convertable(self): def test_unicode_headers_convertable(self, client):
self.api.add_route('/', UnicodeHeaderResource()) client.app.add_route('/', UnicodeHeaderResource())
result = self.simulate_get('/') result = client.simulate_get('/')
self.assertEqual(result.headers['Content-Type'], 'application/json') assert result.headers['Content-Type'] == 'application/json'
self.assertEqual(result.headers['X-Auth-Token'], 'toomanysecrets') assert result.headers['X-Auth-Token'] == 'toomanysecrets'
self.assertEqual(result.headers['X-Symbol'], '@') assert result.headers['X-Symbol'] == '@'
def test_unicode_headers_not_convertable(self): @pytest.mark.skipif(six.PY3, reason='Test only applies to Python 2')
if six.PY3: def test_unicode_headers_not_convertable(self, client):
self.skipTest('Test only applies to Python 2') client.app.add_route('/', UnicodeHeaderResource())
with pytest.raises(UnicodeEncodeError):
client.simulate_post('/')
self.api.add_route('/', UnicodeHeaderResource()) with pytest.raises(UnicodeEncodeError):
self.assertRaises(UnicodeEncodeError, self.simulate_post, '/') client.simulate_put('/')
self.assertRaises(UnicodeEncodeError, self.simulate_put, '/')
def test_response_set_and_get_header(self): def test_response_set_and_get_header(self, client):
resource = HeaderHelpersResource() resource = HeaderHelpersResource()
self.api.add_route('/', resource) client.app.add_route('/', resource)
for method in ('HEAD', 'POST', 'PUT'): for method in ('HEAD', 'POST', 'PUT'):
result = self.simulate_request(method=method) result = client.simulate_request(method=method)
content_type = 'x-falcon/peregrine' content_type = 'x-falcon/peregrine'
self.assertEqual(result.headers['Content-Type'], content_type) assert result.headers['Content-Type'] == content_type
self.assertEqual(resource.resp.get_header('content-TyPe'), assert resource.resp.get_header('content-TyPe') == content_type
content_type)
self.assertEqual(result.headers['Cache-Control'], 'no-store') assert result.headers['Cache-Control'] == 'no-store'
self.assertEqual(result.headers['X-Auth-Token'], 'toomanysecrets') assert result.headers['X-Auth-Token'] == 'toomanysecrets'
self.assertEqual(resource.resp.location, None) assert resource.resp.location is None
self.assertEqual(resource.resp.get_header('not-real'), None) assert resource.resp.get_header('not-real') is None
# Check for duplicate headers # Check for duplicate headers
hist = defaultdict(int) hist = defaultdict(int)
for name, value in result.headers.items(): for name, value in result.headers.items():
hist[name] += 1 hist[name] += 1
self.assertEqual(hist[name], 1) assert hist[name] == 1
# Ensure that deleted headers were not sent # Ensure that deleted headers were not sent
self.assertEqual(resource.resp.get_header('x-client-should-never-see-this'), None) assert resource.resp.get_header('x-client-should-never-see-this') is None
def test_response_append_header(self): def test_response_append_header(self, client):
self.api.add_route('/', AppendHeaderResource()) client.app.add_route('/', AppendHeaderResource())
for method in ('HEAD', 'GET'): for method in ('HEAD', 'GET'):
result = self.simulate_request(method=method) result = client.simulate_request(method=method)
value = result.headers['x-things'] value = result.headers['x-things']
self.assertEqual(value, 'thing-1,thing-2,thing-3') assert value == 'thing-1,thing-2,thing-3'
result = self.simulate_request(method='POST') result = client.simulate_request(method='POST')
self.assertEqual(result.headers['x-things'], 'thing-1') assert result.headers['x-things'] == 'thing-1'
def test_vary_star(self): def test_vary_star(self, client):
self.api.add_route('/', VaryHeaderResource(['*'])) client.app.add_route('/', VaryHeaderResource(['*']))
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.headers['vary'], '*') assert result.headers['vary'] == '*'
@ddt.data( @pytest.mark.parametrize('vary,expected_value', [
(['accept-encoding'], 'accept-encoding'), (['accept-encoding'], 'accept-encoding'),
([u'accept-encoding', 'x-auth-token'], 'accept-encoding, x-auth-token'), ([u'accept-encoding', 'x-auth-token'], 'accept-encoding, x-auth-token'),
(('accept-encoding', u'x-auth-token'), 'accept-encoding, x-auth-token'), (('accept-encoding', u'x-auth-token'), 'accept-encoding, x-auth-token'),
) ])
@ddt.unpack def test_vary_header(self, client, vary, expected_value):
def test_vary_header(self, vary, expected_value):
resource = VaryHeaderResource(vary) resource = VaryHeaderResource(vary)
self._check_header(resource, 'Vary', expected_value) self._check_header(client, resource, 'Vary', expected_value)
def test_content_type_no_body(self): def test_content_type_no_body(self, client):
self.api.add_route('/', testing.SimpleTestResource()) client.app.add_route('/', testing.SimpleTestResource())
result = self.simulate_get() result = client.simulate_get()
# NOTE(kgriffs): Even when there is no body, Content-Type # NOTE(kgriffs): Even when there is no body, Content-Type
# should still be included per wsgiref.validate # should still be included per wsgiref.validate
self.assertIn('Content-Type', result.headers) assert 'Content-Type' in result.headers
self.assertEqual(result.headers['Content-Length'], '0') assert result.headers['Content-Length'] == '0'
@ddt.data(falcon.HTTP_204, falcon.HTTP_304) @pytest.mark.parametrize('status', (falcon.HTTP_204, falcon.HTTP_304))
def test_no_content_type(self, status): def test_no_content_type(self, client, status):
self.api.add_route('/', testing.SimpleTestResource(status=status)) client.app.add_route('/', testing.SimpleTestResource(status=status))
result = self.simulate_get() result = client.simulate_get()
self.assertNotIn('Content-Type', result.headers) assert 'Content-Type' not in result.headers
def test_custom_content_type(self): def test_custom_content_type(self, client):
content_type = 'application/xml; charset=utf-8' content_type = 'application/xml; charset=utf-8'
resource = XmlResource(content_type) resource = XmlResource(content_type)
self._check_header(resource, 'Content-Type', content_type) self._check_header(client, resource, 'Content-Type', content_type)
def test_add_link_single(self): def test_add_link_single(self, client):
expected_value = '</things/2842>; rel=next' expected_value = '</things/2842>; rel=next'
resource = LinkHeaderResource() resource = LinkHeaderResource()
resource.add_link('/things/2842', 'next') resource.add_link('/things/2842', 'next')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_multiple(self): def test_add_link_multiple(self, client):
expected_value = ( expected_value = (
'</things/2842>; rel=next, ' + '</things/2842>; rel=next, ' +
'<http://%C3%A7runchy/bacon>; rel=contents, ' + '<http://%C3%A7runchy/bacon>; rel=contents, ' +
@@ -481,9 +486,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/alt-thing', resource.add_link('/alt-thing',
u'alternate http://example.com/\u00e7runchy') u'alternate http://example.com/\u00e7runchy')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_title(self): def test_add_link_with_title(self, client):
expected_value = ('</related/thing>; rel=item; ' expected_value = ('</related/thing>; rel=item; '
'title="A related thing"') 'title="A related thing"')
@@ -491,9 +496,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'item', resource.add_link('/related/thing', 'item',
title='A related thing') title='A related thing')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_title_star(self): def test_add_link_with_title_star(self, client):
expected_value = ('</related/thing>; rel=item; ' expected_value = ('</related/thing>; rel=item; '
"title*=UTF-8''A%20related%20thing, " "title*=UTF-8''A%20related%20thing, "
'</%C3%A7runchy/thing>; rel=item; ' '</%C3%A7runchy/thing>; rel=item; '
@@ -506,9 +511,9 @@ class TestHeaders(testing.TestCase):
resource.add_link(u'/\u00e7runchy/thing', 'item', resource.add_link(u'/\u00e7runchy/thing', 'item',
title_star=('en', u'A \u00e7runchy thing')) title_star=('en', u'A \u00e7runchy thing'))
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_anchor(self): def test_add_link_with_anchor(self, client):
expected_value = ('</related/thing>; rel=item; ' expected_value = ('</related/thing>; rel=item; '
'anchor="/some%20thing/or-other"') 'anchor="/some%20thing/or-other"')
@@ -516,18 +521,18 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'item', resource.add_link('/related/thing', 'item',
anchor='/some thing/or-other') anchor='/some thing/or-other')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_hreflang(self): def test_add_link_with_hreflang(self, client):
expected_value = ('</related/thing>; rel=about; ' expected_value = ('</related/thing>; rel=about; '
'hreflang=en') 'hreflang=en')
resource = LinkHeaderResource() resource = LinkHeaderResource()
resource.add_link('/related/thing', 'about', hreflang='en') resource.add_link('/related/thing', 'about', hreflang='en')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_hreflang_multi(self): def test_add_link_with_hreflang_multi(self, client):
expected_value = ('</related/thing>; rel=about; ' expected_value = ('</related/thing>; rel=about; '
'hreflang=en-GB; hreflang=de') 'hreflang=en-GB; hreflang=de')
@@ -535,9 +540,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'about', resource.add_link('/related/thing', 'about',
hreflang=('en-GB', 'de')) hreflang=('en-GB', 'de'))
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_with_type_hint(self): def test_add_link_with_type_hint(self, client):
expected_value = ('</related/thing>; rel=alternate; ' expected_value = ('</related/thing>; rel=alternate; '
'type="video/mp4; codecs=avc1.640028"') 'type="video/mp4; codecs=avc1.640028"')
@@ -545,9 +550,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'alternate', resource.add_link('/related/thing', 'alternate',
type_hint='video/mp4; codecs=avc1.640028') type_hint='video/mp4; codecs=avc1.640028')
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_add_link_complex(self): def test_add_link_complex(self, client):
expected_value = ('</related/thing>; rel=alternate; ' expected_value = ('</related/thing>; rel=alternate; '
'title="A related thing"; ' 'title="A related thing"; '
"title*=UTF-8'en'A%20%C3%A7runchy%20thing; " "title*=UTF-8'en'A%20%C3%A7runchy%20thing; "
@@ -561,23 +566,23 @@ class TestHeaders(testing.TestCase):
type_hint='application/json', type_hint='application/json',
title_star=('en', u'A \u00e7runchy thing')) title_star=('en', u'A \u00e7runchy thing'))
self._check_link_header(resource, expected_value) self._check_link_header(client, resource, expected_value)
def test_content_length_options(self): def test_content_length_options(self, client):
result = self.simulate_options() result = client.simulate_options()
content_length = '0' content_length = '0'
self.assertEqual(result.headers['Content-Length'], content_length) assert result.headers['Content-Length'] == content_length
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Helpers # Helpers
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def _check_link_header(self, resource, expected_value): def _check_link_header(self, client, resource, expected_value):
self._check_header(resource, 'Link', expected_value) self._check_header(client, resource, 'Link', expected_value)
def _check_header(self, resource, header, expected_value): def _check_header(self, client, resource, header, expected_value):
self.api.add_route('/', resource) client.app.add_route('/', resource)
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.headers[header], expected_value) assert result.headers[header] == expected_value

View File

@@ -1,12 +1,17 @@
import io import io
import ddt import pytest
import six import six
import falcon import falcon
import falcon.testing as testing import falcon.testing as testing
@pytest.fixture
def client():
return testing.TestClient(falcon.API())
# NOTE(kgriffs): Concept from Gunicorn's source (wsgi.py) # NOTE(kgriffs): Concept from Gunicorn's source (wsgi.py)
class FileWrapper(object): class FileWrapper(object):
@@ -108,135 +113,129 @@ class NoStatusResource(object):
pass pass
@ddt.ddt class TestHelloWorld(object):
class TestHelloWorld(testing.TestCase):
def setUp(self):
super(TestHelloWorld, self).setUp()
def test_env_headers_list_of_tuples(self): def test_env_headers_list_of_tuples(self):
env = testing.create_environ(headers=[('User-Agent', 'Falcon-Test')]) env = testing.create_environ(headers=[('User-Agent', 'Falcon-Test')])
self.assertEqual(env['HTTP_USER_AGENT'], 'Falcon-Test') assert env['HTTP_USER_AGENT'] == 'Falcon-Test'
def test_root_route(self): def test_root_route(self, client):
doc = {u'message': u'Hello world!'} doc = {u'message': u'Hello world!'}
resource = testing.SimpleTestResource(json=doc) resource = testing.SimpleTestResource(json=doc)
self.api.add_route('/', resource) client.app.add_route('/', resource)
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.json, doc) assert result.json == doc
def test_no_route(self): def test_no_route(self, client):
result = self.simulate_get('/seenoevil') result = client.simulate_get('/seenoevil')
self.assertEqual(result.status_code, 404) assert result.status_code == 404
@ddt.data( @pytest.mark.parametrize('path,resource,get_body', [
('/body', HelloResource('body'), lambda r: r.body.encode('utf-8')), ('/body', HelloResource('body'), lambda r: r.body.encode('utf-8')),
('/bytes', HelloResource('body, bytes'), lambda r: r.body), ('/bytes', HelloResource('body, bytes'), lambda r: r.body),
('/data', HelloResource('data'), lambda r: r.data), ('/data', HelloResource('data'), lambda r: r.data),
) ])
@ddt.unpack def test_body(self, client, path, resource, get_body):
def test_body(self, path, resource, get_body): client.app.add_route(path, resource)
self.api.add_route(path, resource)
result = self.simulate_get(path) result = client.simulate_get(path)
resp = resource.resp resp = resource.resp
content_length = int(result.headers['content-length']) content_length = int(result.headers['content-length'])
self.assertEqual(content_length, len(resource.sample_utf8)) assert content_length == len(resource.sample_utf8)
self.assertEqual(result.status, resource.sample_status) assert result.status == resource.sample_status
self.assertEqual(resp.status, resource.sample_status) assert resp.status == resource.sample_status
self.assertEqual(get_body(resp), resource.sample_utf8) assert get_body(resp) == resource.sample_utf8
self.assertEqual(result.content, resource.sample_utf8) assert result.content == resource.sample_utf8
def test_no_body_on_head(self): def test_no_body_on_head(self, client):
self.api.add_route('/body', HelloResource('body')) client.app.add_route('/body', HelloResource('body'))
result = self.simulate_head('/body') result = client.simulate_head('/body')
self.assertFalse(result.content) assert not result.content
self.assertEqual(result.status_code, 200) assert result.status_code == 200
def test_stream_chunked(self): def test_stream_chunked(self, client):
resource = HelloResource('stream') resource = HelloResource('stream')
self.api.add_route('/chunked-stream', resource) client.app.add_route('/chunked-stream', resource)
result = self.simulate_get('/chunked-stream') result = client.simulate_get('/chunked-stream')
self.assertEqual(result.content, resource.sample_utf8) assert result.content == resource.sample_utf8
self.assertNotIn('content-length', result.headers) assert 'content-length' not in result.headers
def test_stream_known_len(self): def test_stream_known_len(self, client):
resource = HelloResource('stream, stream_len') resource = HelloResource('stream, stream_len')
self.api.add_route('/stream', resource) client.app.add_route('/stream', resource)
result = self.simulate_get('/stream') result = client.simulate_get('/stream')
self.assertTrue(resource.called) assert resource.called
expected_len = resource.resp.stream_len expected_len = resource.resp.stream_len
actual_len = int(result.headers['content-length']) actual_len = int(result.headers['content-length'])
self.assertEqual(actual_len, expected_len) assert actual_len == expected_len
self.assertEqual(len(result.content), expected_len) assert len(result.content) == expected_len
self.assertEqual(result.content, resource.sample_utf8) assert result.content == resource.sample_utf8
def test_filelike(self): def test_filelike(self, client):
resource = HelloResource('stream, stream_len, filelike') resource = HelloResource('stream, stream_len, filelike')
self.api.add_route('/filelike', resource) client.app.add_route('/filelike', resource)
for file_wrapper in (None, FileWrapper): for file_wrapper in (None, FileWrapper):
result = self.simulate_get('/filelike', file_wrapper=file_wrapper) result = client.simulate_get('/filelike', file_wrapper=file_wrapper)
self.assertTrue(resource.called) assert resource.called
expected_len = resource.resp.stream_len expected_len = resource.resp.stream_len
actual_len = int(result.headers['content-length']) actual_len = int(result.headers['content-length'])
self.assertEqual(actual_len, expected_len) assert actual_len == expected_len
self.assertEqual(len(result.content), expected_len) assert len(result.content) == expected_len
for file_wrapper in (None, FileWrapper): for file_wrapper in (None, FileWrapper):
result = self.simulate_get('/filelike', file_wrapper=file_wrapper) result = client.simulate_get('/filelike', file_wrapper=file_wrapper)
self.assertTrue(resource.called) assert resource.called
expected_len = resource.resp.stream_len expected_len = resource.resp.stream_len
actual_len = int(result.headers['content-length']) actual_len = int(result.headers['content-length'])
self.assertEqual(actual_len, expected_len) assert actual_len == expected_len
self.assertEqual(len(result.content), expected_len) assert len(result.content) == expected_len
@ddt.data( @pytest.mark.parametrize('stream_factory,assert_closed', [
(ClosingBytesIO, True), # Implements close() (ClosingBytesIO, True), # Implements close()
(NonClosingBytesIO, False), # Has a non-callable "close" attr (NonClosingBytesIO, False), # Has a non-callable "close" attr
) ])
@ddt.unpack def test_filelike_closing(self, client, stream_factory, assert_closed):
def test_filelike_closing(self, stream_factory, assert_closed):
resource = ClosingFilelikeHelloResource(stream_factory) resource = ClosingFilelikeHelloResource(stream_factory)
self.api.add_route('/filelike-closing', resource) client.app.add_route('/filelike-closing', resource)
result = self.simulate_get('/filelike-closing', file_wrapper=None) result = client.simulate_get('/filelike-closing', file_wrapper=None)
self.assertTrue(resource.called) assert resource.called
expected_len = resource.resp.stream_len expected_len = resource.resp.stream_len
actual_len = int(result.headers['content-length']) actual_len = int(result.headers['content-length'])
self.assertEqual(actual_len, expected_len) assert actual_len == expected_len
self.assertEqual(len(result.content), expected_len) assert len(result.content) == expected_len
if assert_closed: if assert_closed:
self.assertTrue(resource.stream.close_called) assert resource.stream.close_called
def test_filelike_using_helper(self): def test_filelike_using_helper(self, client):
resource = HelloResource('stream, stream_len, filelike, use_helper') resource = HelloResource('stream, stream_len, filelike, use_helper')
self.api.add_route('/filelike-helper', resource) client.app.add_route('/filelike-helper', resource)
result = self.simulate_get('/filelike-helper') result = client.simulate_get('/filelike-helper')
self.assertTrue(resource.called) assert resource.called
expected_len = resource.resp.stream_len expected_len = resource.resp.stream_len
actual_len = int(result.headers['content-length']) actual_len = int(result.headers['content-length'])
self.assertEqual(actual_len, expected_len) assert actual_len == expected_len
self.assertEqual(len(result.content), expected_len) assert len(result.content) == expected_len
def test_status_not_set(self): def test_status_not_set(self, client):
self.api.add_route('/nostatus', NoStatusResource()) client.app.add_route('/nostatus', NoStatusResource())
result = self.simulate_get('/nostatus') result = client.simulate_get('/nostatus')
self.assertFalse(result.content) assert not result.content
self.assertEqual(result.status_code, 200) assert result.status_code == 200

View File

@@ -1,6 +1,6 @@
from functools import wraps from functools import wraps
from testtools.matchers import Contains import pytest
import falcon import falcon
import falcon.testing as testing import falcon.testing as testing
@@ -18,6 +18,44 @@ HTTP_METHODS = (
) )
@pytest.fixture
def stonewall():
return Stonewall()
@pytest.fixture
def resource_things():
return ThingsResource()
@pytest.fixture
def resource_misc():
return MiscResource()
@pytest.fixture
def resource_get_with_faulty_put():
return GetWithFaultyPutResource()
@pytest.fixture
def client():
app = falcon.API()
app.add_route('/stonewall', Stonewall())
resource_things = ThingsResource()
app.add_route('/things', resource_things)
app.add_route('/things/{id}/stuff/{sid}', resource_things)
resource_misc = MiscResource()
app.add_route('/misc', resource_misc)
resource_get_with_faulty_put = GetWithFaultyPutResource()
app.add_route('/get_with_param/{param}', resource_get_with_faulty_put)
return testing.TestClient(app)
class ThingsResource(object): class ThingsResource(object):
def __init__(self): def __init__(self):
self.called = False self.called = False
@@ -117,101 +155,96 @@ class FaultyDecoratedResource(object):
pass pass
class TestHttpMethodRouting(testing.TestBase): class TestHttpMethodRouting(object):
def before(self): def test_get(self, client, resource_things):
self.api.add_route('/stonewall', Stonewall()) client.app.add_route('/things', resource_things)
client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
response = client.simulate_request(path='/things/42/stuff/57')
assert response.status == falcon.HTTP_204
assert resource_things.called
self.resource_things = ThingsResource() def test_put(self, client, resource_things):
self.api.add_route('/things', self.resource_things) client.app.add_route('/things', resource_things)
self.api.add_route('/things/{id}/stuff/{sid}', self.resource_things) client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
response = client.simulate_request(path='/things/42/stuff/1337', method='PUT')
assert response.status == falcon.HTTP_201
assert resource_things.called
self.resource_misc = MiscResource() def test_post_not_allowed(self, client, resource_things):
self.api.add_route('/misc', self.resource_misc) client.app.add_route('/things', resource_things)
client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
response = client.simulate_request(path='/things/42/stuff/1337', method='POST')
assert response.status == falcon.HTTP_405
assert not resource_things.called
self.resource_get_with_faulty_put = GetWithFaultyPutResource() def test_misc(self, client, resource_misc):
self.api.add_route('/get_with_param/{param}', client.app.add_route('/misc', resource_misc)
self.resource_get_with_faulty_put)
def test_get(self):
self.simulate_request('/things/42/stuff/57')
self.assertEqual(self.srmock.status, falcon.HTTP_204)
self.assertTrue(self.resource_things.called)
def test_put(self):
self.simulate_request('/things/42/stuff/1337', method='PUT')
self.assertEqual(self.srmock.status, falcon.HTTP_201)
self.assertTrue(self.resource_things.called)
def test_post_not_allowed(self):
self.simulate_request('/things/42/stuff/1337', method='POST')
self.assertEqual(self.srmock.status, falcon.HTTP_405)
self.assertFalse(self.resource_things.called)
def test_misc(self):
for method in ['GET', 'HEAD', 'PUT', 'PATCH']: for method in ['GET', 'HEAD', 'PUT', 'PATCH']:
self.resource_misc.called = False resource_misc.called = False
self.simulate_request('/misc', method=method) client.simulate_request(path='/misc', method=method)
self.assertTrue(self.resource_misc.called) assert resource_misc.called
self.assertEqual(self.resource_misc.req.method, method) assert resource_misc.req.method == method
def test_methods_not_allowed_simple(self): def test_methods_not_allowed_simple(self, client, stonewall):
client.app.add_route('/stonewall', stonewall)
for method in ['GET', 'HEAD', 'PUT', 'PATCH']: for method in ['GET', 'HEAD', 'PUT', 'PATCH']:
self.simulate_request('/stonewall', method=method) response = client.simulate_request(path='/stonewall', method=method)
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
def test_methods_not_allowed_complex(self): def test_methods_not_allowed_complex(self, client, resource_things):
client.app.add_route('/things', resource_things)
client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
for method in HTTP_METHODS: for method in HTTP_METHODS:
if method in ('GET', 'PUT', 'HEAD', 'OPTIONS'): if method in ('GET', 'PUT', 'HEAD', 'OPTIONS'):
continue continue
self.resource_things.called = False resource_things.called = False
self.simulate_request('/things/84/stuff/65', method=method) response = client.simulate_request(path='/things/84/stuff/65', method=method)
self.assertFalse(self.resource_things.called) assert not resource_things.called
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
headers = self.srmock.headers headers = response.headers
allow_header = ('allow', 'GET, HEAD, PUT, OPTIONS') assert headers['allow'] == 'GET, HEAD, PUT, OPTIONS'
self.assertThat(headers, Contains(allow_header)) def test_method_not_allowed_with_param(self, client, resource_get_with_faulty_put):
client.app.add_route('/get_with_param/{param}', resource_get_with_faulty_put)
def test_method_not_allowed_with_param(self):
for method in HTTP_METHODS: for method in HTTP_METHODS:
if method in ('GET', 'PUT', 'OPTIONS'): if method in ('GET', 'PUT', 'OPTIONS'):
continue continue
self.resource_get_with_faulty_put.called = False resource_get_with_faulty_put.called = False
self.simulate_request( response = client.simulate_request(
'/get_with_param/bogus_param', method=method) method=method,
path='/get_with_param/bogus_param',
)
self.assertFalse(self.resource_get_with_faulty_put.called) assert not resource_get_with_faulty_put.called
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
headers = self.srmock.headers headers = response.headers
allow_header = ('allow', 'GET, PUT, OPTIONS') assert headers['allow'] == 'GET, PUT, OPTIONS'
self.assertThat(headers, Contains(allow_header)) def test_default_on_options(self, client, resource_things):
client.app.add_route('/things', resource_things)
client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
response = client.simulate_request(path='/things/84/stuff/65', method='OPTIONS')
assert response.status == falcon.HTTP_200
def test_default_on_options(self): headers = response.headers
self.simulate_request('/things/84/stuff/65', method='OPTIONS') assert headers['allow'] == 'GET, HEAD, PUT'
self.assertEqual(self.srmock.status, falcon.HTTP_200)
headers = self.srmock.headers def test_on_options(self, client):
allow_header = ('allow', 'GET, HEAD, PUT') response = client.simulate_request(path='/misc', method='OPTIONS')
assert response.status == falcon.HTTP_204
self.assertThat(headers, Contains(allow_header)) headers = response.headers
assert headers['allow'] == 'GET'
def test_on_options(self): def test_bogus_method(self, client, resource_things):
self.simulate_request('/misc', method='OPTIONS') client.app.add_route('/things', resource_things)
self.assertEqual(self.srmock.status, falcon.HTTP_204) client.app.add_route('/things/{id}/stuff/{sid}', resource_things)
response = client.simulate_request(path='/things', method=testing.rand_string(3, 4))
headers = self.srmock.headers assert not resource_things.called
allow_header = ('allow', 'GET') assert response.status == falcon.HTTP_400
self.assertThat(headers, Contains(allow_header))
def test_bogus_method(self):
self.simulate_request('/things', method=self.getUniqueString())
self.assertFalse(self.resource_things.called)
self.assertEqual(self.srmock.status, falcon.HTTP_400)

View File

@@ -7,14 +7,23 @@ except ImportError:
import json import json
import xml.etree.ElementTree as et import xml.etree.ElementTree as et
import ddt import pytest
from testtools.matchers import Not, raises
import yaml import yaml
import falcon import falcon
import falcon.testing as testing import falcon.testing as testing
@pytest.fixture
def client():
app = falcon.API()
resource = FaultyResource()
app.add_route('/fail', resource)
return testing.TestClient(app)
class FaultyResource: class FaultyResource:
def on_get(self, req, resp): def on_get(self, req, resp):
@@ -237,20 +246,15 @@ class MissingParamResource:
raise falcon.HTTPMissingParam('id', code='P1003') raise falcon.HTTPMissingParam('id', code='P1003')
@ddt.ddt class TestHTTPError(object):
class TestHTTPError(testing.TestBase):
def before(self): def _misc_test(self, client, exception, status, needs_title=True):
self.resource = FaultyResource() client.app.add_route('/misc', MiscErrorsResource(exception, needs_title))
self.api.add_route('/fail', self.resource)
def _misc_test(self, exception, status, needs_title=True): response = client.simulate_request(path='/misc')
self.api.add_route('/misc', MiscErrorsResource(exception, needs_title)) assert response.status == status
self.simulate_request('/misc') def test_base_class(self, client):
self.assertEqual(self.srmock.status, status)
def test_base_class(self):
headers = { headers = {
'X-Error-Title': 'Storage service down', 'X-Error-Title': 'Storage service down',
'X-Error-Description': ('The configured storage service is not ' 'X-Error-Description': ('The configured storage service is not '
@@ -269,37 +273,36 @@ class TestHTTPError(testing.TestBase):
# Try it with Accept: */* # Try it with Accept: */*
headers['Accept'] = '*/*' headers['Accept'] = '*/*'
body = self.simulate_request('/fail', headers=headers, decode='utf-8') response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
self.assertIn(('vary', 'Accept'), self.srmock.headers) assert response.headers['vary'] == 'Accept'
self.assertThat(lambda: json.loads(body), Not(raises(ValueError))) assert expected_body == response.json
self.assertEqual(expected_body, json.loads(body))
# Now try it with application/json # Now try it with application/json
headers['Accept'] = 'application/json' headers['Accept'] = 'application/json'
body = self.simulate_request('/fail', headers=headers, decode='utf-8') response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
self.assertThat(lambda: json.loads(body), Not(raises(ValueError))) assert response.json == expected_body
self.assertEqual(json.loads(body), expected_body)
def test_no_description_json(self): def test_no_description_json(self, client):
body = self.simulate_request('/fail', method='PATCH') response = client.simulate_patch('/fail')
self.assertEqual(self.srmock.status, falcon.HTTP_400) assert response.status == falcon.HTTP_400
self.assertEqual(body, [json.dumps({'title': '400 Bad Request'}).encode('utf8')]) assert response.json == {'title': '400 Bad Request'}
def test_no_description_xml(self): def test_no_description_xml(self, client):
body = self.simulate_request('/fail', method='PATCH', response = client.simulate_patch(
headers={'Accept': 'application/xml'}) path='/fail', headers={'Accept': 'application/xml'}
self.assertEqual(self.srmock.status, falcon.HTTP_400) )
assert response.status == falcon.HTTP_400
expected_xml = (b'<?xml version="1.0" encoding="UTF-8"?><error>' expected_xml = (b'<?xml version="1.0" encoding="UTF-8"?><error>'
b'<title>400 Bad Request</title></error>') b'<title>400 Bad Request</title></error>')
self.assertEqual(body, [expected_xml]) assert response.content == expected_xml
def test_client_does_not_accept_json_or_xml(self): def test_client_does_not_accept_json_or_xml(self, client):
headers = { headers = {
'Accept': 'application/x-yaml', 'Accept': 'application/x-yaml',
'X-Error-Title': 'Storage service down', 'X-Error-Title': 'Storage service down',
@@ -309,12 +312,12 @@ class TestHTTPError(testing.TestBase):
'X-Error-Status': falcon.HTTP_503 'X-Error-Status': falcon.HTTP_503
} }
body = self.simulate_request('/fail', headers=headers) response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
self.assertEqual(self.srmock.headers_dict['Vary'], 'Accept') assert response.headers['Vary'] == 'Accept'
self.assertEqual(body, []) assert not response.content
def test_custom_old_error_serializer(self): def test_custom_old_error_serializer(self, client):
headers = { headers = {
'X-Error-Title': 'Storage service down', 'X-Error-Title': 'Storage service down',
'X-Error-Description': ('The configured storage service is not ' 'X-Error-Description': ('The configured storage service is not '
@@ -348,24 +351,28 @@ class TestHTTPError(testing.TestBase):
def _check(media_type, deserializer): def _check(media_type, deserializer):
headers['Accept'] = media_type headers['Accept'] = media_type
self.api.set_error_serializer(_my_serializer) client.app.set_error_serializer(_my_serializer)
body = self.simulate_request('/fail', headers=headers) response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
actual_doc = deserializer(body[0].decode('utf-8')) actual_doc = deserializer(response.content.decode('utf-8'))
self.assertEqual(expected_doc, actual_doc) assert expected_doc == actual_doc
_check('application/x-yaml', yaml.load) _check('application/x-yaml', yaml.load)
_check('application/json', json.loads) _check('application/json', json.loads)
def test_custom_old_error_serializer_no_body(self): def test_custom_old_error_serializer_no_body(self, client):
headers = {
'X-Error-Status': falcon.HTTP_503
}
def _my_serializer(req, exception): def _my_serializer(req, exception):
return (None, None) return None, None
self.api.set_error_serializer(_my_serializer) client.app.set_error_serializer(_my_serializer)
self.simulate_request('/fail') client.simulate_request(path='/fail', headers=headers)
def test_custom_new_error_serializer(self): def test_custom_new_error_serializer(self, client):
headers = { headers = {
'X-Error-Title': 'Storage service down', 'X-Error-Title': 'Storage service down',
'X-Error-Description': ('The configured storage service is not ' 'X-Error-Description': ('The configured storage service is not '
@@ -400,17 +407,17 @@ class TestHTTPError(testing.TestBase):
def _check(media_type, deserializer): def _check(media_type, deserializer):
headers['Accept'] = media_type headers['Accept'] = media_type
self.api.set_error_serializer(_my_serializer) client.app.set_error_serializer(_my_serializer)
body = self.simulate_request('/fail', headers=headers) response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
actual_doc = deserializer(body[0].decode('utf-8')) actual_doc = deserializer(response.content.decode('utf-8'))
self.assertEqual(expected_doc, actual_doc) assert expected_doc == actual_doc
_check('application/x-yaml', yaml.load) _check('application/x-yaml', yaml.load)
_check('application/json', json.loads) _check('application/json', json.loads)
def test_client_does_not_accept_anything(self): def test_client_does_not_accept_anything(self, client):
headers = { headers = {
'Accept': '45087gigo;;;;', 'Accept': '45087gigo;;;;',
'X-Error-Title': 'Storage service down', 'X-Error-Title': 'Storage service down',
@@ -420,16 +427,16 @@ class TestHTTPError(testing.TestBase):
'X-Error-Status': falcon.HTTP_503 'X-Error-Status': falcon.HTTP_503
} }
body = self.simulate_request('/fail', headers=headers) response = client.simulate_request(path='/fail', headers=headers)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
self.assertEqual(body, []) assert not response.content
@ddt.data( @pytest.mark.parametrize('media_type', [
'application/json', 'application/json',
'application/vnd.company.system.project.resource+json;v=1.1', 'application/vnd.company.system.project.resource+json;v=1.1',
'application/json-patch+json', 'application/json-patch+json',
) ])
def test_forbidden(self, media_type): def test_forbidden(self, client, media_type):
headers = {'Accept': media_type} headers = {'Accept': media_type}
expected_body = { expected_body = {
@@ -443,14 +450,12 @@ class TestHTTPError(testing.TestBase):
}, },
} }
body = self.simulate_request('/fail', headers=headers, method='POST', response = client.simulate_post(path='/fail', headers=headers)
decode='utf-8')
self.assertEqual(self.srmock.status, falcon.HTTP_403) assert response.status == falcon.HTTP_403
self.assertThat(lambda: json.loads(body), Not(raises(ValueError))) assert response.json == expected_body
self.assertEqual(json.loads(body), expected_body)
def test_epic_fail_json(self): def test_epic_fail_json(self, client):
headers = {'Accept': 'application/json'} headers = {'Accept': 'application/json'}
expected_body = { expected_body = {
@@ -464,20 +469,18 @@ class TestHTTPError(testing.TestBase):
}, },
} }
body = self.simulate_request('/fail', headers=headers, method='PUT', response = client.simulate_put('/fail', headers=headers)
decode='utf-8')
self.assertEqual(self.srmock.status, falcon.HTTP_792) assert response.status == falcon.HTTP_792
self.assertThat(lambda: json.loads(body), Not(raises(ValueError))) assert response.json == expected_body
self.assertEqual(json.loads(body), expected_body)
@ddt.data( @pytest.mark.parametrize('media_type', [
'text/xml', 'text/xml',
'application/xml', 'application/xml',
'application/vnd.company.system.project.resource+xml;v=1.1', 'application/vnd.company.system.project.resource+xml;v=1.1',
'application/atom+xml', 'application/atom+xml',
) ])
def test_epic_fail_xml(self, media_type): def test_epic_fail_xml(self, client, media_type):
headers = {'Accept': media_type} headers = {'Accept': media_type}
expected_body = ('<?xml version="1.0" encoding="UTF-8"?>' + expected_body = ('<?xml version="1.0" encoding="UTF-8"?>' +
@@ -494,14 +497,16 @@ class TestHTTPError(testing.TestBase):
'</link>' + '</link>' +
'</error>') '</error>')
body = self.simulate_request('/fail', headers=headers, method='PUT', response = client.simulate_put(path='/fail', headers=headers)
decode='utf-8')
self.assertEqual(self.srmock.status, falcon.HTTP_792) assert response.status == falcon.HTTP_792
self.assertThat(lambda: et.fromstring(body), Not(raises(ValueError))) try:
self.assertEqual(body, expected_body) et.fromstring(response.content.decode('utf-8'))
except ValueError:
pytest.fail()
assert response.text == expected_body
def test_unicode_json(self): def test_unicode_json(self, client):
unicode_resource = UnicodeFaultyResource() unicode_resource = UnicodeFaultyResource()
expected_body = { expected_body = {
@@ -514,14 +519,14 @@ class TestHTTPError(testing.TestBase):
}, },
} }
self.api.add_route('/unicode', unicode_resource) client.app.add_route('/unicode', unicode_resource)
body = self.simulate_request('/unicode', decode='utf-8') response = client.simulate_request(path='/unicode')
self.assertTrue(unicode_resource.called) assert unicode_resource.called
self.assertEqual(self.srmock.status, falcon.HTTP_792) assert response.status == falcon.HTTP_792
self.assertEqual(expected_body, json.loads(body)) assert expected_body == response.json
def test_unicode_xml(self): def test_unicode_xml(self, client):
unicode_resource = UnicodeFaultyResource() unicode_resource = UnicodeFaultyResource()
expected_body = (u'<?xml version="1.0" encoding="UTF-8"?>' + expected_body = (u'<?xml version="1.0" encoding="UTF-8"?>' +
@@ -537,256 +542,255 @@ class TestHTTPError(testing.TestBase):
u'</link>' + u'</link>' +
u'</error>') u'</error>')
self.api.add_route('/unicode', unicode_resource) client.app.add_route('/unicode', unicode_resource)
body = self.simulate_request('/unicode', decode='utf-8', response = client.simulate_request(
headers={'accept': 'application/xml'}) path='/unicode',
headers={'accept': 'application/xml'}
)
self.assertTrue(unicode_resource.called) assert unicode_resource.called
self.assertEqual(self.srmock.status, falcon.HTTP_792) assert response.status == falcon.HTTP_792
self.assertEqual(expected_body, body) assert expected_body == response.text
def test_401(self): def test_401(self, client):
self.api.add_route('/401', UnauthorizedResource()) client.app.add_route('/401', UnauthorizedResource())
self.simulate_request('/401') response = client.simulate_request(path='/401')
self.assertEqual(self.srmock.status, falcon.HTTP_401) assert response.status == falcon.HTTP_401
self.assertIn(('www-authenticate', 'Basic realm="simple"'), assert response.headers['www-authenticate'] == 'Basic realm="simple"'
self.srmock.headers)
self.simulate_request('/401', method='POST') response = client.simulate_post('/401')
self.assertEqual(self.srmock.status, falcon.HTTP_401) assert response.status == falcon.HTTP_401
self.assertIn(('www-authenticate', 'Newauth realm="apps", ' assert response.headers['www-authenticate'] == 'Newauth realm="apps", Basic realm="simple"'
'Basic realm="simple"'),
self.srmock.headers)
self.simulate_request('/401', method='PUT') response = client.simulate_put('/401')
self.assertEqual(self.srmock.status, falcon.HTTP_401) assert response.status == falcon.HTTP_401
self.assertNotIn(('www-authenticate', []), self.srmock.headers) assert 'www-authenticate' not in response.headers
def test_404_without_body(self): def test_404_without_body(self, client):
self.api.add_route('/404', NotFoundResource()) client.app.add_route('/404', NotFoundResource())
body = self.simulate_request('/404') response = client.simulate_request(path='/404')
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_404
self.assertEqual(body, []) assert not response.content
def test_404_with_body(self): def test_404_with_body(self, client):
self.api.add_route('/404', NotFoundResourceWithBody()) client.app.add_route('/404', NotFoundResourceWithBody())
response = self.simulate_request('/404', decode='utf-8') response = client.simulate_request(path='/404')
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_404
self.assertNotEqual(response, []) assert response.content
expected_body = { expected_body = {
u'title': u'404 Not Found', u'title': u'404 Not Found',
u'description': u'Not Found' u'description': u'Not Found'
} }
self.assertEqual(json.loads(response), expected_body) assert response.json == expected_body
def test_405_without_body(self): def test_405_without_body(self, client):
self.api.add_route('/405', MethodNotAllowedResource()) client.app.add_route('/405', MethodNotAllowedResource())
response = self.simulate_request('/405') response = client.simulate_request(path='/405')
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
self.assertEqual(response, []) assert not response.content
self.assertIn(('allow', 'PUT'), self.srmock.headers) assert response.headers['allow'] == 'PUT'
def test_405_without_body_with_extra_headers(self): def test_405_without_body_with_extra_headers(self, client):
self.api.add_route('/405', MethodNotAllowedResourceWithHeaders()) client.app.add_route('/405', MethodNotAllowedResourceWithHeaders())
response = self.simulate_request('/405') response = client.simulate_request(path='/405')
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
self.assertEqual(response, []) assert not response.content
self.assertIn(('allow', 'PUT'), self.srmock.headers) assert response.headers['allow'] == 'PUT'
self.assertIn(('x-ping', 'pong'), self.srmock.headers) assert response.headers['x-ping'] == 'pong'
def test_405_without_body_with_extra_headers_double_check(self): def test_405_without_body_with_extra_headers_double_check(self, client):
self.api.add_route('/405', client.app.add_route(
MethodNotAllowedResourceWithHeadersWithAccept()) '/405/', MethodNotAllowedResourceWithHeadersWithAccept()
)
response = self.simulate_request('/405') response = client.simulate_request(path='/405')
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
self.assertEqual(response, []) assert not response.content
self.assertIn(('allow', 'PUT'), self.srmock.headers) assert response.headers['allow'] == 'PUT'
self.assertNotIn(('allow', 'GET,PUT'), self.srmock.headers) assert response.headers['allow'] != 'GET,PUT'
self.assertNotIn(('allow', 'GET'), self.srmock.headers) assert response.headers['allow'] != 'GET'
self.assertIn(('x-ping', 'pong'), self.srmock.headers) assert response.headers['x-ping'] == 'pong'
def test_405_with_body(self): def test_405_with_body(self, client):
self.api.add_route('/405', MethodNotAllowedResourceWithBody()) client.app.add_route('/405', MethodNotAllowedResourceWithBody())
response = self.simulate_request('/405', decode='utf-8') response = client.simulate_request(path='/405')
self.assertEqual(self.srmock.status, falcon.HTTP_405) assert response.status == falcon.HTTP_405
self.assertNotEqual(response, []) assert response.content
expected_body = { expected_body = {
u'title': u'405 Method Not Allowed', u'title': u'405 Method Not Allowed',
u'description': u'Not Allowed' u'description': u'Not Allowed'
} }
self.assertEqual(json.loads(response), expected_body) assert response.json == expected_body
self.assertIn(('allow', 'PUT'), self.srmock.headers) assert response.headers['allow'] == 'PUT'
def test_410_without_body(self): def test_410_without_body(self, client):
self.api.add_route('/410', GoneResource()) client.app.add_route('/410', GoneResource())
body = self.simulate_request('/410') response = client.simulate_request(path='/410')
self.assertEqual(self.srmock.status, falcon.HTTP_410) assert response.status == falcon.HTTP_410
self.assertEqual(body, []) assert not response.content
def test_410_with_body(self): def test_410_with_body(self, client):
self.api.add_route('/410', GoneResourceWithBody()) client.app.add_route('/410', GoneResourceWithBody())
response = self.simulate_request('/410', decode='utf-8') response = client.simulate_request(path='/410')
self.assertEqual(self.srmock.status, falcon.HTTP_410) assert response.status == falcon.HTTP_410
self.assertNotEqual(response, []) assert response.content
expected_body = { expected_body = {
u'title': u'410 Gone', u'title': u'410 Gone',
u'description': u'Gone with the wind' u'description': u'Gone with the wind'
} }
self.assertEqual(json.loads(response), expected_body) assert response.json == expected_body
def test_411(self): def test_411(self, client):
self.api.add_route('/411', LengthRequiredResource()) client.app.add_route('/411', LengthRequiredResource())
body = self.simulate_request('/411') response = client.simulate_request(path='/411')
parsed_body = json.loads(body[0].decode()) assert response.status == falcon.HTTP_411
self.assertEqual(self.srmock.status, falcon.HTTP_411) parsed_body = response.json
self.assertEqual(parsed_body['title'], 'title') assert parsed_body['title'] == 'title'
self.assertEqual(parsed_body['description'], 'description') assert parsed_body['description'] == 'description'
def test_413(self): def test_413(self, client):
self.api.add_route('/413', RequestEntityTooLongResource()) client.app.add_route('/413', RequestEntityTooLongResource())
body = self.simulate_request('/413') response = client.simulate_request(path='/413')
parsed_body = json.loads(body[0].decode()) assert response.status == falcon.HTTP_413
self.assertEqual(self.srmock.status, falcon.HTTP_413) parsed_body = response.json
self.assertEqual(parsed_body['title'], 'Request Rejected') assert parsed_body['title'] == 'Request Rejected'
self.assertEqual(parsed_body['description'], 'Request Body Too Large') assert parsed_body['description'] == 'Request Body Too Large'
self.assertNotIn('retry-after', self.srmock.headers) assert 'retry-after' not in response.headers
def test_temporary_413_integer_retry_after(self): def test_temporary_413_integer_retry_after(self, client):
self.api.add_route('/413', TemporaryRequestEntityTooLongResource('6')) client.app.add_route('/413', TemporaryRequestEntityTooLongResource('6'))
body = self.simulate_request('/413') response = client.simulate_request(path='/413')
parsed_body = json.loads(body[0].decode()) assert response.status == falcon.HTTP_413
self.assertEqual(self.srmock.status, falcon.HTTP_413) parsed_body = response.json
self.assertEqual(parsed_body['title'], 'Request Rejected') assert parsed_body['title'] == 'Request Rejected'
self.assertEqual(parsed_body['description'], 'Request Body Too Large') assert parsed_body['description'] == 'Request Body Too Large'
self.assertIn(('retry-after', '6'), self.srmock.headers) assert response.headers['retry-after'] == '6'
def test_temporary_413_datetime_retry_after(self): def test_temporary_413_datetime_retry_after(self, client):
date = datetime.datetime.now() + datetime.timedelta(minutes=5) date = datetime.datetime.now() + datetime.timedelta(minutes=5)
self.api.add_route('/413', client.app.add_route(
TemporaryRequestEntityTooLongResource(date)) '/413',
body = self.simulate_request('/413') TemporaryRequestEntityTooLongResource(date)
parsed_body = json.loads(body[0].decode()) )
response = client.simulate_request(path='/413')
self.assertEqual(self.srmock.status, falcon.HTTP_413) assert response.status == falcon.HTTP_413
self.assertEqual(parsed_body['title'], 'Request Rejected')
self.assertEqual(parsed_body['description'], 'Request Body Too Large')
self.assertIn(('retry-after', falcon.util.dt_to_http(date)),
self.srmock.headers)
def test_414(self): parsed_body = response.json
self.api.add_route('/414', UriTooLongResource()) assert parsed_body['title'] == 'Request Rejected'
self.simulate_request('/414') assert parsed_body['description'] == 'Request Body Too Large'
self.assertEqual(self.srmock.status, falcon.HTTP_414) assert response.headers['retry-after'] == falcon.util.dt_to_http(date)
def test_414_with_title(self): def test_414(self, client):
client.app.add_route('/414', UriTooLongResource())
response = client.simulate_request(path='/414')
assert response.status == falcon.HTTP_414
def test_414_with_title(self, client):
title = 'Argh! Error!' title = 'Argh! Error!'
self.api.add_route('/414', UriTooLongResource(title=title)) client.app.add_route('/414', UriTooLongResource(title=title))
body = self.simulate_request('/414', headers={}) response = client.simulate_request(path='/414', headers={})
parsed_body = json.loads(body[0].decode()) parsed_body = json.loads(response.content.decode())
self.assertEqual(parsed_body['title'], title) assert parsed_body['title'] == title
def test_414_with_description(self): def test_414_with_description(self, client):
description = 'Be short please.' description = 'Be short please.'
self.api.add_route('/414', UriTooLongResource(description=description)) client.app.add_route('/414', UriTooLongResource(description=description))
body = self.simulate_request('/414', headers={}) response = client.simulate_request(path='/414', headers={})
parsed_body = json.loads(body[0].decode()) parsed_body = json.loads(response.content.decode())
self.assertEqual(parsed_body['description'], description) assert parsed_body['description'] == description
def test_414_with_custom_kwargs(self): def test_414_with_custom_kwargs(self, client):
code = 'someid' code = 'someid'
self.api.add_route('/414', UriTooLongResource(code=code)) client.app.add_route('/414', UriTooLongResource(code=code))
body = self.simulate_request('/414', headers={}) response = client.simulate_request(path='/414', headers={})
parsed_body = json.loads(body[0].decode()) parsed_body = json.loads(response.content.decode())
self.assertEqual(parsed_body['code'], code) assert parsed_body['code'] == code
def test_416(self): def test_416(self, client):
self.api = falcon.API() client.app = falcon.API()
self.api.add_route('/416', RangeNotSatisfiableResource()) client.app.add_route('/416', RangeNotSatisfiableResource())
body = self.simulate_request('/416', headers={'accept': 'text/xml'}) response = client.simulate_request(path='/416', headers={'accept': 'text/xml'})
self.assertEqual(self.srmock.status, falcon.HTTP_416) assert response.status == falcon.HTTP_416
self.assertEqual(body, []) assert not response.content
self.assertIn(('content-range', 'bytes */123456'), self.srmock.headers) assert response.headers['content-range'] == 'bytes */123456'
self.assertIn(('content-length', '0'), self.srmock.headers) assert response.headers['content-length'] == '0'
def test_429_no_retry_after(self): def test_429_no_retry_after(self, client):
self.api.add_route('/429', TooManyRequestsResource()) client.app.add_route('/429', TooManyRequestsResource())
body = self.simulate_request('/429') response = client.simulate_request(path='/429')
parsed_body = json.loads(body[0].decode()) parsed_body = response.json
self.assertEqual(self.srmock.status, falcon.HTTP_429) assert response.status == falcon.HTTP_429
self.assertEqual(parsed_body['title'], 'Too many requests') assert parsed_body['title'] == 'Too many requests'
self.assertEqual(parsed_body['description'], '1 per minute') assert parsed_body['description'] == '1 per minute'
self.assertNotIn('retry-after', self.srmock.headers) assert 'retry-after' not in response.headers
def test_429(self): def test_429(self, client):
self.api.add_route('/429', TooManyRequestsResource(60)) client.app.add_route('/429', TooManyRequestsResource(60))
body = self.simulate_request('/429') response = client.simulate_request(path='/429')
parsed_body = json.loads(body[0].decode()) parsed_body = response.json
self.assertEqual(self.srmock.status, falcon.HTTP_429) assert response.status == falcon.HTTP_429
self.assertEqual(parsed_body['title'], 'Too many requests') assert parsed_body['title'] == 'Too many requests'
self.assertEqual(parsed_body['description'], '1 per minute') assert parsed_body['description'] == '1 per minute'
self.assertIn(('retry-after', '60'), self.srmock.headers) assert response.headers['retry-after'] == '60'
def test_429_datetime(self): def test_429_datetime(self, client):
date = datetime.datetime.now() + datetime.timedelta(minutes=1) date = datetime.datetime.now() + datetime.timedelta(minutes=1)
self.api.add_route('/429', TooManyRequestsResource(date)) client.app.add_route('/429', TooManyRequestsResource(date))
body = self.simulate_request('/429') response = client.simulate_request(path='/429')
parsed_body = json.loads(body[0].decode()) parsed_body = response.json
self.assertEqual(self.srmock.status, falcon.HTTP_429) assert response.status == falcon.HTTP_429
self.assertEqual(parsed_body['title'], 'Too many requests') assert parsed_body['title'] == 'Too many requests'
self.assertEqual(parsed_body['description'], '1 per minute') assert parsed_body['description'] == '1 per minute'
self.assertIn(('retry-after', falcon.util.dt_to_http(date)), assert response.headers['retry-after'] == falcon.util.dt_to_http(date)
self.srmock.headers)
def test_503_integer_retry_after(self): def test_503_integer_retry_after(self, client):
self.api.add_route('/503', ServiceUnavailableResource(60)) client.app.add_route('/503', ServiceUnavailableResource(60))
body = self.simulate_request('/503', decode='utf-8') response = client.simulate_request(path='/503')
expected_body = { expected_body = {
u'title': u'Oops', u'title': u'Oops',
u'description': u'Stand by...', u'description': u'Stand by...',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_503) assert response.status == falcon.HTTP_503
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
self.assertIn(('retry-after', '60'), self.srmock.headers) assert response.headers['retry-after'] == '60'
def test_503_datetime_retry_after(self): def test_503_datetime_retry_after(self, client):
date = datetime.datetime.now() + datetime.timedelta(minutes=5) date = datetime.datetime.now() + datetime.timedelta(minutes=5)
self.api.add_route('/503', client.app.add_route('/503', ServiceUnavailableResource(date))
ServiceUnavailableResource(date)) response = client.simulate_request(path='/503')
body = self.simulate_request('/503', decode='utf-8')
expected_body = { expected_body = {
u'title': u'Oops', u'title': u'Oops',
u'description': u'Stand by...', u'description': u'Stand by...',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_503) assert response.status == falcon.HTTP_503
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
self.assertIn(('retry-after', falcon.util.dt_to_http(date)), assert response.headers['retry-after'] == falcon.util.dt_to_http(date)
self.srmock.headers)
def test_invalid_header(self): def test_invalid_header(self, client):
self.api.add_route('/400', InvalidHeaderResource()) client.app.add_route('/400', InvalidHeaderResource())
body = self.simulate_request('/400', decode='utf-8') response = client.simulate_request(path='/400')
expected_desc = (u'The value provided for the X-Auth-Token ' expected_desc = (u'The value provided for the X-Auth-Token '
u'header is invalid. Please provide a valid token.') u'header is invalid. Please provide a valid token.')
@@ -797,24 +801,24 @@ class TestHTTPError(testing.TestBase):
u'code': u'A1001', u'code': u'A1001',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_400) assert response.status == falcon.HTTP_400
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
def test_missing_header(self): def test_missing_header(self, client):
self.api.add_route('/400', MissingHeaderResource()) client.app.add_route('/400', MissingHeaderResource())
body = self.simulate_request('/400', decode='utf-8') response = client.simulate_request(path='/400')
expected_body = { expected_body = {
u'title': u'Missing header value', u'title': u'Missing header value',
u'description': u'The X-Auth-Token header is required.', u'description': u'The X-Auth-Token header is required.',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_400) assert response.status == falcon.HTTP_400
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
def test_invalid_param(self): def test_invalid_param(self, client):
self.api.add_route('/400', InvalidParamResource()) client.app.add_route('/400', InvalidParamResource())
body = self.simulate_request('/400', decode='utf-8') response = client.simulate_request(path='/400')
expected_desc = (u'The "id" parameter is invalid. The ' expected_desc = (u'The "id" parameter is invalid. The '
u'value must be a hex-encoded UUID.') u'value must be a hex-encoded UUID.')
@@ -824,12 +828,12 @@ class TestHTTPError(testing.TestBase):
u'code': u'P1002', u'code': u'P1002',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_400) assert response.status == falcon.HTTP_400
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
def test_missing_param(self): def test_missing_param(self, client):
self.api.add_route('/400', MissingParamResource()) client.app.add_route('/400', MissingParamResource())
body = self.simulate_request('/400', decode='utf-8') response = client.simulate_request(path='/400')
expected_body = { expected_body = {
u'title': u'Missing parameter', u'title': u'Missing parameter',
@@ -837,30 +841,29 @@ class TestHTTPError(testing.TestBase):
u'code': u'P1003', u'code': u'P1003',
} }
self.assertEqual(self.srmock.status, falcon.HTTP_400) assert response.status == falcon.HTTP_400
self.assertEqual(json.loads(body), expected_body) assert response.json == expected_body
def test_misc(self): def test_misc(self, client):
self._misc_test(falcon.HTTPBadRequest, falcon.HTTP_400) self._misc_test(client, falcon.HTTPBadRequest, falcon.HTTP_400)
self._misc_test(falcon.HTTPNotAcceptable, falcon.HTTP_406, self._misc_test(client, falcon.HTTPNotAcceptable, falcon.HTTP_406,
needs_title=False) needs_title=False)
self._misc_test(falcon.HTTPConflict, falcon.HTTP_409) self._misc_test(client, falcon.HTTPConflict, falcon.HTTP_409)
self._misc_test(falcon.HTTPPreconditionFailed, falcon.HTTP_412) self._misc_test(client, falcon.HTTPPreconditionFailed, falcon.HTTP_412)
self._misc_test(falcon.HTTPUnsupportedMediaType, falcon.HTTP_415, self._misc_test(client, falcon.HTTPUnsupportedMediaType, falcon.HTTP_415,
needs_title=False) needs_title=False)
self._misc_test(falcon.HTTPUnprocessableEntity, falcon.HTTP_422) self._misc_test(client, falcon.HTTPUnprocessableEntity, falcon.HTTP_422)
self._misc_test(falcon.HTTPUnavailableForLegalReasons, falcon.HTTP_451, self._misc_test(client, falcon.HTTPUnavailableForLegalReasons, falcon.HTTP_451,
needs_title=False) needs_title=False)
self._misc_test(falcon.HTTPInternalServerError, falcon.HTTP_500) self._misc_test(client, falcon.HTTPInternalServerError, falcon.HTTP_500)
self._misc_test(falcon.HTTPBadGateway, falcon.HTTP_502) self._misc_test(client, falcon.HTTPBadGateway, falcon.HTTP_502)
def test_title_default_message_if_none(self): def test_title_default_message_if_none(self, client):
headers = { headers = {
'X-Error-Status': falcon.HTTP_503 'X-Error-Status': falcon.HTTP_503
} }
body = self.simulate_request('/fail', headers=headers, decode='utf-8') response = client.simulate_request(path='/fail', headers=headers)
body_json = json.loads(body)
self.assertEqual(self.srmock.status, headers['X-Error-Status']) assert response.status == headers['X-Error-Status']
self.assertEqual(body_json['title'], headers['X-Error-Status']) assert response.json['title'] == headers['X-Error-Status']

View File

@@ -47,8 +47,7 @@ class TestStatusResource:
resp.body = 'Fail' resp.body = 'Fail'
def on_patch(self, req, resp): def on_patch(self, req, resp):
raise HTTPStatus(falcon.HTTP_200, raise HTTPStatus(falcon.HTTP_200, body=None)
body=None)
@falcon.after(noop_after_hook) @falcon.after(noop_after_hook)
def on_delete(self, req, resp): def on_delete(self, req, resp):
@@ -74,50 +73,62 @@ class TestHookResource:
body='Pass') body='Pass')
class TestHTTPStatus(testing.TestBase): class TestHTTPStatus(object):
def before(self):
self.resource = TestStatusResource()
self.api.add_route('/status', self.resource)
def test_raise_status_in_before_hook(self): def test_raise_status_in_before_hook(self):
""" Make sure we get the 200 raised by before hook """ """ Make sure we get the 200 raised by before hook """
body = self.simulate_request('/status', method='GET', decode='utf-8') app = falcon.API()
self.assertEqual(self.srmock.status, falcon.HTTP_200) app.add_route('/status', TestStatusResource())
self.assertIn(('x-failed', 'False'), self.srmock.headers) client = testing.TestClient(app)
self.assertEqual(body, 'Pass')
response = client.simulate_request(path='/status', method='GET')
assert response.status == falcon.HTTP_200
assert response.headers['x-failed'] == 'False'
assert response.text == 'Pass'
def test_raise_status_in_responder(self): def test_raise_status_in_responder(self):
""" Make sure we get the 200 raised by responder """ """ Make sure we get the 200 raised by responder """
body = self.simulate_request('/status', method='POST', decode='utf-8') app = falcon.API()
self.assertEqual(self.srmock.status, falcon.HTTP_200) app.add_route('/status', TestStatusResource())
self.assertIn(('x-failed', 'False'), self.srmock.headers) client = testing.TestClient(app)
self.assertEqual(body, 'Pass')
response = client.simulate_request(path='/status', method='POST')
assert response.status == falcon.HTTP_200
assert response.headers['x-failed'] == 'False'
assert response.text == 'Pass'
def test_raise_status_runs_after_hooks(self): def test_raise_status_runs_after_hooks(self):
""" Make sure after hooks still run """ """ Make sure after hooks still run """
body = self.simulate_request('/status', method='PUT', decode='utf-8') app = falcon.API()
self.assertEqual(self.srmock.status, falcon.HTTP_200) app.add_route('/status', TestStatusResource())
self.assertIn(('x-failed', 'False'), self.srmock.headers) client = testing.TestClient(app)
self.assertEqual(body, 'Pass')
response = client.simulate_request(path='/status', method='PUT')
assert response.status == falcon.HTTP_200
assert response.headers['x-failed'] == 'False'
assert response.text == 'Pass'
def test_raise_status_survives_after_hooks(self): def test_raise_status_survives_after_hooks(self):
""" Make sure after hook doesn't overwrite our status """ """ Make sure after hook doesn't overwrite our status """
body = self.simulate_request('/status', method='DELETE', app = falcon.API()
decode='utf-8') app.add_route('/status', TestStatusResource())
self.assertEqual(self.srmock.status, falcon.HTTP_200) client = testing.TestClient(app)
self.assertIn(('x-failed', 'False'), self.srmock.headers)
self.assertEqual(body, 'Pass') response = client.simulate_request(path='/status', method='DELETE')
assert response.status == falcon.HTTP_200
assert response.headers['x-failed'] == 'False'
assert response.text == 'Pass'
def test_raise_status_empty_body(self): def test_raise_status_empty_body(self):
""" Make sure passing None to body results in empty body """ """ Make sure passing None to body results in empty body """
body = self.simulate_request('/status', method='PATCH', decode='utf-8') app = falcon.API()
self.assertEqual(body, '') app.add_route('/status', TestStatusResource())
client = testing.TestClient(app)
response = client.simulate_request(path='/status', method='PATCH')
assert response.text == ''
class TestHTTPStatusWithMiddleware(testing.TestBase): class TestHTTPStatusWithMiddleware(object):
def before(self):
self.resource = TestHookResource()
def test_raise_status_in_process_request(self): def test_raise_status_in_process_request(self):
""" Make sure we can raise status from middleware process request """ """ Make sure we can raise status from middleware process request """
class TestMiddleware: class TestMiddleware:
@@ -126,13 +137,14 @@ class TestHTTPStatusWithMiddleware(testing.TestBase):
headers={'X-Failed': 'False'}, headers={'X-Failed': 'False'},
body='Pass') body='Pass')
self.api = falcon.API(middleware=TestMiddleware()) app = falcon.API(middleware=TestMiddleware())
self.api.add_route('/status', self.resource) app.add_route('/status', TestHookResource())
client = testing.TestClient(app)
body = self.simulate_request('/status', method='GET', decode='utf-8') response = client.simulate_request(path='/status', method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn(('x-failed', 'False'), self.srmock.headers) assert response.headers['x-failed'] == 'False'
self.assertEqual(body, 'Pass') assert response.text == 'Pass'
def test_raise_status_in_process_resource(self): def test_raise_status_in_process_resource(self):
""" Make sure we can raise status from middleware process resource """ """ Make sure we can raise status from middleware process resource """
@@ -142,13 +154,14 @@ class TestHTTPStatusWithMiddleware(testing.TestBase):
headers={'X-Failed': 'False'}, headers={'X-Failed': 'False'},
body='Pass') body='Pass')
self.api = falcon.API(middleware=TestMiddleware()) app = falcon.API(middleware=TestMiddleware())
self.api.add_route('/status', self.resource) app.add_route('/status', TestHookResource())
client = testing.TestClient(app)
body = self.simulate_request('/status', method='GET', decode='utf-8') response = client.simulate_request(path='/status', method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn(('x-failed', 'False'), self.srmock.headers) assert response.headers['x-failed'] == 'False'
self.assertEqual(body, 'Pass') assert response.text == 'Pass'
def test_raise_status_runs_process_response(self): def test_raise_status_runs_process_response(self):
""" Make sure process_response still runs """ """ Make sure process_response still runs """
@@ -158,10 +171,11 @@ class TestHTTPStatusWithMiddleware(testing.TestBase):
resp.set_header('X-Failed', 'False') resp.set_header('X-Failed', 'False')
resp.body = 'Pass' resp.body = 'Pass'
self.api = falcon.API(middleware=TestMiddleware()) app = falcon.API(middleware=TestMiddleware())
self.api.add_route('/status', self.resource) app.add_route('/status', TestHookResource())
client = testing.TestClient(app)
body = self.simulate_request('/status', method='GET', decode='utf-8') response = client.simulate_request(path='/status', method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn(('x-failed', 'False'), self.srmock.headers) assert response.headers['x-failed'] == 'False'
self.assertEqual(body, 'Pass') assert response.text == 'Pass'

View File

@@ -1,4 +1,7 @@
from datetime import datetime from datetime import datetime
import pytest
try: try:
import ujson as json import ujson as json
except ImportError: except ImportError:
@@ -10,6 +13,7 @@ import falcon.testing as testing
_EXPECTED_BODY = {u'status': u'ok'} _EXPECTED_BODY = {u'status': u'ok'}
context = {'executed_methods': []} context = {'executed_methods': []}
TEST_ROUTE = '/test_path'
class CaptureResponseMiddleware(object): class CaptureResponseMiddleware(object):
@@ -100,34 +104,28 @@ class MiddlewareClassResource(object):
raise falcon.HTTPForbidden(falcon.HTTP_403, 'Setec Astronomy') raise falcon.HTTPForbidden(falcon.HTTP_403, 'Setec Astronomy')
class TestMiddleware(testing.TestBase): class TestMiddleware(object):
def setup_method(self, method):
def setUp(self):
# Clear context # Clear context
global context global context
context = {'executed_methods': []} context = {'executed_methods': []}
testing.TestBase.setUp(self)
# TODO(kgriffs): Consider adding this to TestBase
def simulate_json_request(self, *args, **kwargs):
result = self.simulate_request(*args, decode='utf-8', **kwargs)
return json.loads(result)
class TestRequestTimeMiddleware(TestMiddleware): class TestRequestTimeMiddleware(TestMiddleware):
def test_skip_process_resource(self): def test_skip_process_resource(self):
global context global context
self.api = falcon.API(middleware=[RequestTimeMiddleware()]) app = falcon.API(middleware=[RequestTimeMiddleware()])
self.api.add_route('/', MiddlewareClassResource()) app.add_route('/', MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request('/404') response = client.simulate_request(path='/404')
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_404
self.assertIn('start_time', context) assert 'start_time' in context
self.assertNotIn('mid_time', context) assert 'mid_time' not in context
self.assertIn('end_time', context) assert 'end_time' in context
self.assertFalse(context['req_succeeded']) assert not context['req_succeeded']
def test_add_invalid_middleware(self): def test_add_invalid_middleware(self):
"""Test than an invalid class can not be added as middleware""" """Test than an invalid class can not be added as middleware"""
@@ -136,11 +134,14 @@ class TestRequestTimeMiddleware(TestMiddleware):
pass pass
mw_list = [RequestTimeMiddleware(), InvalidMiddleware] mw_list = [RequestTimeMiddleware(), InvalidMiddleware]
self.assertRaises(AttributeError, falcon.API, middleware=mw_list) with pytest.raises(AttributeError):
falcon.API(middleware=mw_list)
mw_list = [RequestTimeMiddleware(), 'InvalidMiddleware'] mw_list = [RequestTimeMiddleware(), 'InvalidMiddleware']
self.assertRaises(TypeError, falcon.API, middleware=mw_list) with pytest.raises(TypeError):
falcon.API(middleware=mw_list)
mw_list = [{'process_request': 90}] mw_list = [{'process_request': 90}]
self.assertRaises(TypeError, falcon.API, middleware=mw_list) with pytest.raises(TypeError):
falcon.API(middleware=mw_list)
def test_response_middleware_raises_exception(self): def test_response_middleware_raises_exception(self):
"""Test that error in response middleware is propagated up""" """Test that error in response middleware is propagated up"""
@@ -149,92 +150,97 @@ class TestRequestTimeMiddleware(TestMiddleware):
def process_response(self, req, resp, resource): def process_response(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[RaiseErrorMiddleware()]) app = falcon.API(middleware=[RaiseErrorMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.assertRaises(Exception, self.simulate_request, self.test_route) with pytest.raises(Exception):
client.simulate_request(path=TEST_ROUTE)
def test_log_get_request(self): def test_log_get_request(self):
"""Test that Log middleware is executed""" """Test that Log middleware is executed"""
global context global context
self.api = falcon.API(middleware=[RequestTimeMiddleware()]) app = falcon.API(middleware=[RequestTimeMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request(self.test_route) response = client.simulate_request(path=TEST_ROUTE)
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn('start_time', context) assert 'start_time' in context
self.assertIn('mid_time', context) assert 'mid_time' in context
self.assertIn('end_time', context) assert 'end_time' in context
self.assertTrue(context['mid_time'] >= context['start_time'], assert context['mid_time'] >= context['start_time'], \
'process_resource not executed after request') 'process_resource not executed after request'
self.assertTrue(context['end_time'] >= context['start_time'], assert context['end_time'] >= context['start_time'], \
'process_response not executed after request') 'process_response not executed after request'
self.assertTrue(context['req_succeeded']) assert context['req_succeeded']
class TestTransactionIdMiddleware(TestMiddleware): class TestTransactionIdMiddleware(TestMiddleware):
def test_generate_trans_id_with_request(self): def test_generate_trans_id_with_request(self):
"""Test that TransactionIdmiddleware is executed""" """Test that TransactionIdmiddleware is executed"""
global context global context
self.api = falcon.API(middleware=TransactionIdMiddleware()) app = falcon.API(middleware=TransactionIdMiddleware())
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request(self.test_route) response = client.simulate_request(path=TEST_ROUTE)
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn('transaction_id', context) assert 'transaction_id' in context
self.assertEqual('unique-req-id', context['transaction_id']) assert 'unique-req-id' == context['transaction_id']
class TestSeveralMiddlewares(TestMiddleware): class TestSeveralMiddlewares(TestMiddleware):
def test_generate_trans_id_and_time_with_request(self): def test_generate_trans_id_and_time_with_request(self):
global context global context
self.api = falcon.API(middleware=[TransactionIdMiddleware(), app = falcon.API(middleware=[TransactionIdMiddleware(),
RequestTimeMiddleware()]) RequestTimeMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request(self.test_route) response = client.simulate_request(path=TEST_ROUTE)
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.assertIn('transaction_id', context) assert 'transaction_id' in context
self.assertEqual('unique-req-id', context['transaction_id']) assert 'unique-req-id' == context['transaction_id']
self.assertIn('start_time', context) assert 'start_time' in context
self.assertIn('mid_time', context) assert 'mid_time' in context
self.assertIn('end_time', context) assert 'end_time' in context
self.assertTrue(context['mid_time'] >= context['start_time'], assert context['mid_time'] >= context['start_time'], \
'process_resource not executed after request') 'process_resource not executed after request'
self.assertTrue(context['end_time'] >= context['start_time'], assert context['end_time'] >= context['start_time'], \
'process_response not executed after request') 'process_response not executed after request'
def test_legacy_middleware_called_with_correct_args(self): def test_legacy_middleware_called_with_correct_args(self):
global context global context
self.api = falcon.API(middleware=[ExecutedFirstMiddleware()]) app = falcon.API(middleware=[ExecutedFirstMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
self.assertIsInstance(context['req'], falcon.Request) assert isinstance(context['req'], falcon.Request)
self.assertIsInstance(context['resp'], falcon.Response) assert isinstance(context['resp'], falcon.Response)
self.assertIsInstance(context['resource'], MiddlewareClassResource) assert isinstance(context['resource'], MiddlewareClassResource)
def test_middleware_execution_order(self): def test_middleware_execution_order(self):
global context global context
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(), app = falcon.API(middleware=[ExecutedFirstMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request(self.test_route) response = client.simulate_request(path=TEST_ROUTE)
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
# as the method registration is in a list, the order also is # as the method registration is in a list, the order also is
# tested # tested
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -245,19 +251,20 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_independent_middleware_execution_order(self): def test_independent_middleware_execution_order(self):
global context global context
self.api = falcon.API(independent_middleware=True, app = falcon.API(independent_middleware=True,
middleware=[ExecutedFirstMiddleware(), middleware=[ExecutedFirstMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request(self.test_route) response = client.simulate_request(path=TEST_ROUTE)
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
# as the method registration is in a list, the order also is # as the method registration is in a list, the order also is
# tested # tested
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -268,7 +275,7 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_multiple_reponse_mw_throw_exception(self): def test_multiple_reponse_mw_throw_exception(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -289,20 +296,22 @@ class TestSeveralMiddlewares(TestMiddleware):
context['executed_methods'].append('process_response') context['executed_methods'].append('process_response')
context['req_succeeded'].append(req_succeeded) context['req_succeeded'].append(req_succeeded)
self.api = falcon.API(middleware=[ProcessResponseMiddleware(), app = falcon.API(middleware=[ProcessResponseMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ProcessResponseMiddleware(), ProcessResponseMiddleware(),
RaiseStatusMiddleware(), RaiseStatusMiddleware(),
ProcessResponseMiddleware()]) ProcessResponseMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
self.simulate_request(self.test_route) client = testing.TestClient(app)
self.assertEqual(self.srmock.status, falcon.HTTP_748) response = client.simulate_request(path=TEST_ROUTE)
assert response.status == falcon.HTTP_748
expected_methods = ['process_response'] * 3 expected_methods = ['process_response'] * 3
self.assertEqual(context['executed_methods'], expected_methods) assert context['executed_methods'] == expected_methods
self.assertEqual(context['req_succeeded'], [True, False, False]) assert context['req_succeeded'] == [True, False, False]
def test_inner_mw_throw_exception(self): def test_inner_mw_throw_exception(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -313,19 +322,21 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_request(self, req, resp): def process_request(self, req, resp):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[TransactionIdMiddleware(), app = falcon.API(middleware=[TransactionIdMiddleware(),
RequestTimeMiddleware(), RequestTimeMiddleware(),
RaiseErrorMiddleware()]) RaiseErrorMiddleware()])
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.assertRaises(Exception, self.simulate_request, self.test_route) with pytest.raises(Exception):
client.simulate_request(path=TEST_ROUTE)
# RequestTimeMiddleware process_response should be executed # RequestTimeMiddleware process_response should be executed
self.assertIn('transaction_id', context) assert 'transaction_id' in context
self.assertIn('start_time', context) assert 'start_time' in context
self.assertNotIn('mid_time', context) assert 'mid_time' not in context
self.assertIn('end_time', context) assert 'end_time' in context
def test_inner_mw_with_ex_handler_throw_exception(self): def test_inner_mw_with_ex_handler_throw_exception(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -336,25 +347,26 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_request(self, req, resp, resource): def process_request(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[TransactionIdMiddleware(), app = falcon.API(middleware=[TransactionIdMiddleware(),
RequestTimeMiddleware(), RequestTimeMiddleware(),
RaiseErrorMiddleware()]) RaiseErrorMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
context['error_handler'] = True context['error_handler'] = True
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# RequestTimeMiddleware process_response should be executed # RequestTimeMiddleware process_response should be executed
self.assertIn('transaction_id', context) assert 'transaction_id' in context
self.assertIn('start_time', context) assert 'start_time' in context
self.assertNotIn('mid_time', context) assert 'mid_time' not in context
self.assertIn('end_time', context) assert 'end_time' in context
self.assertIn('error_handler', context) assert 'error_handler' in context
def test_outer_mw_with_ex_handler_throw_exception(self): def test_outer_mw_with_ex_handler_throw_exception(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -365,25 +377,26 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_request(self, req, resp): def process_request(self, req, resp):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[TransactionIdMiddleware(), app = falcon.API(middleware=[TransactionIdMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
RequestTimeMiddleware()]) RequestTimeMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
context['error_handler'] = True context['error_handler'] = True
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
self.assertIn('transaction_id', context) assert 'transaction_id' in context
self.assertNotIn('start_time', context) assert 'start_time' not in context
self.assertNotIn('mid_time', context) assert 'mid_time' not in context
self.assertNotIn('end_time', context) assert 'end_time' not in context
self.assertIn('error_handler', context) assert 'error_handler' in context
def test_order_mw_executed_when_exception_in_resp(self): def test_order_mw_executed_when_exception_in_resp(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -394,18 +407,19 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_response(self, req, resp, resource): def process_response(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(), app = falcon.API(middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -416,7 +430,7 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_order_independent_mw_executed_when_exception_in_resp(self): def test_order_independent_mw_executed_when_exception_in_resp(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -427,19 +441,20 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_response(self, req, resp, resource): def process_response(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(independent_middleware=True, app = falcon.API(independent_middleware=True,
middleware=[ExecutedFirstMiddleware(), middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -450,7 +465,7 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_order_mw_executed_when_exception_in_req(self): def test_order_mw_executed_when_exception_in_req(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -461,25 +476,26 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_request(self, req, resp): def process_request(self, req, resp):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(), app = falcon.API(middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
expectedExecutedMethods = [ expectedExecutedMethods = [
'ExecutedFirstMiddleware.process_request', 'ExecutedFirstMiddleware.process_request',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_order_independent_mw_executed_when_exception_in_req(self): def test_order_independent_mw_executed_when_exception_in_req(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -490,19 +506,20 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_request(self, req, resp): def process_request(self, req, resp):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(independent_middleware=True, app = falcon.API(independent_middleware=True,
middleware=[ExecutedFirstMiddleware(), middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# All response middleware still executed... # All response middleware still executed...
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -510,7 +527,7 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_order_mw_executed_when_exception_in_rsrc(self): def test_order_mw_executed_when_exception_in_rsrc(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -521,18 +538,19 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_resource(self, req, resp, resource): def process_resource(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(middleware=[ExecutedFirstMiddleware(), app = falcon.API(middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -542,7 +560,7 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
def test_order_independent_mw_executed_when_exception_in_rsrc(self): def test_order_independent_mw_executed_when_exception_in_rsrc(self):
"""Test that error in inner middleware leaves""" """Test that error in inner middleware leaves"""
@@ -553,19 +571,20 @@ class TestSeveralMiddlewares(TestMiddleware):
def process_resource(self, req, resp, resource): def process_resource(self, req, resp, resource):
raise Exception('Always fail') raise Exception('Always fail')
self.api = falcon.API(independent_middleware=True, app = falcon.API(independent_middleware=True,
middleware=[ExecutedFirstMiddleware(), middleware=[ExecutedFirstMiddleware(),
RaiseErrorMiddleware(), RaiseErrorMiddleware(),
ExecutedLastMiddleware()]) ExecutedLastMiddleware()])
def handler(ex, req, resp, params): def handler(ex, req, resp, params):
pass pass
self.api.add_error_handler(Exception, handler) app.add_error_handler(Exception, handler)
self.api.add_route(self.test_route, MiddlewareClassResource()) app.add_route(TEST_ROUTE, MiddlewareClassResource())
client = testing.TestClient(app)
self.simulate_request(self.test_route) client.simulate_request(path=TEST_ROUTE)
# Any mw is executed now... # Any mw is executed now...
expectedExecutedMethods = [ expectedExecutedMethods = [
@@ -575,27 +594,26 @@ class TestSeveralMiddlewares(TestMiddleware):
'ExecutedLastMiddleware.process_response', 'ExecutedLastMiddleware.process_response',
'ExecutedFirstMiddleware.process_response' 'ExecutedFirstMiddleware.process_response'
] ]
self.assertEqual(expectedExecutedMethods, context['executed_methods']) assert expectedExecutedMethods == context['executed_methods']
class TestRemoveBasePathMiddleware(TestMiddleware): class TestRemoveBasePathMiddleware(TestMiddleware):
def test_base_path_is_removed_before_routing(self): def test_base_path_is_removed_before_routing(self):
"""Test that RemoveBasePathMiddleware is executed before routing""" """Test that RemoveBasePathMiddleware is executed before routing"""
self.api = falcon.API(middleware=RemoveBasePathMiddleware()) app = falcon.API(middleware=RemoveBasePathMiddleware())
# We dont include /base_path as it will be removed in middleware # We dont include /base_path as it will be removed in middleware
self.api.add_route('/sub_path', MiddlewareClassResource()) app.add_route('/sub_path', MiddlewareClassResource())
client = testing.TestClient(app)
body = self.simulate_json_request('/base_path/sub_path') response = client.simulate_request(path='/base_path/sub_path')
self.assertEqual(_EXPECTED_BODY, body) assert _EXPECTED_BODY == response.json
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
self.simulate_request('/base_pathIncorrect/sub_path') response = client.simulate_request(path='/base_pathIncorrect/sub_path')
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_404
class TestResourceMiddleware(TestMiddleware): class TestResourceMiddleware(TestMiddleware):
def test_can_access_resource_params(self): def test_can_access_resource_params(self):
"""Test that params can be accessed from within process_resource""" """Test that params can be accessed from within process_resource"""
global context global context
@@ -604,49 +622,51 @@ class TestResourceMiddleware(TestMiddleware):
def on_get(self, req, resp, **params): def on_get(self, req, resp, **params):
resp.body = json.dumps(params) resp.body = json.dumps(params)
self.api = falcon.API(middleware=AccessParamsMiddleware()) app = falcon.API(middleware=AccessParamsMiddleware())
self.api.add_route('/path/{id}', Resource()) app.add_route('/path/{id}', Resource())
body = self.simulate_json_request('/path/22') client = testing.TestClient(app)
response = client.simulate_request(path='/path/22')
self.assertIn('params', context) assert 'params' in context
self.assertTrue(context['params']) assert context['params']
self.assertEqual(context['params']['id'], '22') assert context['params']['id'] == '22'
self.assertEqual(body, {'added': True, 'id': '22'}) assert response.json == {'added': True, 'id': '22'}
class TestErrorHandling(TestMiddleware): class TestErrorHandling(TestMiddleware):
def setUp(self):
super(TestErrorHandling, self).setUp()
self.mw = CaptureResponseMiddleware()
self.api = falcon.API(middleware=self.mw)
self.api.add_route('/', MiddlewareClassResource())
def test_error_composed_before_resp_middleware_called(self): def test_error_composed_before_resp_middleware_called(self):
self.simulate_request('/', method='POST') mw = CaptureResponseMiddleware()
self.assertEqual(self.srmock.status, falcon.HTTP_403) app = falcon.API(middleware=mw)
self.assertEqual(self.mw.resp.status, self.srmock.status) app.add_route('/', MiddlewareClassResource())
client = testing.TestClient(app)
composed_body = json.loads(self.mw.resp.body) response = client.simulate_request(path='/', method='POST')
self.assertEqual(composed_body['title'], self.srmock.status) assert response.status == falcon.HTTP_403
assert mw.resp.status == response.status
self.assertFalse(self.mw.req_succeeded) composed_body = json.loads(mw.resp.body)
assert composed_body['title'] == response.status
assert not mw.req_succeeded
# NOTE(kgriffs): Sanity-check the other params passed to # NOTE(kgriffs): Sanity-check the other params passed to
# process_response() # process_response()
self.assertIsInstance(self.mw.req, falcon.Request) assert isinstance(mw.req, falcon.Request)
self.assertIsInstance(self.mw.resource, MiddlewareClassResource) assert isinstance(mw.resource, MiddlewareClassResource)
def test_http_status_raised_from_error_handler(self): def test_http_status_raised_from_error_handler(self):
mw = CaptureResponseMiddleware()
app = falcon.API(middleware=mw)
app.add_route('/', MiddlewareClassResource())
client = testing.TestClient(app)
def _http_error_handler(error, req, resp, params): def _http_error_handler(error, req, resp, params):
raise falcon.HTTPStatus(falcon.HTTP_201) raise falcon.HTTPStatus(falcon.HTTP_201)
# NOTE(kgriffs): This will take precedence over the default # NOTE(kgriffs): This will take precedence over the default
# handler for facon.HTTPError. # handler for facon.HTTPError.
self.api.add_error_handler(falcon.HTTPError, _http_error_handler) app.add_error_handler(falcon.HTTPError, _http_error_handler)
self.simulate_request('/', method='POST') response = client.simulate_request(path='/', method='POST')
self.assertEqual(self.srmock.status, falcon.HTTP_201) assert response.status == falcon.HTTP_201
self.assertEqual(self.mw.resp.status, self.srmock.status) assert mw.resp.status == response.status

View File

@@ -1,34 +1,32 @@
import ddt import pytest
from falcon.request import RequestOptions from falcon.request import RequestOptions
import falcon.testing as testing
@ddt.ddt class TestRequestOptions(object):
class TestRequestOptions(testing.TestBase):
def test_option_defaults(self): def test_option_defaults(self):
options = RequestOptions() options = RequestOptions()
self.assertFalse(options.keep_blank_qs_values) assert not options.keep_blank_qs_values
self.assertFalse(options.auto_parse_form_urlencoded) assert not options.auto_parse_form_urlencoded
self.assertTrue(options.auto_parse_qs_csv) assert options.auto_parse_qs_csv
self.assertTrue(options.strip_url_path_trailing_slash) assert options.strip_url_path_trailing_slash
@ddt.data( @pytest.mark.parametrize('option_name', [
'keep_blank_qs_values', 'keep_blank_qs_values',
'auto_parse_form_urlencoded', 'auto_parse_form_urlencoded',
'auto_parse_qs_csv', 'auto_parse_qs_csv',
'strip_url_path_trailing_slash', 'strip_url_path_trailing_slash',
) ])
def test_options_toggle(self, option_name): def test_options_toggle(self, option_name):
options = RequestOptions() options = RequestOptions()
setattr(options, option_name, True) setattr(options, option_name, True)
self.assertTrue(getattr(options, option_name)) assert getattr(options, option_name)
setattr(options, option_name, False) setattr(options, option_name, False)
self.assertFalse(getattr(options, option_name)) assert not getattr(options, option_name)
def test_incorrect_options(self): def test_incorrect_options(self):
options = RequestOptions() options = RequestOptions()
@@ -36,4 +34,5 @@ class TestRequestOptions(testing.TestBase):
def _assign_invalid(): def _assign_invalid():
options.invalid_option_and_attribute = True options.invalid_option_and_attribute = True
self.assertRaises(AttributeError, _assign_invalid) with pytest.raises(AttributeError):
_assign_invalid()

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,19 @@
import ddt import pytest
import falcon import falcon
import falcon.testing as testing import falcon.testing as testing
@pytest.fixture
def client():
app = falcon.API()
resource = RedirectingResource()
app.add_route('/', resource)
return testing.TestClient(app)
class RedirectingResource(object): class RedirectingResource(object):
# NOTE(kgriffs): You wouldn't necessarily use these types of # NOTE(kgriffs): You wouldn't necessarily use these types of
@@ -26,24 +36,18 @@ class RedirectingResource(object):
raise falcon.HTTPPermanentRedirect('/perm/redirect') raise falcon.HTTPPermanentRedirect('/perm/redirect')
@ddt.ddt class TestRedirects(object):
class TestRedirects(testing.TestBase):
def before(self): @pytest.mark.parametrize('method,expected_status,expected_location', [
self.api.add_route('/', RedirectingResource())
@ddt.data(
('GET', falcon.HTTP_301, '/moved/perm'), ('GET', falcon.HTTP_301, '/moved/perm'),
('POST', falcon.HTTP_302, '/found'), ('POST', falcon.HTTP_302, '/found'),
('PUT', falcon.HTTP_303, '/see/other'), ('PUT', falcon.HTTP_303, '/see/other'),
('DELETE', falcon.HTTP_307, '/tmp/redirect'), ('DELETE', falcon.HTTP_307, '/tmp/redirect'),
('HEAD', falcon.HTTP_308, '/perm/redirect'), ('HEAD', falcon.HTTP_308, '/perm/redirect'),
) ])
@ddt.unpack def test_redirect(self, client, method, expected_status, expected_location):
def test_redirect(self, method, expected_status, expected_location): result = client.simulate_request(path='/', method=method)
result = self.simulate_request('/', method=method)
self.assertEqual(result, []) assert not result.content
self.assertEqual(self.srmock.status, expected_status) assert result.status == expected_status
self.assertEqual(self.srmock.headers_dict['location'], assert result.headers['location'] == expected_location
expected_location)

View File

@@ -1,8 +1,7 @@
import datetime import datetime
import ddt import pytest
import six import six
import testtools
import falcon import falcon
from falcon.request import Request, RequestOptions from falcon.request import Request, RequestOptions
@@ -12,12 +11,9 @@ import falcon.uri
_PROTOCOLS = ['HTTP/1.0', 'HTTP/1.1'] _PROTOCOLS = ['HTTP/1.0', 'HTTP/1.1']
@ddt.ddt class TestReqVars(object):
class TestReqVars(testing.TestCase):
def setUp(self):
super(TestReqVars, self).setUp()
def setup_method(self, method):
self.qs = 'marker=deadbeef&limit=10' self.qs = 'marker=deadbeef&limit=10'
self.headers = { self.headers = {
@@ -51,36 +47,36 @@ class TestReqVars(testing.TestCase):
Request(env) Request(env)
def test_empty(self): def test_empty(self):
self.assertIs(self.req.auth, None) assert self.req.auth is None
def test_host(self): def test_host(self):
self.assertEqual(self.req.host, testing.DEFAULT_HOST) assert self.req.host == testing.DEFAULT_HOST
def test_subdomain(self): def test_subdomain(self):
req = Request(testing.create_environ( req = Request(testing.create_environ(
host='com', host='com',
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertIs(req.subdomain, None) assert req.subdomain is None
req = Request(testing.create_environ( req = Request(testing.create_environ(
host='example.com', host='example.com',
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertEqual(req.subdomain, 'example') assert req.subdomain == 'example'
req = Request(testing.create_environ( req = Request(testing.create_environ(
host='highwire.example.com', host='highwire.example.com',
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertEqual(req.subdomain, 'highwire') assert req.subdomain == 'highwire'
req = Request(testing.create_environ( req = Request(testing.create_environ(
host='lb01.dfw01.example.com', host='lb01.dfw01.example.com',
port=8080, port=8080,
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertEqual(req.subdomain, 'lb01') assert req.subdomain == 'lb01'
# NOTE(kgriffs): Behavior for IP addresses is undefined, # NOTE(kgriffs): Behavior for IP addresses is undefined,
# so just make sure it doesn't blow up. # so just make sure it doesn't blow up.
@@ -88,7 +84,7 @@ class TestReqVars(testing.TestCase):
host='127.0.0.1', host='127.0.0.1',
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertEqual(type(req.subdomain), str) assert type(req.subdomain) == str
# NOTE(kgriffs): Test fallback to SERVER_NAME by using # NOTE(kgriffs): Test fallback to SERVER_NAME by using
# HTTP 1.0, which will cause .create_environ to not set # HTTP 1.0, which will cause .create_environ to not set
@@ -98,7 +94,7 @@ class TestReqVars(testing.TestCase):
host='example.com', host='example.com',
path='/hello', path='/hello',
headers=self.headers)) headers=self.headers))
self.assertEqual(req.subdomain, 'example') assert req.subdomain == 'example'
def test_reconstruct_url(self): def test_reconstruct_url(self):
req = self.req req = self.req
@@ -112,14 +108,14 @@ class TestReqVars(testing.TestCase):
expected_uri = ''.join([scheme, '://', host, app, path, expected_uri = ''.join([scheme, '://', host, app, path,
'?', query_string]) '?', query_string])
self.assertEqual(expected_uri, req.uri) assert expected_uri == req.uri
@ddt.data( @pytest.mark.skipif(not six.PY3, reason='Test only applies to Python 3')
@pytest.mark.parametrize('test_path', [
u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442', u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442',
u'/test/%E5%BB%B6%E5%AE%89', u'/test/%E5%BB%B6%E5%AE%89',
u'/test/%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC', u'/test/%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC',
) ])
@testtools.skipUnless(six.PY3, 'Test only applies to Python 3')
def test_nonlatin_path(self, test_path): def test_nonlatin_path(self, test_path):
# NOTE(kgriffs): When a request comes in, web servers decode # NOTE(kgriffs): When a request comes in, web servers decode
# the path. The decoded path may contain UTF-8 characters, # the path. The decoded path may contain UTF-8 characters,
@@ -142,20 +138,20 @@ class TestReqVars(testing.TestCase):
path=test_path, path=test_path,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.path, falcon.uri.decode(test_path)) assert req.path == falcon.uri.decode(test_path)
def test_uri(self): def test_uri(self):
uri = ('http://' + testing.DEFAULT_HOST + ':8080' + uri = ('http://' + testing.DEFAULT_HOST + ':8080' +
self.app + self.relative_uri) self.app + self.relative_uri)
self.assertEqual(self.req.url, uri) assert self.req.url == uri
# NOTE(kgriffs): Call twice to check caching works # NOTE(kgriffs): Call twice to check caching works
self.assertEqual(self.req.uri, uri) assert self.req.uri == uri
self.assertEqual(self.req.uri, uri) assert self.req.uri == uri
uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path)
self.assertEqual(self.req_noqs.uri, uri_noqs) assert self.req_noqs.uri == uri_noqs
def test_uri_https(self): def test_uri_https(self):
# ======================================================= # =======================================================
@@ -165,7 +161,7 @@ class TestReqVars(testing.TestCase):
path='/hello', scheme='https')) path='/hello', scheme='https'))
uri = ('https://' + testing.DEFAULT_HOST + '/hello') uri = ('https://' + testing.DEFAULT_HOST + '/hello')
self.assertEqual(req.uri, uri) assert req.uri == uri
# ======================================================= # =======================================================
# Default port, explicit # Default port, explicit
@@ -174,7 +170,7 @@ class TestReqVars(testing.TestCase):
path='/hello', scheme='https', port=443)) path='/hello', scheme='https', port=443))
uri = ('https://' + testing.DEFAULT_HOST + '/hello') uri = ('https://' + testing.DEFAULT_HOST + '/hello')
self.assertEqual(req.uri, uri) assert req.uri == uri
# ======================================================= # =======================================================
# Non-default port # Non-default port
@@ -183,7 +179,7 @@ class TestReqVars(testing.TestCase):
path='/hello', scheme='https', port=22)) path='/hello', scheme='https', port=22))
uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') uri = ('https://' + testing.DEFAULT_HOST + ':22/hello')
self.assertEqual(req.uri, uri) assert req.uri == uri
def test_uri_http_1_0(self): def test_uri_http_1_0(self):
# ======================================================= # =======================================================
@@ -200,7 +196,7 @@ class TestReqVars(testing.TestCase):
uri = ('http://' + testing.DEFAULT_HOST + uri = ('http://' + testing.DEFAULT_HOST +
self.app + self.relative_uri) self.app + self.relative_uri)
self.assertEqual(req.uri, uri) assert req.uri == uri
# ======================================================= # =======================================================
# HTTP, 80 # HTTP, 80
@@ -216,7 +212,7 @@ class TestReqVars(testing.TestCase):
uri = ('http://' + testing.DEFAULT_HOST + ':8080' + uri = ('http://' + testing.DEFAULT_HOST + ':8080' +
self.app + self.relative_uri) self.app + self.relative_uri)
self.assertEqual(req.uri, uri) assert req.uri == uri
# ======================================================= # =======================================================
# HTTP, 80 # HTTP, 80
@@ -233,7 +229,7 @@ class TestReqVars(testing.TestCase):
uri = ('https://' + testing.DEFAULT_HOST + uri = ('https://' + testing.DEFAULT_HOST +
self.app + self.relative_uri) self.app + self.relative_uri)
self.assertEqual(req.uri, uri) assert req.uri == uri
# ======================================================= # =======================================================
# HTTP, 80 # HTTP, 80
@@ -250,19 +246,18 @@ class TestReqVars(testing.TestCase):
uri = ('https://' + testing.DEFAULT_HOST + ':22' + uri = ('https://' + testing.DEFAULT_HOST + ':22' +
self.app + self.relative_uri) self.app + self.relative_uri)
self.assertEqual(req.uri, uri) assert req.uri == uri
def test_relative_uri(self): def test_relative_uri(self):
self.assertEqual(self.req.relative_uri, self.app + self.relative_uri) assert self.req.relative_uri == self.app + self.relative_uri
self.assertEqual( assert self.req_noqs.relative_uri == self.app + self.path
self.req_noqs.relative_uri, self.app + self.path)
req_noapp = Request(testing.create_environ( req_noapp = Request(testing.create_environ(
path='/hello', path='/hello',
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req_noapp.relative_uri, self.relative_uri) assert req_noapp.relative_uri == self.relative_uri
req_noapp = Request(testing.create_environ( req_noapp = Request(testing.create_environ(
path='/hello/', path='/hello/',
@@ -270,8 +265,8 @@ class TestReqVars(testing.TestCase):
headers=self.headers)) headers=self.headers))
# NOTE(kgriffs): Call twice to check caching works # NOTE(kgriffs): Call twice to check caching works
self.assertEqual(req_noapp.relative_uri, self.relative_uri) assert req_noapp.relative_uri == self.relative_uri
self.assertEqual(req_noapp.relative_uri, self.relative_uri) assert req_noapp.relative_uri == self.relative_uri
options = RequestOptions() options = RequestOptions()
options.strip_url_path_trailing_slash = False options.strip_url_path_trailing_slash = False
@@ -281,192 +276,195 @@ class TestReqVars(testing.TestCase):
headers=self.headers), headers=self.headers),
options=options) options=options)
self.assertEqual(req_noapp.relative_uri, '/hello/' + '?' + self.qs) assert req_noapp.relative_uri == '/hello/' + '?' + self.qs
def test_client_accepts(self): def test_client_accepts(self):
headers = {'Accept': 'application/xml'} headers = {'Accept': 'application/xml'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
headers = {'Accept': '*/*'} headers = {'Accept': '*/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
self.assertTrue(req.client_accepts('application/json')) assert req.client_accepts('application/json')
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
headers = {'Accept': 'application/x-msgpack'} headers = {'Accept': 'application/x-msgpack'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts('application/xml')) assert not req.client_accepts('application/xml')
self.assertFalse(req.client_accepts('application/json')) assert not req.client_accepts('application/json')
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
headers = {'Accept': 'application/json'} headers = {'Accept': 'application/json'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts('application/xml')) assert not req.client_accepts('application/xml')
headers = {'Accept': 'application/x-msgpack'} headers = {'Accept': 'application/x-msgpack'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
headers = {'Accept': 'application/xm'} headers = {'Accept': 'application/xm'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts('application/xml')) assert not req.client_accepts('application/xml')
headers = {'Accept': 'application/*'} headers = {'Accept': 'application/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('application/json')) assert req.client_accepts('application/json')
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
headers = {'Accept': 'text/*'} headers = {'Accept': 'text/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('text/plain')) assert req.client_accepts('text/plain')
self.assertTrue(req.client_accepts('text/csv')) assert req.client_accepts('text/csv')
self.assertFalse(req.client_accepts('application/xhtml+xml')) assert not req.client_accepts('application/xhtml+xml')
headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('text/plain')) assert req.client_accepts('text/plain')
self.assertTrue(req.client_accepts('text/csv')) assert req.client_accepts('text/csv')
self.assertFalse(req.client_accepts('application/xhtml+xml')) assert not req.client_accepts('application/xhtml+xml')
headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('text/plain')) assert req.client_accepts('text/plain')
self.assertTrue(req.client_accepts('application/xhtml+xml')) assert req.client_accepts('application/xhtml+xml')
headers = {'Accept': 'text/*, application/*'} headers = {'Accept': 'text/*, application/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('text/plain')) assert req.client_accepts('text/plain')
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
self.assertTrue(req.client_accepts('application/json')) assert req.client_accepts('application/json')
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
headers = {'Accept': 'text/*,application/*'} headers = {'Accept': 'text/*,application/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts('text/plain')) assert req.client_accepts('text/plain')
self.assertTrue(req.client_accepts('application/xml')) assert req.client_accepts('application/xml')
self.assertTrue(req.client_accepts('application/json')) assert req.client_accepts('application/json')
self.assertTrue(req.client_accepts('application/x-msgpack')) assert req.client_accepts('application/x-msgpack')
def test_client_accepts_bogus(self): def test_client_accepts_bogus(self):
headers = {'Accept': '~'} headers = {'Accept': '~'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts('text/plain')) assert not req.client_accepts('text/plain')
self.assertFalse(req.client_accepts('application/json')) assert not req.client_accepts('application/json')
def test_client_accepts_props(self): def test_client_accepts_props(self):
headers = {'Accept': 'application/xml'} headers = {'Accept': 'application/xml'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts_xml) assert req.client_accepts_xml
self.assertFalse(req.client_accepts_json) assert not req.client_accepts_json
self.assertFalse(req.client_accepts_msgpack) assert not req.client_accepts_msgpack
headers = {'Accept': 'application/*'} headers = {'Accept': 'application/*'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts_xml) assert req.client_accepts_xml
self.assertTrue(req.client_accepts_json) assert req.client_accepts_json
self.assertTrue(req.client_accepts_msgpack) assert req.client_accepts_msgpack
headers = {'Accept': 'application/json'} headers = {'Accept': 'application/json'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts_xml) assert not req.client_accepts_xml
self.assertTrue(req.client_accepts_json) assert req.client_accepts_json
self.assertFalse(req.client_accepts_msgpack) assert not req.client_accepts_msgpack
headers = {'Accept': 'application/x-msgpack'} headers = {'Accept': 'application/x-msgpack'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts_xml) assert not req.client_accepts_xml
self.assertFalse(req.client_accepts_json) assert not req.client_accepts_json
self.assertTrue(req.client_accepts_msgpack) assert req.client_accepts_msgpack
headers = {'Accept': 'application/msgpack'} headers = {'Accept': 'application/msgpack'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertFalse(req.client_accepts_xml) assert not req.client_accepts_xml
self.assertFalse(req.client_accepts_json) assert not req.client_accepts_json
self.assertTrue(req.client_accepts_msgpack) assert req.client_accepts_msgpack
headers = { headers = {
'Accept': 'application/json,application/xml,application/x-msgpack' 'Accept': 'application/json,application/xml,application/x-msgpack'
} }
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertTrue(req.client_accepts_xml) assert req.client_accepts_xml
self.assertTrue(req.client_accepts_json) assert req.client_accepts_json
self.assertTrue(req.client_accepts_msgpack) assert req.client_accepts_msgpack
def test_client_prefers(self): def test_client_prefers(self):
headers = {'Accept': 'application/xml'} headers = {'Accept': 'application/xml'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
preferred_type = req.client_prefers(['application/xml']) preferred_type = req.client_prefers(['application/xml'])
self.assertEqual(preferred_type, 'application/xml') assert preferred_type == 'application/xml'
headers = {'Accept': '*/*'} headers = {'Accept': '*/*'}
preferred_type = req.client_prefers(('application/xml', preferred_type = req.client_prefers(('application/xml',
'application/json')) 'application/json'))
# NOTE(kgriffs): If client doesn't care, "prefer" the first one # NOTE(kgriffs): If client doesn't care, "prefer" the first one
self.assertEqual(preferred_type, 'application/xml') assert preferred_type == 'application/xml'
headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
preferred_type = req.client_prefers(['application/xhtml+xml']) preferred_type = req.client_prefers(['application/xhtml+xml'])
self.assertEqual(preferred_type, 'application/xhtml+xml') assert preferred_type == 'application/xhtml+xml'
headers = {'Accept': '3p12845j;;;asfd;'} headers = {'Accept': '3p12845j;;;asfd;'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
preferred_type = req.client_prefers(['application/xhtml+xml']) preferred_type = req.client_prefers(['application/xhtml+xml'])
self.assertEqual(preferred_type, None) assert preferred_type is None
def test_range(self): def test_range(self):
headers = {'Range': 'bytes=10-'} headers = {'Range': 'bytes=10-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (10, -1)) assert req.range == (10, -1)
headers = {'Range': 'bytes=10-20'} headers = {'Range': 'bytes=10-20'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (10, 20)) assert req.range == (10, 20)
headers = {'Range': 'bytes=-10240'} headers = {'Range': 'bytes=-10240'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (-10240, -1)) assert req.range == (-10240, -1)
headers = {'Range': 'bytes=0-2'} headers = {'Range': 'bytes=0-2'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (0, 2)) assert req.range == (0, 2)
headers = {'Range': ''} headers = {'Range': ''}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPInvalidHeader, lambda: req.range) with pytest.raises(falcon.HTTPInvalidHeader):
req.range
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertIs(req.range, None) assert req.range is None
def test_range_unit(self): def test_range_unit(self):
headers = {'Range': 'bytes=10-'} headers = {'Range': 'bytes=10-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (10, -1)) assert req.range == (10, -1)
self.assertEqual(req.range_unit, 'bytes') assert req.range_unit == 'bytes'
headers = {'Range': 'items=10-'} headers = {'Range': 'items=10-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.range, (10, -1)) assert req.range == (10, -1)
self.assertEqual(req.range_unit, 'items') assert req.range_unit == 'items'
headers = {'Range': ''} headers = {'Range': ''}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPInvalidHeader, lambda: req.range_unit) with pytest.raises(falcon.HTTPInvalidHeader):
req.range_unit
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertIs(req.range_unit, None) assert req.range_unit is None
def test_range_invalid(self): def test_range_invalid(self):
headers = {'Range': 'bytes=10240'} headers = {'Range': 'bytes=10240'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=-'} headers = {'Range': 'bytes=-'}
expected_desc = ('The value provided for the Range header is ' expected_desc = ('The value provided for the Range header is '
@@ -477,47 +475,58 @@ class TestReqVars(testing.TestCase):
headers = {'Range': 'bytes=--'} headers = {'Range': 'bytes=--'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=-3-'} headers = {'Range': 'bytes=-3-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=-3-4'} headers = {'Range': 'bytes=-3-4'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=3-3-4'} headers = {'Range': 'bytes=3-3-4'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=3-3-'} headers = {'Range': 'bytes=3-3-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=3-3- '} headers = {'Range': 'bytes=3-3- '}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=fizbit'} headers = {'Range': 'bytes=fizbit'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=a-'} headers = {'Range': 'bytes=a-'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=a-3'} headers = {'Range': 'bytes=a-3'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=-b'} headers = {'Range': 'bytes=-b'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=3-b'} headers = {'Range': 'bytes=3-b'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) with pytest.raises(falcon.HTTPBadRequest):
req.range
headers = {'Range': 'bytes=x-y'} headers = {'Range': 'bytes=x-y'}
expected_desc = ('The value provided for the Range header is ' expected_desc = ('The value provided for the Range header is '
@@ -545,19 +554,19 @@ class TestReqVars(testing.TestCase):
def test_missing_attribute_header(self): def test_missing_attribute_header(self):
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertEqual(req.range, None) assert req.range is None
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertEqual(req.content_length, None) assert req.content_length is None
def test_content_length(self): def test_content_length(self):
headers = {'content-length': '5656'} headers = {'content-length': '5656'}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.content_length, 5656) assert req.content_length == 5656
headers = {'content-length': ''} headers = {'content-length': ''}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(req.content_length, None) assert req.content_length is None
def test_bogus_content_length_nan(self): def test_bogus_content_length_nan(self):
headers = {'content-length': 'fuzzy-bunnies'} headers = {'content-length': 'fuzzy-bunnies'}
@@ -577,22 +586,22 @@ class TestReqVars(testing.TestCase):
falcon.HTTPInvalidHeader, falcon.HTTPInvalidHeader,
'Invalid header value', expected_desc) 'Invalid header value', expected_desc)
@ddt.data(('Date', 'date'), @pytest.mark.parametrize('header,attr', [
('If-Modified-since', 'if_modified_since'), ('Date', 'date'),
('If-Unmodified-since', 'if_unmodified_since'), ('If-Modified-Since', 'if_modified_since'),
) ('If-Unmodified-Since', 'if_unmodified_since'),
@ddt.unpack ])
def test_date(self, header, attr): def test_date(self, header, attr):
date = datetime.datetime(2013, 4, 4, 5, 19, 18) date = datetime.datetime(2013, 4, 4, 5, 19, 18)
date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' date_str = 'Thu, 04 Apr 2013 05:19:18 GMT'
self._test_header_expected_value(header, date_str, attr, date) self._test_header_expected_value(header, date_str, attr, date)
@ddt.data(('Date', 'date'), @pytest.mark.parametrize('header,attr', [
('If-Modified-Since', 'if_modified_since'), ('Date', 'date'),
('If-Unmodified-Since', 'if_unmodified_since'), ('If-Modified-Since', 'if_modified_since'),
) ('If-Unmodified-Since', 'if_unmodified_since'),
@ddt.unpack ])
def test_date_invalid(self, header, attr): def test_date_invalid(self, header, attr):
# Date formats don't conform to RFC 1123 # Date formats don't conform to RFC 1123
@@ -612,10 +621,10 @@ class TestReqVars(testing.TestCase):
'Invalid header value', 'Invalid header value',
expected_desc.format(header)) expected_desc.format(header))
@ddt.data('date', 'if_modified_since', 'if_unmodified_since') @pytest.mark.parametrize('attr', ('date', 'if_modified_since', 'if_unmodified_since'))
def test_date_missing(self, attr): def test_date_missing(self, attr):
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertIs(getattr(req, attr), None) assert getattr(req, attr) is None
def test_attribute_headers(self): def test_attribute_headers(self):
hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' hash = 'fa0d1a60ef6616bb28038515c8ea4cb2'
@@ -642,24 +651,24 @@ class TestReqVars(testing.TestCase):
self._test_attribute_header('Referer', referer, 'referer') self._test_attribute_header('Referer', referer, 'referer')
def test_method(self): def test_method(self):
self.assertEqual(self.req.method, 'GET') assert self.req.method == 'GET'
self.req = Request(testing.create_environ(path='', method='HEAD')) self.req = Request(testing.create_environ(path='', method='HEAD'))
self.assertEqual(self.req.method, 'HEAD') assert self.req.method == 'HEAD'
def test_empty_path(self): def test_empty_path(self):
self.req = Request(testing.create_environ(path='')) self.req = Request(testing.create_environ(path=''))
self.assertEqual(self.req.path, '/') assert self.req.path == '/'
def test_content_type_method(self): def test_content_type_method(self):
self.assertEqual(self.req.get_header('content-type'), 'text/plain') assert self.req.get_header('content-type') == 'text/plain'
def test_content_length_method(self): def test_content_length_method(self):
self.assertEqual(self.req.get_header('content-length'), '4829') assert self.req.get_header('content-length') == '4829'
# TODO(kgriffs): Migrate to pytest and parametrized fixtures # TODO(kgriffs): Migrate to pytest and parametrized fixtures
# to DRY things up a bit. # to DRY things up a bit.
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_port_explicit(self, protocol): def test_port_explicit(self, protocol):
port = 9000 port = 9000
req = Request(testing.create_environ( req = Request(testing.create_environ(
@@ -670,9 +679,9 @@ class TestReqVars(testing.TestCase):
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.port, port) assert req.port == port
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_scheme_https(self, protocol): def test_scheme_https(self, protocol):
scheme = 'https' scheme = 'https'
req = Request(testing.create_environ( req = Request(testing.create_environ(
@@ -683,10 +692,10 @@ class TestReqVars(testing.TestCase):
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.scheme, scheme) assert req.scheme == scheme
self.assertEqual(req.port, 443) assert req.port == 443
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_scheme_http(self, protocol): def test_scheme_http(self, protocol):
scheme = 'http' scheme = 'http'
req = Request(testing.create_environ( req = Request(testing.create_environ(
@@ -697,10 +706,10 @@ class TestReqVars(testing.TestCase):
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.scheme, scheme) assert req.scheme == scheme
self.assertEqual(req.port, 80) assert req.port == 80
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_netloc_default_port(self, protocol): def test_netloc_default_port(self, protocol):
req = Request(testing.create_environ( req = Request(testing.create_environ(
protocol=protocol, protocol=protocol,
@@ -709,9 +718,9 @@ class TestReqVars(testing.TestCase):
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.netloc, 'falconframework.org') assert req.netloc == 'falconframework.org'
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_netloc_nondefault_port(self, protocol): def test_netloc_nondefault_port(self, protocol):
req = Request(testing.create_environ( req = Request(testing.create_environ(
protocol=protocol, protocol=protocol,
@@ -721,9 +730,9 @@ class TestReqVars(testing.TestCase):
query_string=self.qs, query_string=self.qs,
headers=self.headers)) headers=self.headers))
self.assertEqual(req.netloc, 'falconframework.org:8080') assert req.netloc == 'falconframework.org:8080'
@ddt.data(*_PROTOCOLS) @pytest.mark.parametrize('protocol', _PROTOCOLS)
def test_netloc_from_env(self, protocol): def test_netloc_from_env(self, protocol):
port = 9000 port = 9000
host = 'example.org' host = 'example.org'
@@ -738,8 +747,8 @@ class TestReqVars(testing.TestCase):
req = Request(env) req = Request(env)
self.assertEqual(req.port, port) assert req.port == port
self.assertEqual(req.netloc, '{0}:{1}'.format(host, port)) assert req.netloc == '{0}:{1}'.format(host, port)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Helpers # Helpers
@@ -748,15 +757,15 @@ class TestReqVars(testing.TestCase):
def _test_attribute_header(self, name, value, attr, default=None): def _test_attribute_header(self, name, value, attr, default=None):
headers = {name: value} headers = {name: value}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(getattr(req, attr), value) assert getattr(req, attr) == value
req = Request(testing.create_environ()) req = Request(testing.create_environ())
self.assertEqual(getattr(req, attr), default) assert getattr(req, attr) == default
def _test_header_expected_value(self, name, value, attr, expected_value): def _test_header_expected_value(self, name, value, attr, expected_value):
headers = {name: value} headers = {name: value}
req = Request(testing.create_environ(headers=headers)) req = Request(testing.create_environ(headers=headers))
self.assertEqual(getattr(req, attr), expected_value) assert getattr(req, attr) == expected_value
def _test_error_details(self, headers, attr_name, def _test_error_details(self, headers, attr_name,
error_type, title, description): error_type, title, description):
@@ -764,7 +773,7 @@ class TestReqVars(testing.TestCase):
try: try:
getattr(req, attr_name) getattr(req, attr_name)
self.fail('{0} not raised'.format(error_type.__name__)) pytest.fail('{0} not raised'.format(error_type.__name__))
except error_type as ex: except error_type as ex:
self.assertEqual(ex.title, title) assert ex.title == title
self.assertEqual(ex.description, description) assert ex.description == description

View File

@@ -1,68 +1,88 @@
import io import io
from wsgiref.validate import InputWrapper
import pytest
import falcon import falcon
from falcon import request_helpers from falcon import request_helpers
import falcon.request
import falcon.testing as testing import falcon.testing as testing
SIZE_1_KB = 1024 SIZE_1_KB = 1024
class TestRequestBody(testing.TestBase): @pytest.fixture
def resource():
return testing.TestResource()
def before(self):
self.resource = testing.TestResource()
self.api.add_route('/', self.resource)
def test_empty_body(self): @pytest.fixture
self.simulate_request('/', body='') def client():
stream = self.resource.req.stream app = falcon.API()
return testing.TestClient(app)
stream.seek(0, 2)
self.assertEqual(stream.tell(), 0)
def test_tiny_body(self): class TestRequestBody(object):
def _get_wrapped_stream(self, req):
# Getting wrapped wsgi.input:
stream = req.stream
if isinstance(stream, request_helpers.BoundedStream):
stream = stream.stream
if isinstance(stream, InputWrapper):
stream = stream.input
if isinstance(stream, io.BytesIO):
return stream
def test_empty_body(self, client, resource):
client.app.add_route('/', resource)
client.simulate_request(path='/', body='')
stream = self._get_wrapped_stream(resource.req)
assert stream.tell() == 0
def test_tiny_body(self, client, resource):
client.app.add_route('/', resource)
expected_body = '.' expected_body = '.'
self.simulate_request('', body=expected_body) client.simulate_request(path='/', body=expected_body)
stream = self.resource.req.stream stream = self._get_wrapped_stream(resource.req)
actual_body = stream.read(1) actual_body = stream.read(1)
self.assertEqual(actual_body, expected_body.encode('utf-8')) assert actual_body == expected_body.encode('utf-8')
stream.seek(0, 2) assert stream.tell() == 1
self.assertEqual(stream.tell(), 1)
def test_tiny_body_overflow(self): def test_tiny_body_overflow(self, client, resource):
client.app.add_route('/', resource)
expected_body = '.' expected_body = '.'
self.simulate_request('', body=expected_body) client.simulate_request(path='/', body=expected_body)
stream = self.resource.req.stream stream = self._get_wrapped_stream(resource.req)
# Read too many bytes; shouldn't block # Read too many bytes; shouldn't block
actual_body = stream.read(len(expected_body) + 1) actual_body = stream.read(len(expected_body) + 1)
self.assertEqual(actual_body, expected_body.encode('utf-8')) assert actual_body == expected_body.encode('utf-8')
def test_read_body(self): def test_read_body(self, client, resource):
client.app.add_route('/', resource)
expected_body = testing.rand_string(SIZE_1_KB / 2, SIZE_1_KB) expected_body = testing.rand_string(SIZE_1_KB / 2, SIZE_1_KB)
expected_len = len(expected_body) expected_len = len(expected_body)
headers = {'Content-Length': str(expected_len)} headers = {'Content-Length': str(expected_len)}
self.simulate_request('', body=expected_body, headers=headers) client.simulate_request(path='/', body=expected_body, headers=headers)
content_len = self.resource.req.get_header('content-length') content_len = resource.req.get_header('content-length')
self.assertEqual(content_len, str(expected_len)) assert content_len == str(expected_len)
stream = self.resource.req.stream stream = self._get_wrapped_stream(resource.req)
actual_body = stream.read() actual_body = stream.read()
self.assertEqual(actual_body, expected_body.encode('utf-8')) assert actual_body == expected_body.encode('utf-8')
stream.seek(0, 2) stream.seek(0, 2)
self.assertEqual(stream.tell(), expected_len) assert stream.tell() == expected_len
self.assertEqual(stream.tell(), expected_len) assert stream.tell() == expected_len
def test_bounded_stream_property_empty_body(self): def test_bounded_stream_property_empty_body(self):
"""Test that we can get a bounded stream outside of wsgiref.""" """Test that we can get a bounded stream outside of wsgiref."""
environ = testing.create_environ() environ = testing.create_environ()
req = falcon.Request(environ) req = falcon.Request(environ)
@@ -73,7 +93,7 @@ class TestRequestBody(testing.TestBase):
# coverage of the property implementation. # coverage of the property implementation.
assert bounded_stream is req.bounded_stream assert bounded_stream is req.bounded_stream
data = bounded_stream.read() data = bounded_stream.read()
self.assertEqual(len(data), 0) assert len(data) == 0
def test_body_stream_wrapper(self): def test_body_stream_wrapper(self):
data = testing.rand_string(SIZE_1_KB / 2, SIZE_1_KB) data = testing.rand_string(SIZE_1_KB / 2, SIZE_1_KB)
@@ -91,15 +111,15 @@ class TestRequestBody(testing.TestBase):
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.read(), expected_body) assert body.read() == expected_body
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.read(2), expected_body[0:2]) assert body.read(2) == expected_body[0:2]
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.read(expected_len + 1), expected_body) assert body.read(expected_len + 1) == expected_body
# NOTE(kgriffs): Test that reading past the end does not # NOTE(kgriffs): Test that reading past the end does not
# hang, but returns the empty string. # hang, but returns the empty string.
@@ -107,43 +127,43 @@ class TestRequestBody(testing.TestBase):
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
for i in range(expected_len + 1): for i in range(expected_len + 1):
expected_value = expected_body[i:i + 1] if i < expected_len else b'' expected_value = expected_body[i:i + 1] if i < expected_len else b''
self.assertEqual(body.read(1), expected_value) assert body.read(1) == expected_value
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(), expected_lines[0]) assert body.readline() == expected_lines[0]
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(-1), expected_lines[0]) assert body.readline(-1) == expected_lines[0]
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readline(expected_len + 1), expected_lines[0]) assert body.readline(expected_len + 1) == expected_lines[0]
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(), expected_lines) assert body.readlines() == expected_lines
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(-1), expected_lines) assert body.readlines(-1) == expected_lines
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(expected_len + 1), expected_lines) assert body.readlines(expected_len + 1) == expected_lines
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
self.assertEqual(next(body), expected_lines[0]) assert next(body) == expected_lines[0]
stream = io.BytesIO(expected_body) stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len) body = request_helpers.Body(stream, expected_len)
for i, line in enumerate(body): for i, line in enumerate(body):
self.assertEqual(line, expected_lines[i]) assert line == expected_lines[i]
def test_request_repr(self): def test_request_repr(self):
environ = testing.create_environ() environ = testing.create_environ()
req = falcon.Request(environ) req = falcon.Request(environ)
_repr = '<%s: %s %r>' % (req.__class__.__name__, req.method, req.url) _repr = '<%s: %s %r>' % (req.__class__.__name__, req.method, req.url)
self.assertEquals(req.__repr__(), _repr) assert req.__repr__() == _repr

View File

@@ -1,13 +1,15 @@
import pytest
from falcon.request import Request from falcon.request import Request
import falcon.testing as testing import falcon.testing as testing
class TestRequestContext(testing.TestBase): class TestRequestContext(object):
def test_default_request_context(self): def test_default_request_context(self):
env = testing.create_environ() env = testing.create_environ()
req = Request(env) req = Request(env)
self.assertIsInstance(req.context, dict) assert isinstance(req.context, dict)
def test_custom_request_context(self): def test_custom_request_context(self):
@@ -20,7 +22,7 @@ class TestRequestContext(testing.TestBase):
env = testing.create_environ() env = testing.create_environ()
req = MyCustomRequest(env) req = MyCustomRequest(env)
self.assertIsInstance(req.context, MyCustomContextType) assert isinstance(req.context, MyCustomContextType)
def test_custom_request_context_failure(self): def test_custom_request_context_failure(self):
@@ -29,7 +31,8 @@ class TestRequestContext(testing.TestBase):
context_type = False context_type = False
env = testing.create_environ() env = testing.create_environ()
self.assertRaises(TypeError, MyCustomRequest, env) with pytest.raises(TypeError):
MyCustomRequest(env)
def test_custom_request_context_request_access(self): def test_custom_request_context_request_access(self):
@@ -42,5 +45,5 @@ class TestRequestContext(testing.TestBase):
env = testing.create_environ() env = testing.create_environ()
req = MyCustomRequest(env) req = MyCustomRequest(env)
self.assertIsInstance(req.context, dict) assert isinstance(req.context, dict)
self.assertEqual(req.context['uri'], req.uri) assert req.context['uri'] == req.uri

View File

@@ -1,9 +1,8 @@
import falcon import falcon
import falcon.testing as testing
class TestResponseBody(testing.TestBase): class TestResponseBody(object):
def test_append_body(self): def test_append_body(self):
text = 'Hello beautiful world! ' text = 'Hello beautiful world! '
@@ -14,9 +13,9 @@ class TestResponseBody(testing.TestBase):
resp.body += token resp.body += token
resp.body += ' ' resp.body += ' '
self.assertEqual(resp.body, text) assert resp.body == text
def test_response_repr(self): def test_response_repr(self):
resp = falcon.Response() resp = falcon.Response()
_repr = '<%s: %s>' % (resp.__class__.__name__, resp.status) _repr = '<%s: %s>' % (resp.__class__.__name__, resp.status)
self.assertEqual(resp.__repr__(), _repr) assert resp.__repr__() == _repr

View File

@@ -1,12 +1,13 @@
import pytest
from falcon import Response from falcon import Response
import falcon.testing as testing
class TestRequestContext(testing.TestBase): class TestRequestContext(object):
def test_default_response_context(self): def test_default_response_context(self):
resp = Response() resp = Response()
self.assertIsInstance(resp.context, dict) assert isinstance(resp.context, dict)
def test_custom_response_context(self): def test_custom_response_context(self):
@@ -17,14 +18,15 @@ class TestRequestContext(testing.TestBase):
context_type = MyCustomContextType context_type = MyCustomContextType
resp = MyCustomResponse() resp = MyCustomResponse()
self.assertIsInstance(resp.context, MyCustomContextType) assert isinstance(resp.context, MyCustomContextType)
def test_custom_response_context_failure(self): def test_custom_response_context_failure(self):
class MyCustomResponse(Response): class MyCustomResponse(Response):
context_type = False context_type = False
self.assertRaises(TypeError, MyCustomResponse) with pytest.raises(TypeError):
MyCustomResponse()
def test_custom_response_context_factory(self): def test_custom_response_context_factory(self):
@@ -35,5 +37,5 @@ class TestRequestContext(testing.TestBase):
context_type = create_context context_type = create_context
resp = MyCustomResponse() resp = MyCustomResponse()
self.assertIsInstance(resp.context, dict) assert isinstance(resp.context, dict)
self.assertIs(resp.context['resp'], resp) assert resp.context['resp'] is resp

View File

@@ -1,5 +1,7 @@
import re import re
import pytest
import falcon import falcon
import falcon.testing as testing import falcon.testing as testing
@@ -27,90 +29,102 @@ class BookCollection(testing.TestResource):
pass pass
class TestDefaultRouting(testing.TestBase): @pytest.fixture
def resource():
return BookCollection()
def before(self):
self.sink = Sink()
self.resource = BookCollection()
def test_single_default_pattern(self): @pytest.fixture
self.api.add_sink(self.sink) def sink():
return Sink()
self.simulate_request('/')
self.assertEqual(self.srmock.status, falcon.HTTP_503)
def test_single_simple_pattern(self): @pytest.fixture
self.api.add_sink(self.sink, r'/foo') def client():
app = falcon.API()
return testing.TestClient(app)
self.simulate_request('/foo/bar')
self.assertEqual(self.srmock.status, falcon.HTTP_503)
def test_single_compiled_pattern(self): class TestDefaultRouting(object):
self.api.add_sink(self.sink, re.compile(r'/foo'))
self.simulate_request('/foo/bar') def test_single_default_pattern(self, client, sink, resource):
self.assertEqual(self.srmock.status, falcon.HTTP_503) client.app.add_sink(sink)
self.simulate_request('/auth') response = client.simulate_request(path='/')
self.assertEqual(self.srmock.status, falcon.HTTP_404) assert response.status == falcon.HTTP_503
def test_named_groups(self): def test_single_simple_pattern(self, client, sink, resource):
self.api.add_sink(self.sink, r'/user/(?P<id>\d+)') client.app.add_sink(sink, r'/foo')
self.simulate_request('/user/309') response = client.simulate_request(path='/foo/bar')
self.assertEqual(self.srmock.status, falcon.HTTP_503) assert response.status == falcon.HTTP_503
self.assertEqual(self.sink.kwargs['id'], '309')
self.simulate_request('/user/sally') def test_single_compiled_pattern(self, client, sink, resource):
self.assertEqual(self.srmock.status, falcon.HTTP_404) client.app.add_sink(sink, re.compile(r'/foo'))
def test_multiple_patterns(self): response = client.simulate_request(path='/foo/bar')
self.api.add_sink(self.sink, r'/foo') assert response.status == falcon.HTTP_503
self.api.add_sink(sink_too, r'/foo') # Last duplicate wins
self.api.add_sink(self.sink, r'/katza') response = client.simulate_request(path='/auth')
assert response.status == falcon.HTTP_404
self.simulate_request('/foo/bar') def test_named_groups(self, client, sink, resource):
self.assertEqual(self.srmock.status, falcon.HTTP_781) client.app.add_sink(sink, r'/user/(?P<id>\d+)')
self.simulate_request('/katza') response = client.simulate_request(path='/user/309')
self.assertEqual(self.srmock.status, falcon.HTTP_503) assert response.status == falcon.HTTP_503
assert sink.kwargs['id'] == '309'
def test_with_route(self): response = client.simulate_request(path='/user/sally')
self.api.add_route('/books', self.resource) assert response.status == falcon.HTTP_404
self.api.add_sink(self.sink, '/proxy')
self.simulate_request('/proxy/books') def test_multiple_patterns(self, client, sink, resource):
self.assertFalse(self.resource.called) client.app.add_sink(sink, r'/foo')
self.assertEqual(self.srmock.status, falcon.HTTP_503) client.app.add_sink(sink_too, r'/foo') # Last duplicate wins
self.simulate_request('/books') client.app.add_sink(sink, r'/katza')
self.assertTrue(self.resource.called)
self.assertEqual(self.srmock.status, falcon.HTTP_200)
def test_route_precedence(self): response = client.simulate_request(path='/foo/bar')
assert response.status == falcon.HTTP_781
response = client.simulate_request(path='/katza')
assert response.status == falcon.HTTP_503
def test_with_route(self, client, sink, resource):
client.app.add_route('/books', resource)
client.app.add_sink(sink, '/proxy')
response = client.simulate_request(path='/proxy/books')
assert not resource.called
assert response.status == falcon.HTTP_503
response = client.simulate_request(path='/books')
assert resource.called
assert response.status == falcon.HTTP_200
def test_route_precedence(self, client, sink, resource):
# NOTE(kgriffs): In case of collision, the route takes precedence. # NOTE(kgriffs): In case of collision, the route takes precedence.
self.api.add_route('/books', self.resource) client.app.add_route('/books', resource)
self.api.add_sink(self.sink, '/books') client.app.add_sink(sink, '/books')
self.simulate_request('/books') response = client.simulate_request(path='/books')
self.assertTrue(self.resource.called) assert resource.called
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200
def test_route_precedence_with_id(self): def test_route_precedence_with_id(self, client, sink, resource):
# NOTE(kgriffs): In case of collision, the route takes precedence. # NOTE(kgriffs): In case of collision, the route takes precedence.
self.api.add_route('/books/{id}', self.resource) client.app.add_route('/books/{id}', resource)
self.api.add_sink(self.sink, '/books') client.app.add_sink(sink, '/books')
self.simulate_request('/books') response = client.simulate_request(path='/books')
self.assertFalse(self.resource.called) assert not resource.called
self.assertEqual(self.srmock.status, falcon.HTTP_503) assert response.status == falcon.HTTP_503
def test_route_precedence_with_both_id(self): def test_route_precedence_with_both_id(self, client, sink, resource):
# NOTE(kgriffs): In case of collision, the route takes precedence. # NOTE(kgriffs): In case of collision, the route takes precedence.
self.api.add_route('/books/{id}', self.resource) client.app.add_route('/books/{id}', resource)
self.api.add_sink(self.sink, '/books/\d+') client.app.add_sink(sink, '/books/\d+')
self.simulate_request('/books/123') response = client.simulate_request(path='/books/123')
self.assertTrue(self.resource.called) assert resource.called
self.assertEqual(self.srmock.status, falcon.HTTP_200) assert response.status == falcon.HTTP_200

View File

@@ -1,8 +1,10 @@
import pytest
from falcon import Request, Response from falcon import Request, Response
import falcon.testing as testing import falcon.testing as testing
class TestSlots(testing.TestBase): class TestSlots(object):
def test_slots_request(self): def test_slots_request(self):
env = testing.create_environ() env = testing.create_environ()
@@ -11,7 +13,7 @@ class TestSlots(testing.TestBase):
try: try:
req.doesnt = 'exist' req.doesnt = 'exist'
except AttributeError: except AttributeError:
self.fail('Unable to add additional variables dynamically') pytest.fail('Unable to add additional variables dynamically')
def test_slots_response(self): def test_slots_response(self):
resp = Response() resp = Response()
@@ -19,4 +21,4 @@ class TestSlots(testing.TestBase):
try: try:
resp.doesnt = 'exist' resp.doesnt = 'exist'
except AttributeError: except AttributeError:
self.fail('Unable to add additional variables dynamically') pytest.fail('Unable to add additional variables dynamically')

View File

@@ -1,107 +1,108 @@
import ddt import pytest
import falcon import falcon
from falcon import routing from falcon import routing
import falcon.testing as testing
@ddt.ddt class TestUriTemplates(object):
class TestUriTemplates(testing.TestBase):
@ddt.data(42, falcon.API) @pytest.mark.parametrize('value', (42, falcon.API))
def test_string_type_required(self, value): def test_string_type_required(self, value):
self.assertRaises(TypeError, routing.compile_uri_template, value) with pytest.raises(TypeError):
routing.compile_uri_template(value)
@ddt.data('this', 'this/that') @pytest.mark.parametrize('value', ('this', 'this/that'))
def test_template_must_start_with_slash(self, value): def test_template_must_start_with_slash(self, value):
self.assertRaises(ValueError, routing.compile_uri_template, value) with pytest.raises(ValueError):
routing.compile_uri_template(value)
@ddt.data('//', 'a//', '//b', 'a//b', 'a/b//', 'a/b//c') @pytest.mark.parametrize('value', ('//', 'a//', '//b', 'a//b', 'a/b//', 'a/b//c'))
def test_template_may_not_contain_double_slash(self, value): def test_template_may_not_contain_double_slash(self, value):
self.assertRaises(ValueError, routing.compile_uri_template, value) with pytest.raises(ValueError):
routing.compile_uri_template(value)
def test_root(self): def test_root(self):
fields, pattern = routing.compile_uri_template('/') fields, pattern = routing.compile_uri_template('/')
self.assertFalse(fields) assert not fields
self.assertFalse(pattern.match('/x')) assert not pattern.match('/x')
result = pattern.match('/') result = pattern.match('/')
self.assertTrue(result) assert result
self.assertFalse(result.groupdict()) assert not result.groupdict()
@ddt.data('/hello', '/hello/world', '/hi/there/how/are/you') @pytest.mark.parametrize('path', ('/hello', '/hello/world', '/hi/there/how/are/you'))
def test_no_fields(self, path): def test_no_fields(self, path):
fields, pattern = routing.compile_uri_template(path) fields, pattern = routing.compile_uri_template(path)
self.assertFalse(fields) assert not fields
self.assertFalse(pattern.match(path[:-1])) assert not pattern.match(path[:-1])
result = pattern.match(path) result = pattern.match(path)
self.assertTrue(result) assert result
self.assertFalse(result.groupdict()) assert not result.groupdict()
def test_one_field(self): def test_one_field(self):
fields, pattern = routing.compile_uri_template('/{name}') fields, pattern = routing.compile_uri_template('/{name}')
self.assertEqual(fields, set(['name'])) assert fields == set(['name'])
result = pattern.match('/Kelsier') result = pattern.match('/Kelsier')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) assert result.groupdict() == {'name': 'Kelsier'}
fields, pattern = routing.compile_uri_template('/character/{name}') fields, pattern = routing.compile_uri_template('/character/{name}')
self.assertEqual(fields, set(['name'])) assert fields == set(['name'])
result = pattern.match('/character/Kelsier') result = pattern.match('/character/Kelsier')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) assert result.groupdict() == {'name': 'Kelsier'}
fields, pattern = routing.compile_uri_template('/character/{name}/profile') fields, pattern = routing.compile_uri_template('/character/{name}/profile')
self.assertEqual(fields, set(['name'])) assert fields == set(['name'])
self.assertFalse(pattern.match('/character')) assert not pattern.match('/character')
self.assertFalse(pattern.match('/character/Kelsier')) assert not pattern.match('/character/Kelsier')
self.assertFalse(pattern.match('/character/Kelsier/')) assert not pattern.match('/character/Kelsier/')
result = pattern.match('/character/Kelsier/profile') result = pattern.match('/character/Kelsier/profile')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) assert result.groupdict() == {'name': 'Kelsier'}
def test_one_field_with_digits(self): def test_one_field_with_digits(self):
fields, pattern = routing.compile_uri_template('/{name123}') fields, pattern = routing.compile_uri_template('/{name123}')
self.assertEqual(fields, set(['name123'])) assert fields == set(['name123'])
result = pattern.match('/Kelsier') result = pattern.match('/Kelsier')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'name123': 'Kelsier'}) assert result.groupdict() == {'name123': 'Kelsier'}
def test_one_field_with_prefixed_digits(self): def test_one_field_with_prefixed_digits(self):
fields, pattern = routing.compile_uri_template('/{37signals}') fields, pattern = routing.compile_uri_template('/{37signals}')
self.assertEqual(fields, set()) assert fields == set()
result = pattern.match('/s2n') result = pattern.match('/s2n')
self.assertFalse(result) assert not result
@ddt.data('', '/') @pytest.mark.parametrize('postfix', ('', '/'))
def test_two_fields(self, postfix): def test_two_fields(self, postfix):
path = '/book/{book_id}/characters/{n4m3}' + postfix path = '/book/{book_id}/characters/{n4m3}' + postfix
fields, pattern = routing.compile_uri_template(path) fields, pattern = routing.compile_uri_template(path)
self.assertEqual(fields, set(['n4m3', 'book_id'])) assert fields == set(['n4m3', 'book_id'])
result = pattern.match('/book/0765350386/characters/Vin') result = pattern.match('/book/0765350386/characters/Vin')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'n4m3': 'Vin', 'book_id': '0765350386'}) assert result.groupdict() == {'n4m3': 'Vin', 'book_id': '0765350386'}
def test_three_fields(self): def test_three_fields(self):
fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}') fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}')
self.assertEqual(fields, set('abc')) assert fields == set('abc')
result = pattern.match('/one/2/x/3') result = pattern.match('/one/2/x/3')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'a': 'one', 'b': '2', 'c': '3'}) assert result.groupdict() == {'a': 'one', 'b': '2', 'c': '3'}
def test_malformed_field(self): def test_malformed_field(self):
fields, pattern = routing.compile_uri_template('/{a}/{1b}/x/{c}') fields, pattern = routing.compile_uri_template('/{a}/{1b}/x/{c}')
self.assertEqual(fields, set('ac')) assert fields == set('ac')
result = pattern.match('/one/{1b}/x/3') result = pattern.match('/one/{1b}/x/3')
self.assertTrue(result) assert result
self.assertEqual(result.groupdict(), {'a': 'one', 'c': '3'}) assert result.groupdict() == {'a': 'one', 'c': '3'}

View File

@@ -10,7 +10,6 @@ import random
import pytest import pytest
import six import six
import testtools
import falcon import falcon
from falcon import testing from falcon import testing
@@ -27,10 +26,9 @@ def _arbitrary_uris(count, length):
) )
class TestFalconUtils(testtools.TestCase): class TestFalconUtils(object):
def setUp(self): def setup_method(self, method):
super(TestFalconUtils, self).setUp()
# NOTE(cabrera): for DRYness - used in uri.[de|en]code tests # NOTE(cabrera): for DRYness - used in uri.[de|en]code tests
# below. # below.
self.uris = _arbitrary_uris(count=100, length=32) self.uris = _arbitrary_uris(count=100, length=32)
@@ -55,73 +53,56 @@ class TestFalconUtils(testtools.TestCase):
delta = actual - expected delta = actual - expected
delta_sec = abs(delta.days * 86400 + delta.seconds) delta_sec = abs(delta.days * 86400 + delta.seconds)
self.assertLessEqual(delta_sec, 1) assert delta_sec <= 1
def test_dt_to_http(self): def test_dt_to_http(self):
self.assertEqual( assert falcon.dt_to_http(datetime(2013, 4, 4)) == 'Thu, 04 Apr 2013 00:00:00 GMT'
falcon.dt_to_http(datetime(2013, 4, 4)),
'Thu, 04 Apr 2013 00:00:00 GMT')
self.assertEqual( assert falcon.dt_to_http(
falcon.dt_to_http(datetime(2013, 4, 4, 10, 28, 54)), datetime(2013, 4, 4, 10, 28, 54)
'Thu, 04 Apr 2013 10:28:54 GMT') ) == 'Thu, 04 Apr 2013 10:28:54 GMT'
def test_http_date_to_dt(self): def test_http_date_to_dt(self):
self.assertEqual( assert falcon.http_date_to_dt('Thu, 04 Apr 2013 00:00:00 GMT') == datetime(2013, 4, 4)
falcon.http_date_to_dt('Thu, 04 Apr 2013 00:00:00 GMT'),
datetime(2013, 4, 4))
self.assertEqual( assert falcon.http_date_to_dt(
falcon.http_date_to_dt('Thu, 04 Apr 2013 10:28:54 GMT'), 'Thu, 04 Apr 2013 10:28:54 GMT'
datetime(2013, 4, 4, 10, 28, 54)) ) == datetime(2013, 4, 4, 10, 28, 54)
self.assertRaises( with pytest.raises(ValueError):
ValueError, falcon.http_date_to_dt('Thu, 04-Apr-2013 10:28:54 GMT')
falcon.http_date_to_dt, 'Thu, 04-Apr-2013 10:28:54 GMT')
self.assertEqual( assert falcon.http_date_to_dt(
falcon.http_date_to_dt('Thu, 04-Apr-2013 10:28:54 GMT', 'Thu, 04-Apr-2013 10:28:54 GMT', obs_date=True
obs_date=True), ) == datetime(2013, 4, 4, 10, 28, 54)
datetime(2013, 4, 4, 10, 28, 54))
self.assertRaises( with pytest.raises(ValueError):
ValueError, falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994')
falcon.http_date_to_dt, 'Sun Nov 6 08:49:37 1994')
self.assertRaises( with pytest.raises(ValueError):
ValueError, falcon.http_date_to_dt('Nov 6 08:49:37 1994', obs_date=True)
falcon.http_date_to_dt, 'Nov 6 08:49:37 1994', obs_date=True)
self.assertEqual( assert falcon.http_date_to_dt(
falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994', obs_date=True), 'Sun Nov 6 08:49:37 1994', obs_date=True
datetime(1994, 11, 6, 8, 49, 37)) ) == datetime(1994, 11, 6, 8, 49, 37)
self.assertEqual( assert falcon.http_date_to_dt(
falcon.http_date_to_dt('Sunday, 06-Nov-94 08:49:37 GMT', 'Sunday, 06-Nov-94 08:49:37 GMT', obs_date=True
obs_date=True), ) == datetime(1994, 11, 6, 8, 49, 37)
datetime(1994, 11, 6, 8, 49, 37))
def test_pack_query_params_none(self): def test_pack_query_params_none(self):
self.assertEqual( assert falcon.to_query_str({}) == ''
falcon.to_query_str({}),
'')
def test_pack_query_params_one(self): def test_pack_query_params_one(self):
self.assertEqual( assert falcon.to_query_str({'limit': 10}) == '?limit=10'
falcon.to_query_str({'limit': 10}),
'?limit=10')
self.assertEqual( assert falcon.to_query_str(
falcon.to_query_str({'things': [1, 2, 3]}), {'things': [1, 2, 3]}) == '?things=1,2,3'
'?things=1,2,3')
self.assertEqual( assert falcon.to_query_str({'things': ['a']}) == '?things=a'
falcon.to_query_str({'things': ['a']}),
'?things=a')
self.assertEqual( assert falcon.to_query_str(
falcon.to_query_str({'things': ['a', 'b']}), {'things': ['a', 'b']}) == '?things=a,b'
'?things=a,b')
expected = ('?things=a&things=b&things=&things=None' expected = ('?things=a&things=b&things=&things=None'
'&things=true&things=false&things=0') '&things=true&things=false&things=0')
@@ -131,7 +112,7 @@ class TestFalconUtils(testtools.TestCase):
comma_delimited_lists=False comma_delimited_lists=False
) )
self.assertEqual(actual, expected) assert actual == expected
def test_pack_query_params_several(self): def test_pack_query_params_several(self):
garbage_in = { garbage_in = {
@@ -157,76 +138,73 @@ class TestFalconUtils(testtools.TestCase):
'y': '0.2', 'y': '0.2',
'doit': 'false'} 'doit': 'false'}
self.assertEqual(expected, garbage_out) assert expected == garbage_out
def test_uri_encode(self): def test_uri_encode(self):
url = 'http://example.com/v1/fizbit/messages?limit=3&echo=true' url = 'http://example.com/v1/fizbit/messages?limit=3&echo=true'
self.assertEqual(uri.encode(url), url) assert uri.encode(url) == url
url = 'http://example.com/v1/fiz bit/messages' url = 'http://example.com/v1/fiz bit/messages'
expected = 'http://example.com/v1/fiz%20bit/messages' expected = 'http://example.com/v1/fiz%20bit/messages'
self.assertEqual(uri.encode(url), expected) assert uri.encode(url) == expected
url = u'http://example.com/v1/fizbit/messages?limit=3&e\u00e7ho=true' url = u'http://example.com/v1/fizbit/messages?limit=3&e\u00e7ho=true'
expected = ('http://example.com/v1/fizbit/messages' expected = ('http://example.com/v1/fizbit/messages'
'?limit=3&e%C3%A7ho=true') '?limit=3&e%C3%A7ho=true')
self.assertEqual(uri.encode(url), expected) assert uri.encode(url) == expected
def test_uri_encode_double(self): def test_uri_encode_double(self):
url = 'http://example.com/v1/fiz bit/messages' url = 'http://example.com/v1/fiz bit/messages'
expected = 'http://example.com/v1/fiz%20bit/messages' expected = 'http://example.com/v1/fiz%20bit/messages'
self.assertEqual(uri.encode(uri.encode(url)), expected) assert uri.encode(uri.encode(url)) == expected
url = u'http://example.com/v1/fizbit/messages?limit=3&e\u00e7ho=true' url = u'http://example.com/v1/fizbit/messages?limit=3&e\u00e7ho=true'
expected = ('http://example.com/v1/fizbit/messages' expected = ('http://example.com/v1/fizbit/messages'
'?limit=3&e%C3%A7ho=true') '?limit=3&e%C3%A7ho=true')
self.assertEqual(uri.encode(uri.encode(url)), expected) assert uri.encode(uri.encode(url)) == expected
url = 'http://example.com/v1/fiz%bit/mess%ages/%' url = 'http://example.com/v1/fiz%bit/mess%ages/%'
expected = 'http://example.com/v1/fiz%25bit/mess%25ages/%25' expected = 'http://example.com/v1/fiz%25bit/mess%25ages/%25'
self.assertEqual(uri.encode(uri.encode(url)), expected) assert uri.encode(uri.encode(url)) == expected
url = 'http://example.com/%%' url = 'http://example.com/%%'
expected = 'http://example.com/%25%25' expected = 'http://example.com/%25%25'
self.assertEqual(uri.encode(uri.encode(url)), expected) assert uri.encode(uri.encode(url)) == expected
# NOTE(kgriffs): Specific example cited in GH issue # NOTE(kgriffs): Specific example cited in GH issue
url = 'http://something?redirect_uri=http%3A%2F%2Fsite' url = 'http://something?redirect_uri=http%3A%2F%2Fsite'
self.assertEqual(uri.encode(url), url) assert uri.encode(url) == url
hex_digits = 'abcdefABCDEF0123456789' hex_digits = 'abcdefABCDEF0123456789'
for c1 in hex_digits: for c1 in hex_digits:
for c2 in hex_digits: for c2 in hex_digits:
url = 'http://example.com/%' + c1 + c2 url = 'http://example.com/%' + c1 + c2
encoded = uri.encode(uri.encode(url)) encoded = uri.encode(uri.encode(url))
self.assertEqual(encoded, url) assert encoded == url
def test_uri_encode_value(self): def test_uri_encode_value(self):
self.assertEqual(uri.encode_value('abcd'), 'abcd') assert uri.encode_value('abcd') == 'abcd'
self.assertEqual(uri.encode_value(u'abcd'), u'abcd') assert uri.encode_value(u'abcd') == u'abcd'
self.assertEqual(uri.encode_value(u'ab cd'), u'ab%20cd') assert uri.encode_value(u'ab cd') == u'ab%20cd'
self.assertEqual(uri.encode_value(u'\u00e7'), '%C3%A7') assert uri.encode_value(u'\u00e7') == '%C3%A7'
self.assertEqual(uri.encode_value(u'\u00e7\u20ac'), assert uri.encode_value(u'\u00e7\u20ac') == '%C3%A7%E2%82%AC'
'%C3%A7%E2%82%AC') assert uri.encode_value('ab/cd') == 'ab%2Fcd'
self.assertEqual(uri.encode_value('ab/cd'), 'ab%2Fcd') assert uri.encode_value('ab+cd=42,9') == 'ab%2Bcd%3D42%2C9'
self.assertEqual(uri.encode_value('ab+cd=42,9'),
'ab%2Bcd%3D42%2C9')
def test_uri_decode(self): def test_uri_decode(self):
self.assertEqual(uri.decode('abcd'), 'abcd') assert uri.decode('abcd') == 'abcd'
self.assertEqual(uri.decode(u'abcd'), u'abcd') assert uri.decode(u'abcd') == u'abcd'
self.assertEqual(uri.decode(u'ab%20cd'), u'ab cd') assert uri.decode(u'ab%20cd') == u'ab cd'
self.assertEqual(uri.decode('This thing is %C3%A7'), assert uri.decode('This thing is %C3%A7') == u'This thing is \u00e7'
u'This thing is \u00e7')
self.assertEqual(uri.decode('This thing is %C3%A7%E2%82%AC'), assert uri.decode('This thing is %C3%A7%E2%82%AC') == u'This thing is \u00e7\u20ac'
u'This thing is \u00e7\u20ac')
self.assertEqual(uri.decode('ab%2Fcd'), 'ab/cd') assert uri.decode('ab%2Fcd') == 'ab/cd'
self.assertEqual(uri.decode('http://example.com?x=ab%2Bcd%3D42%2C9'), assert uri.decode(
'http://example.com?x=ab+cd=42,9') 'http://example.com?x=ab%2Bcd%3D42%2C9'
) == 'http://example.com?x=ab+cd=42,9'
def test_prop_uri_encode_models_stdlib_quote(self): def test_prop_uri_encode_models_stdlib_quote(self):
equiv_quote = functools.partial( equiv_quote = functools.partial(
@@ -235,7 +213,7 @@ class TestFalconUtils(testtools.TestCase):
for case in self.uris: for case in self.uris:
expect = equiv_quote(case) expect = equiv_quote(case)
actual = uri.encode(case) actual = uri.encode(case)
self.assertEqual(expect, actual) assert expect == actual
def test_prop_uri_encode_value_models_stdlib_quote_safe_tilde(self): def test_prop_uri_encode_value_models_stdlib_quote_safe_tilde(self):
equiv_quote = functools.partial( equiv_quote = functools.partial(
@@ -244,7 +222,7 @@ class TestFalconUtils(testtools.TestCase):
for case in self.uris: for case in self.uris:
expect = equiv_quote(case) expect = equiv_quote(case)
actual = uri.encode_value(case) actual = uri.encode_value(case)
self.assertEqual(expect, actual) assert expect == actual
def test_prop_uri_decode_models_stdlib_unquote_plus(self): def test_prop_uri_decode_models_stdlib_unquote_plus(self):
stdlib_unquote = six.moves.urllib.parse.unquote_plus stdlib_unquote = six.moves.urllib.parse.unquote_plus
@@ -253,7 +231,7 @@ class TestFalconUtils(testtools.TestCase):
expect = stdlib_unquote(case) expect = stdlib_unquote(case)
actual = uri.decode(case) actual = uri.decode(case)
self.assertEqual(expect, actual) assert expect == actual
def test_parse_query_string(self): def test_parse_query_string(self):
query_strinq = ( query_strinq = (
@@ -270,114 +248,106 @@ class TestFalconUtils(testtools.TestCase):
decoded_json = '{"test1": "data1", "test2": "data2"}' decoded_json = '{"test1": "data1", "test2": "data2"}'
result = uri.parse_query_string(query_strinq) result = uri.parse_query_string(query_strinq)
self.assertEqual(result['a'], decoded_url) assert result['a'] == decoded_url
self.assertEqual(result['b'], decoded_json) assert result['b'] == decoded_json
self.assertEqual(result['c'], ['1', '2', '3']) assert result['c'] == ['1', '2', '3']
self.assertEqual(result['d'], 'test') assert result['d'] == 'test'
self.assertEqual(result['e'], ['a', '&=,']) assert result['e'] == ['a', '&=,']
self.assertEqual(result['f'], ['a', 'a=b']) assert result['f'] == ['a', 'a=b']
self.assertEqual(result[u'é'], 'a=b') assert result[u'é'] == 'a=b'
result = uri.parse_query_string(query_strinq, True) result = uri.parse_query_string(query_strinq, True)
self.assertEqual(result['a'], decoded_url) assert result['a'] == decoded_url
self.assertEqual(result['b'], decoded_json) assert result['b'] == decoded_json
self.assertEqual(result['c'], ['1', '2', '3']) assert result['c'] == ['1', '2', '3']
self.assertEqual(result['d'], 'test') assert result['d'] == 'test'
self.assertEqual(result['e'], ['a', '', '&=,']) assert result['e'] == ['a', '', '&=,']
self.assertEqual(result['f'], ['a', 'a=b']) assert result['f'] == ['a', 'a=b']
self.assertEqual(result[u'é'], 'a=b') assert result[u'é'] == 'a=b'
def test_parse_host(self): def test_parse_host(self):
self.assertEqual(uri.parse_host('::1'), ('::1', None)) assert uri.parse_host('::1') == ('::1', None)
self.assertEqual(uri.parse_host('2001:ODB8:AC10:FE01::'), assert uri.parse_host('2001:ODB8:AC10:FE01::') == ('2001:ODB8:AC10:FE01::', None)
('2001:ODB8:AC10:FE01::', None)) assert uri.parse_host(
self.assertEqual( '2001:ODB8:AC10:FE01::', default_port=80
uri.parse_host('2001:ODB8:AC10:FE01::', default_port=80), ) == ('2001:ODB8:AC10:FE01::', 80)
('2001:ODB8:AC10:FE01::', 80))
ipv6_addr = '2001:4801:1221:101:1c10::f5:116' ipv6_addr = '2001:4801:1221:101:1c10::f5:116'
self.assertEqual(uri.parse_host(ipv6_addr), (ipv6_addr, None)) assert uri.parse_host(ipv6_addr) == (ipv6_addr, None)
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']'), assert uri.parse_host('[' + ipv6_addr + ']') == (ipv6_addr, None)
(ipv6_addr, None)) assert uri.parse_host('[' + ipv6_addr + ']:28080') == (ipv6_addr, 28080)
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:28080'), assert uri.parse_host('[' + ipv6_addr + ']:8080') == (ipv6_addr, 8080)
(ipv6_addr, 28080)) assert uri.parse_host('[' + ipv6_addr + ']:123') == (ipv6_addr, 123)
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:8080'), assert uri.parse_host('[' + ipv6_addr + ']:42') == (ipv6_addr, 42)
(ipv6_addr, 8080))
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:123'),
(ipv6_addr, 123))
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:42'),
(ipv6_addr, 42))
self.assertEqual(uri.parse_host('173.203.44.122'), assert uri.parse_host('173.203.44.122') == ('173.203.44.122', None)
('173.203.44.122', None)) assert uri.parse_host('173.203.44.122', default_port=80) == ('173.203.44.122', 80)
self.assertEqual(uri.parse_host('173.203.44.122', default_port=80), assert uri.parse_host('173.203.44.122:27070') == ('173.203.44.122', 27070)
('173.203.44.122', 80)) assert uri.parse_host('173.203.44.122:123') == ('173.203.44.122', 123)
self.assertEqual(uri.parse_host('173.203.44.122:27070'), assert uri.parse_host('173.203.44.122:42') == ('173.203.44.122', 42)
('173.203.44.122', 27070))
self.assertEqual(uri.parse_host('173.203.44.122:123'),
('173.203.44.122', 123))
self.assertEqual(uri.parse_host('173.203.44.122:42'),
('173.203.44.122', 42))
self.assertEqual(uri.parse_host('example.com'), assert uri.parse_host('example.com') == ('example.com', None)
('example.com', None)) assert uri.parse_host('example.com', default_port=443) == ('example.com', 443)
self.assertEqual(uri.parse_host('example.com', default_port=443), assert uri.parse_host('falcon.example.com') == ('falcon.example.com', None)
('example.com', 443)) assert uri.parse_host('falcon.example.com:9876') == ('falcon.example.com', 9876)
self.assertEqual(uri.parse_host('falcon.example.com'), assert uri.parse_host('falcon.example.com:42') == ('falcon.example.com', 42)
('falcon.example.com', None))
self.assertEqual(uri.parse_host('falcon.example.com:9876'),
('falcon.example.com', 9876))
self.assertEqual(uri.parse_host('falcon.example.com:42'),
('falcon.example.com', 42))
def test_get_http_status(self): def test_get_http_status(self):
self.assertEqual(falcon.get_http_status(404), falcon.HTTP_404) assert falcon.get_http_status(404) == falcon.HTTP_404
self.assertEqual(falcon.get_http_status(404.3), falcon.HTTP_404) assert falcon.get_http_status(404.3) == falcon.HTTP_404
self.assertEqual(falcon.get_http_status('404.3'), falcon.HTTP_404) assert falcon.get_http_status('404.3') == falcon.HTTP_404
self.assertEqual(falcon.get_http_status(404.9), falcon.HTTP_404) assert falcon.get_http_status(404.9) == falcon.HTTP_404
self.assertEqual(falcon.get_http_status('404'), falcon.HTTP_404) assert falcon.get_http_status('404') == falcon.HTTP_404
self.assertEqual(falcon.get_http_status(123), '123 Unknown') assert falcon.get_http_status(123) == '123 Unknown'
self.assertRaises(ValueError, falcon.get_http_status, 'not_a_number') with pytest.raises(ValueError):
self.assertRaises(ValueError, falcon.get_http_status, 0) falcon.get_http_status('not_a_number')
self.assertRaises(ValueError, falcon.get_http_status, 99) with pytest.raises(ValueError):
self.assertRaises(ValueError, falcon.get_http_status, -404.3) falcon.get_http_status(0)
self.assertRaises(ValueError, falcon.get_http_status, '-404') with pytest.raises(ValueError):
self.assertRaises(ValueError, falcon.get_http_status, '-404.3') falcon.get_http_status(0)
self.assertEqual(falcon.get_http_status(123, 'Go Away'), '123 Go Away') with pytest.raises(ValueError):
falcon.get_http_status(99)
with pytest.raises(ValueError):
falcon.get_http_status(-404.3)
with pytest.raises(ValueError):
falcon.get_http_status('-404')
with pytest.raises(ValueError):
falcon.get_http_status('-404.3')
assert falcon.get_http_status(123, 'Go Away') == '123 Go Away'
class TestFalconTesting(testing.TestBase): class TestFalconTesting(object):
"""Catch some uncommon branches not covered elsewhere.""" """Catch some uncommon branches not covered elsewhere."""
def test_path_escape_chars_in_create_environ(self): def test_path_escape_chars_in_create_environ(self):
env = testing.create_environ('/hello%20world%21') env = testing.create_environ('/hello%20world%21')
self.assertEqual(env['PATH_INFO'], '/hello world!') assert env['PATH_INFO'] == '/hello world!'
def test_no_prefix_allowed_for_query_strings_in_create_environ(self): def test_no_prefix_allowed_for_query_strings_in_create_environ(self):
self.assertRaises(ValueError, testing.create_environ, with pytest.raises(ValueError):
query_string='?foo=bar') testing.create_environ(query_string='?foo=bar')
@pytest.mark.skipif(six.PY3, reason='Test does not apply to Py3K')
def test_unicode_path_in_create_environ(self): def test_unicode_path_in_create_environ(self):
if six.PY3:
self.skip('Test does not apply to Py3K')
env = testing.create_environ(u'/fancy/unícode') env = testing.create_environ(u'/fancy/unícode')
self.assertEqual(env['PATH_INFO'], '/fancy/un\xc3\xadcode') assert env['PATH_INFO'] == '/fancy/un\xc3\xadcode'
env = testing.create_environ(u'/simple') env = testing.create_environ(u'/simple')
self.assertEqual(env['PATH_INFO'], '/simple') assert env['PATH_INFO'] == '/simple'
def test_none_header_value_in_create_environ(self): def test_none_header_value_in_create_environ(self):
env = testing.create_environ('/', headers={'X-Foo': None}) env = testing.create_environ('/', headers={'X-Foo': None})
self.assertEqual(env['HTTP_X_FOO'], '') assert env['HTTP_X_FOO'] == ''
def test_decode_empty_result(self): def test_decode_empty_result(self):
body = self.simulate_request('/', decode='utf-8') app = falcon.API()
self.assertEqual(body, '') client = testing.TestClient(app)
response = client.simulate_request(path='/')
assert response.text == ''
def test_httpnow_alias_for_backwards_compat(self): def test_httpnow_alias_for_backwards_compat(self):
self.assertIs(testing.httpnow, util.http_now) assert testing.httpnow is util.http_now
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -408,36 +378,40 @@ def test_simulate_request_protocol(protocol, method):
pass pass
class TestFalconTestCase(testing.TestCase): class TestFalconTestCase(object):
"""Verify some branches not covered elsewhere.""" """Verify some branches not covered elsewhere."""
def test_status(self): def test_status(self):
app = falcon.API()
resource = testing.SimpleTestResource(status=falcon.HTTP_702) resource = testing.SimpleTestResource(status=falcon.HTTP_702)
self.api.add_route('/', resource) app.add_route('/', resource)
client = testing.TestClient(app)
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.status, falcon.HTTP_702) assert result.status == falcon.HTTP_702
def test_wsgi_iterable_not_closeable(self): def test_wsgi_iterable_not_closeable(self):
result = testing.Result([], falcon.HTTP_200, []) result = testing.Result([], falcon.HTTP_200, [])
self.assertFalse(result.content) assert not result.content
def test_path_must_start_with_slash(self): def test_path_must_start_with_slash(self):
self.assertRaises(ValueError, self.simulate_get, 'foo') app = falcon.API()
app.add_route('/', testing.SimpleTestResource())
client = testing.TestClient(app)
with pytest.raises(ValueError):
client.simulate_get('foo')
def test_cached_text_in_result(self): def test_cached_text_in_result(self):
self.api.add_route('/', testing.SimpleTestResource(body='test')) app = falcon.API()
app.add_route('/', testing.SimpleTestResource(body='test'))
client = testing.TestClient(app)
result = self.simulate_get() result = client.simulate_get()
self.assertEqual(result.text, result.text) assert result.text == result.text
def test_simple_resource_body_json_xor(self): def test_simple_resource_body_json_xor(self):
self.assertRaises( with pytest.raises(ValueError):
ValueError, testing.SimpleTestResource(body='', json={})
testing.SimpleTestResource,
body='',
json={},
)
def test_query_string(self): def test_query_string(self):
class SomeResource(object): class SomeResource(object):
@@ -451,47 +425,71 @@ class TestFalconTestCase(testing.TestCase):
resp.body = json.dumps(doc) resp.body = json.dumps(doc)
self.api.add_route('/', SomeResource()) app = falcon.API()
app.add_route('/', SomeResource())
client = testing.TestClient(app)
result = self.simulate_get(query_string='oid=42&detailed=no&things=1') result = client.simulate_get(query_string='oid=42&detailed=no&things=1')
self.assertEqual(result.json['oid'], 42) assert result.json['oid'] == 42
self.assertFalse(result.json['detailed']) assert not result.json['detailed']
self.assertEqual(result.json['things'], [1]) assert result.json['things'] == [1]
params = {'oid': 42, 'detailed': False} params = {'oid': 42, 'detailed': False}
result = self.simulate_get(params=params) result = client.simulate_get(params=params)
self.assertEqual(result.json['oid'], params['oid']) assert result.json['oid'] == params['oid']
self.assertFalse(result.json['detailed']) assert not result.json['detailed']
self.assertEqual(result.json['things'], None) assert result.json['things'] is None
params = {'oid': 1978, 'detailed': 'yes', 'things': [1, 2, 3]} params = {'oid': 1978, 'detailed': 'yes', 'things': [1, 2, 3]}
result = self.simulate_get(params=params) result = client.simulate_get(params=params)
self.assertEqual(result.json['oid'], params['oid']) assert result.json['oid'] == params['oid']
self.assertTrue(result.json['detailed']) assert result.json['detailed']
self.assertEqual(result.json['things'], params['things']) assert result.json['things'] == params['things']
expected_qs = 'things=1,2,3' expected_qs = 'things=1,2,3'
result = self.simulate_get(params={'things': [1, 2, 3]}) result = client.simulate_get(params={'things': [1, 2, 3]})
self.assertEqual(result.json['query_string'], expected_qs) assert result.json['query_string'] == expected_qs
expected_qs = 'things=1&things=2&things=3' expected_qs = 'things=1&things=2&things=3'
result = self.simulate_get(params={'things': [1, 2, 3]}, result = client.simulate_get(params={'things': [1, 2, 3]},
params_csv=False) params_csv=False)
self.assertEqual(result.json['query_string'], expected_qs) assert result.json['query_string'] == expected_qs
def test_query_string_no_question(self): def test_query_string_no_question(self):
self.assertRaises(ValueError, self.simulate_get, query_string='?x=1') app = falcon.API()
app.add_route('/', testing.SimpleTestResource())
client = testing.TestClient(app)
with pytest.raises(ValueError):
client.simulate_get(query_string='?x=1')
def test_query_string_in_path(self): def test_query_string_in_path(self):
self.assertRaises(ValueError, self.simulate_get, path='/thing?x=1') app = falcon.API()
app.add_route('/', testing.SimpleTestResource())
client = testing.TestClient(app)
with pytest.raises(ValueError):
client.simulate_get(path='/thing?x=1')
class FancyAPI(falcon.API): class FancyAPI(falcon.API):
pass pass
class FancyTestCase(testing.TestCase): class TestCaseFancyAPI(testing.TestCase):
api_class = FancyAPI api_class = FancyAPI
def test_something(self): def test_something(self):
self.assertTrue(isinstance(self.api, FancyAPI)) self.assertTrue(isinstance(self.api, FancyAPI))
class TestNoApiClass(testing.TestCase):
def test_something(self):
self.assertTrue(isinstance(self.api, falcon.API))
class TestSetupApi(testing.TestCase):
def setUp(self):
super(TestSetupApi, self).setUp()
self.api = falcon.API()
def test_something(self):
self.assertTrue(isinstance(self.api, falcon.API))

View File

@@ -1,12 +1,23 @@
import io import io
import pytest
import six import six
import falcon
import falcon.testing as testing import falcon.testing as testing
unicode_message = u'Unicode: \x80' unicode_message = u'Unicode: \x80'
@pytest.fixture
def client():
app = falcon.API()
tehlogger = LoggerResource()
app.add_route('/logger', tehlogger)
return testing.TestClient(app)
class LoggerResource: class LoggerResource:
def on_get(self, req, resp): def on_get(self, req, resp):
@@ -16,13 +27,9 @@ class LoggerResource:
req.log_error(unicode_message.encode('utf-8')) req.log_error(unicode_message.encode('utf-8'))
class TestWSGIError(testing.TestBase): class TestWSGIError(object):
def before(self):
self.tehlogger = LoggerResource()
self.api.add_route('/logger', self.tehlogger)
def setup_method(self, method):
self.wsgierrors_buffer = io.BytesIO() self.wsgierrors_buffer = io.BytesIO()
if six.PY3: if six.PY3:
@@ -35,21 +42,20 @@ class TestWSGIError(testing.TestBase):
# with undefined encoding, so do the encoding manually. # with undefined encoding, so do the encoding manually.
self.wsgierrors = self.wsgierrors_buffer self.wsgierrors = self.wsgierrors_buffer
def test_responder_logged_bytestring(self): def test_responder_logged_bytestring(self, client):
self.simulate_request('/logger', wsgierrors=self.wsgierrors, client.simulate_request(path='/logger',
query_string='amount=10') wsgierrors=self.wsgierrors,
query_string='amount=10')
log = self.wsgierrors_buffer.getvalue() log = self.wsgierrors_buffer.getvalue()
self.assertIn(unicode_message.encode('utf-8'), log) assert unicode_message.encode('utf-8') in log
self.assertIn(b'?amount=10', log) assert b'?amount=10' in log
def test_responder_logged_unicode(self):
if six.PY3:
self.skipTest('Test only applies to Python 2')
self.simulate_request('/logger', wsgierrors=self.wsgierrors,
method='HEAD')
@pytest.mark.skipif(six.PY3, reason='Test only applies to Python 2')
def test_responder_logged_unicode(self, client):
client.simulate_request(path='/logger',
wsgierrors=self.wsgierrors,
method='HEAD')
log = self.wsgierrors_buffer.getvalue() log = self.wsgierrors_buffer.getvalue()
self.assertIn(unicode_message, log.decode('utf-8')) assert unicode_message in log.decode('utf-8')

View File

@@ -19,21 +19,19 @@ class TypeResource(testing.SimpleTestResource):
resp.body = json.dumps({'data': req.stream.read().decode('utf-8')}) resp.body = json.dumps({'data': req.stream.read().decode('utf-8')})
class TestWsgiRefInputWrapper(testing.TestCase): class TestWsgiRefInputWrapper(object):
def setUp(self):
super(TestWsgiRefInputWrapper, self).setUp()
# Set up a route to our TypeResoure
self.type_route = '/type'
self.api.add_route(self.type_route, TypeResource())
def test_resources_can_read_request_stream_during_tests(self): def test_resources_can_read_request_stream_during_tests(self):
"""Make sure we can perform a simple request during testing. """Make sure we can perform a simple request during testing.
Originally, testing would fail after performing a request because no Originally, testing would fail after performing a request because no
size was specified when calling `wsgiref.validate.InputWrapper.read()` size was specified when calling `wsgiref.validate.InputWrapper.read()`
via `req.stream.read()`""" via `req.stream.read()`"""
result = self.simulate_post(path=self.type_route, body='hello') app = falcon.API()
type_route = '/type'
app.add_route(type_route, TypeResource())
client = testing.TestClient(app)
self.assertEqual(result.status, falcon.HTTP_200) result = client.simulate_post(path=type_route, body='hello')
self.assertEqual(result.json, {'data': 'hello'})
assert result.status == falcon.HTTP_200
assert result.json == {'data': 'hello'}