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_put, simulate_options, simulate_patch, simulate_delete,
TestClient, TestCase, SimpleTestResource, StartResponseMock,
capture_responder_args, rand_string, create_environ
capture_responder_args, rand_string, create_environ, redirected
Deprecated
----------

View File

@ -246,7 +246,7 @@ class Cookie(object):
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'):
"""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
high-performance file transmission when `resp.stream` is
set to a file-like object.
wsgierrors (io): The stream to use as *wsgierrors*
(default ``sys.stderr``)
Returns:
:py:class:`~.Result`: The result of the request
@ -323,6 +325,7 @@ def simulate_request(app, method='GET', path='/', query_string=None,
headers=headers,
body=body,
file_wrapper=file_wrapper,
wsgierrors=wsgierrors,
)
srmock = StartResponseMock()

View File

@ -24,6 +24,7 @@ directly from the `testing` package::
"""
import cgi
import contextlib
import io
import random
import sys
@ -206,6 +207,24 @@ def create_environ(path='/', query_string='', protocol='HTTP/1.1',
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
# ---------------------------------------------------------------------

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,3 @@
import sys
import testtools
try:
from StringIO import StringIO
except ImportError:
@ -9,45 +5,30 @@ except ImportError:
from falcon import API
from falcon.cmd import print_routes
from falcon.testing import redirected
_api = API()
_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):
"""Ensure traverse finds the proper routes and adds verbose output."""
print_routes.traverse(
_api._router._roots,
verbose=True)
output = StringIO()
with redirected(stdout=output):
print_routes.traverse(_api._router._roots, verbose=True)
route, options = self.output.getvalue().strip().split('\n')
self.assertEquals('-> /test', route)
self.assertTrue('OPTIONS' in options)
self.assertTrue('falcon/responders.py:' in options)
route, options = output.getvalue().strip().split('\n')
assert '-> /test' == route
assert 'OPTIONS' in options
assert 'falcon/responders.py:' in options
def test_traverse(self):
"""Ensure traverse finds the proper routes."""
print_routes.traverse(
_api._router._roots,
verbose=False)
output = StringIO()
with redirected(stdout=output):
print_routes.traverse(_api._router._roots, verbose=False)
route = self.output.getvalue().strip()
self.assertEquals('-> /test', route)
route = output.getvalue().strip()
assert '-> /test' == route

View File

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

View File

@ -1,8 +1,7 @@
import mimeparse
import testtools
class TestDeps(testtools.TestCase):
class TestDeps(object):
def test_mimeparse_correct_package(self):
"""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,
# whereas the mimeparse package is at version 0.1.4 at the time
# 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.status_codes as status
class TestError(testtools.TestCase):
class TestError(object):
def test_http_bad_request_no_title_and_desc(self):
try:
raise falcon.HTTPBadRequest()
except falcon.HTTPBadRequest as e:
self.assertEqual(status.HTTP_400, 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 status.HTTP_400 == e.title, \
'The title should be ' + status.HTTP_400 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_bad_request_with_title_and_desc(self):
try:
raise falcon.HTTPBadRequest(title='Test', description='Testdescription')
except falcon.HTTPBadRequest as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_unauthorized_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnauthorized()
except falcon.HTTPUnauthorized as e:
self.assertEqual(status.HTTP_401, e.title,
'The title should be ' + status.HTTP_401 + ', but it is: ' + e.title)
self.assertEqual(None, e.description, 'The description should be None')
self.assertNotIn('WWW-Authenticate', e.headers,
'Challenges should not be found in headers')
assert status.HTTP_401 == e.title, \
'The title should be ' + status.HTTP_401 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
assert 'WWW-Authenticate' not in e.headers, \
'Challenges should not be found in headers'
def test_http_unauthorized_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnauthorized(title='Test', description='Testdescription',
challenges=['Testch'])
except falcon.HTTPUnauthorized as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
self.assertEqual('Testch', e.headers['WWW-Authenticate'], 'Challenges should be None')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
assert 'Testch' == e.headers['WWW-Authenticate'], \
'Challenges should be None'
def test_http_forbidden_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPForbidden()
except falcon.HTTPForbidden as e:
self.assertEqual(status.HTTP_403, 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 status.HTTP_403 == e.title, \
'The title should be ' + status.HTTP_403 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_forbidden_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPForbidden(title='Test', description='Testdescription')
except falcon.HTTPForbidden as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_not_acceptable_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPNotAcceptable()
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):
try:
raise falcon.HTTPNotAcceptable(description='Testdescription')
except falcon.HTTPNotAcceptable as e:
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_conflict_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPConflict()
except falcon.HTTPConflict as e:
self.assertEqual(status.HTTP_409, 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 status.HTTP_409 == e.title, \
'The title should be ' + status.HTTP_409 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_conflict_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPConflict(title='Test', description='Testdescription')
except falcon.HTTPConflict as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_length_required_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLengthRequired()
except falcon.HTTPLengthRequired as e:
self.assertEqual(status.HTTP_411, 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 status.HTTP_411 == e.title, \
'The title should be ' + status.HTTP_411 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_length_required_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLengthRequired(title='Test', description='Testdescription')
except falcon.HTTPLengthRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_precondition_failed_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPPreconditionFailed()
except falcon.HTTPPreconditionFailed as e:
self.assertEqual(status.HTTP_412, 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 status.HTTP_412 == e.title, \
'The title should be ' + status.HTTP_412 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_precondition_failed_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPPreconditionFailed(title='Test', description='Testdescription')
except falcon.HTTPPreconditionFailed as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_request_entity_too_large_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPRequestEntityTooLarge()
except falcon.HTTPRequestEntityTooLarge as e:
self.assertEqual(status.HTTP_413, e.title,
'The title should be ' + status.HTTP_413 + ', but it is: ' + e.title)
self.assertEqual(None, e.description, 'The description should be None')
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers')
assert status.HTTP_413 == e.title, \
'The title should be ' + status.HTTP_413 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
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):
try:
raise falcon.HTTPRequestEntityTooLarge(title='Test', description='Testdescription',
retry_after=123)
except falcon.HTTPRequestEntityTooLarge as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
assert '123' == e.headers['Retry-After'], \
'Retry-After should be 123'
def test_http_uri_too_long_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUriTooLong()
except falcon.HTTPUriTooLong as e:
self.assertEqual(status.HTTP_414, 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 status.HTTP_414 == e.title, \
'The title should be ' + status.HTTP_414 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_uri_too_long_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUriTooLong(title='Test', description='Testdescription')
except falcon.HTTPUriTooLong as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_unsupported_media_type_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnsupportedMediaType()
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):
try:
raise falcon.HTTPUnsupportedMediaType(description='Testdescription')
except falcon.HTTPUnsupportedMediaType as e:
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_unprocessable_entity_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnprocessableEntity()
except falcon.HTTPUnprocessableEntity as e:
self.assertEqual(status.HTTP_422, 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 status.HTTP_422 == e.title, \
'The title should be ' + status.HTTP_422 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_unprocessable_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnprocessableEntity(title='Test', description='Testdescription')
except falcon.HTTPUnprocessableEntity as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_locked_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLocked()
except falcon.HTTPLocked as e:
self.assertEqual(status.HTTP_423, 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 status.HTTP_423 == e.title, \
'The title should be ' + status.HTTP_423 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_locked_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLocked(title='Test', description='Testdescription')
except falcon.HTTPLocked as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_failed_dependency_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPFailedDependency()
except falcon.HTTPFailedDependency as e:
self.assertEqual(status.HTTP_424, 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 status.HTTP_424 == e.title, \
'The title should be ' + status.HTTP_424 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_failed_dependency_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPFailedDependency(title='Test', description='Testdescription')
except falcon.HTTPFailedDependency as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_precondition_required_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPPreconditionRequired()
except falcon.HTTPPreconditionRequired as e:
self.assertEqual(status.HTTP_428, 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 status.HTTP_428 == e.title, \
'The title should be ' + status.HTTP_428 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_precondition_required_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPPreconditionRequired(title='Test', description='Testdescription')
except falcon.HTTPPreconditionRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_too_many_requests_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPTooManyRequests()
except falcon.HTTPTooManyRequests as e:
self.assertEqual(status.HTTP_429, e.title,
'The title should be ' + status.HTTP_429 + ', but it is: ' + e.title)
self.assertEqual(None, e.description, 'The description should be None')
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers')
assert status.HTTP_429 == e.title, \
'The title should be ' + status.HTTP_429 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
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):
try:
raise falcon.HTTPTooManyRequests(title='Test', description='Testdescription',
retry_after=123)
except falcon.HTTPTooManyRequests as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
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):
try:
raise falcon.HTTPRequestHeaderFieldsTooLarge()
except falcon.HTTPRequestHeaderFieldsTooLarge as e:
self.assertEqual(status.HTTP_431, 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 status.HTTP_431 == e.title, \
'The title should be ' + status.HTTP_431 + ', but it is: ' + e.title
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):
try:
raise falcon.HTTPRequestHeaderFieldsTooLarge(title='Test',
description='Testdescription')
except falcon.HTTPRequestHeaderFieldsTooLarge as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_unavailable_for_legal_reasons_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnavailableForLegalReasons()
except falcon.HTTPUnavailableForLegalReasons as e:
self.assertEqual(status.HTTP_451, 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 status.HTTP_451 == e.title, \
'The title should be ' + status.HTTP_451 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_unavailable_for_legal_reasons_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPUnavailableForLegalReasons(title='Test',
description='Testdescription')
except falcon.HTTPUnavailableForLegalReasons as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, \
'Description should be "Testdescription"'
def test_http_internal_server_error_entity_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPInternalServerError()
except falcon.HTTPInternalServerError as e:
self.assertEqual(status.HTTP_500, 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 status.HTTP_500 == e.title, \
'The title should be ' + status.HTTP_500 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_internal_server_error_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPInternalServerError(title='Test', description='Testdescription')
except falcon.HTTPInternalServerError as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_bad_gateway_entity_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPBadGateway()
except falcon.HTTPBadGateway as e:
self.assertEqual(status.HTTP_502, 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 status.HTTP_502 == e.title, \
'The title should be ' + status.HTTP_502 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_bad_gateway_entity_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPBadGateway(title='Test', description='Testdescription')
except falcon.HTTPBadGateway as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_service_unavailable_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPServiceUnavailable()
except falcon.HTTPServiceUnavailable as e:
self.assertEqual(status.HTTP_503, e.title,
'The title should be ' + status.HTTP_503 + ', but it is: ' + e.title)
self.assertEqual(None, e.description, 'The description should be None')
self.assertNotIn('Retry-After', e.headers, 'Retry-After should not be in the headers')
assert status.HTTP_503 == e.title, \
'The title should be ' + status.HTTP_503 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
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):
try:
raise falcon.HTTPServiceUnavailable(title='Test', description='Testdescription',
retry_after=123)
except falcon.HTTPServiceUnavailable as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
self.assertEqual('123', e.headers['Retry-After'], 'Retry-After should be 123')
assert 'Test', e.title == 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
assert '123', e.headers['Retry-After'] == 'Retry-After should be 123'
def test_http_insufficient_storage_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPInsufficientStorage()
except falcon.HTTPInsufficientStorage as e:
self.assertEqual(status.HTTP_507, 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 status.HTTP_507 == e.title, \
'The title should be ' + status.HTTP_507 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_insufficient_storage_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPInsufficientStorage(title='Test', description='Testdescription')
except falcon.HTTPInsufficientStorage as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_loop_detected_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLoopDetected()
except falcon.HTTPLoopDetected as e:
self.assertEqual(status.HTTP_508, 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 status.HTTP_508 == e.title, \
'The title should be ' + status.HTTP_508 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_loop_detected_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPLoopDetected(title='Test', description='Testdescription')
except falcon.HTTPLoopDetected as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_network_authentication_required_no_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPNetworkAuthenticationRequired()
except falcon.HTTPNetworkAuthenticationRequired as e:
self.assertEqual(status.HTTP_511, 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 status.HTTP_511 == e.title, \
'The title should be ' + status.HTTP_511 + ', but it is: ' + e.title
assert e.description is None, 'The description should be None'
def test_http_network_authentication_required_with_title_and_desc_and_challenges(self):
try:
raise falcon.HTTPNetworkAuthenticationRequired(title='Test',
description='Testdescription')
except falcon.HTTPNetworkAuthenticationRequired as e:
self.assertEqual('Test', e.title, 'Title should be "Test"')
self.assertEqual('Testdescription', e.description,
'Description should be "Testdescription"')
assert 'Test' == e.title, 'Title should be "Test"'
assert 'Testdescription' == e.description, 'Description should be "Testdescription"'
def test_http_error_repr(self):
error = falcon.HTTPBadRequest()
_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
from falcon import testing
@ -40,70 +42,75 @@ class ErroredClassResource(object):
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):
self.api.add_error_handler(Exception, capture_error)
class TestErrorHandler(object):
result = self.simulate_get()
self.assertEqual(result.text, 'error: Plain Exception')
def test_caught_error(self, client):
client.app.add_error_handler(Exception, capture_error)
result = self.simulate_head()
self.assertEqual(result.status_code, 723)
self.assertFalse(result.content)
result = client.simulate_get()
assert result.text == 'error: Plain Exception'
def test_uncaught_error(self):
self.api.add_error_handler(CustomException, capture_error)
self.assertRaises(Exception, self.simulate_get)
result = client.simulate_head()
assert result.status_code == 723
assert not result.content
def test_uncaught_error_else(self):
self.assertRaises(Exception, self.simulate_get)
def test_uncaught_error(self, client):
client.app.add_error_handler(CustomException, capture_error)
with pytest.raises(Exception):
client.simulate_get()
def test_converted_error(self):
self.api.add_error_handler(CustomException)
def test_uncaught_error_else(self, client):
with pytest.raises(Exception):
client.simulate_get()
result = self.simulate_delete()
self.assertEqual(result.status_code, 792)
self.assertEqual(result.json[u'title'], u'Internet crashed!')
def test_converted_error(self, client):
client.app.add_error_handler(CustomException)
def test_handle_not_defined(self):
self.assertRaises(AttributeError,
self.api.add_error_handler, CustomBaseException)
result = client.simulate_delete()
assert result.status_code == 792
assert result.json[u'title'] == u'Internet crashed!'
def test_subclass_error(self):
self.api.add_error_handler(CustomBaseException, capture_error)
def test_handle_not_defined(self, client):
with pytest.raises(AttributeError):
client.app.add_error_handler(CustomBaseException)
result = self.simulate_delete()
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: CustomException')
def test_subclass_error(self, client):
client.app.add_error_handler(CustomBaseException, capture_error)
def test_error_order_duplicate(self):
self.api.add_error_handler(Exception, capture_error)
self.api.add_error_handler(Exception, handle_error_first)
result = client.simulate_delete()
assert result.status_code == 723
assert result.text == 'error: CustomException'
result = self.simulate_get()
self.assertEqual(result.text, 'first error handler')
def test_error_order_duplicate(self, client):
client.app.add_error_handler(Exception, capture_error)
client.app.add_error_handler(Exception, handle_error_first)
def test_error_order_subclass(self):
self.api.add_error_handler(Exception, capture_error)
self.api.add_error_handler(CustomException, handle_error_first)
result = client.simulate_get()
assert result.text == 'first error handler'
result = self.simulate_delete()
self.assertEqual(result.status_code, 200)
self.assertEqual(result.text, 'first error handler')
def test_error_order_subclass(self, client):
client.app.add_error_handler(Exception, capture_error)
client.app.add_error_handler(CustomException, handle_error_first)
result = self.simulate_get()
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: Plain Exception')
result = client.simulate_delete()
assert result.status_code == 200
assert result.text == 'first error handler'
def test_error_order_subclass_masked(self):
self.api.add_error_handler(CustomException, handle_error_first)
self.api.add_error_handler(Exception, capture_error)
result = client.simulate_get()
assert result.status_code == 723
assert result.text == 'error: Plain Exception'
result = self.simulate_delete()
self.assertEqual(result.status_code, 723)
self.assertEqual(result.text, 'error: CustomException')
def test_error_order_subclass_masked(self, client):
client.app.add_error_handler(CustomException, handle_error_first)
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 datetime import datetime
import ddt
import pytest
import six
import falcon
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):
def __init__(self, content_type):
self.content_type = content_type
@ -169,78 +178,78 @@ class AppendHeaderResource(object):
resp.append_header('X-Things', 'thing-1')
@ddt.ddt
class TestHeaders(testing.TestCase):
class TestHeaders(object):
def setUp(self):
super(TestHeaders, self).setUp()
def test_content_length(self, client):
resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
result = client.simulate_get()
self.sample_body = testing.rand_string(0, 128 * 1024)
self.resource = testing.SimpleTestResource(body=self.sample_body)
self.api.add_route('/', self.resource)
content_length = str(len(SAMPLE_BODY))
assert result.headers['Content-Length'] == content_length
def test_content_length(self):
result = self.simulate_get()
def test_default_value(self, client):
resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
client.simulate_get()
content_length = str(len(self.sample_body))
self.assertEqual(result.headers['Content-Length'], content_length)
def test_default_value(self):
self.simulate_get()
req = self.resource.captured_req
req = resource.captured_req
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')
self.assertEqual(value, 'some-value')
assert value == 'some-value'
def test_required_header(self):
self.simulate_get()
def test_required_header(self, client):
resource = testing.SimpleTestResource(body=SAMPLE_BODY)
client.app.add_route('/', resource)
client.simulate_get()
try:
req = self.resource.captured_req
req = resource.captured_req
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:
self.assertIsInstance(ex, falcon.HTTPBadRequest)
self.assertEqual(ex.title, 'Missing header value')
assert isinstance(ex, falcon.HTTPBadRequest)
assert ex.title == 'Missing header value'
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)
def test_no_content_length(self, status):
self.api.add_route('/xxx', testing.SimpleTestResource(status=status))
@pytest.mark.parametrize('status', (falcon.HTTP_204, falcon.HTTP_304))
def test_no_content_length(self, client, status):
client.app.add_route('/xxx', testing.SimpleTestResource(status=status))
result = self.simulate_get('/xxx')
self.assertNotIn('Content-Length', result.headers)
self.assertFalse(result.content)
result = client.simulate_get('/xxx')
assert 'Content-Length' not in result.headers
assert not result.content
def test_content_header_missing(self):
def test_content_header_missing(self, client):
environ = testing.create_environ()
req = falcon.Request(environ)
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 = {
'X-Auth-Token': 'Setec Astronomy',
'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():
actual_value = self.resource.captured_req.get_header(name)
self.assertEqual(actual_value, expected_value)
actual_value = resource.captured_req.get_header(name)
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
for name, expected_value in request_headers.items():
actual_value = self.resource.captured_req.get_header(name)
self.assertEqual(actual_value, expected_value)
actual_value = resource.captured_req.get_header(name)
assert actual_value == expected_value
def test_headers_as_list(self):
def test_headers_as_list(self, client):
headers = [
('Client-ID', '692ba466-74bb-11e3-bf3f-7567c531c7ca'),
('Accept', 'audio/*; q=0.2, audio/basic')
@ -251,217 +260,213 @@ class TestHeaders(testing.TestCase):
req = falcon.Request(environ)
for name, value in headers:
self.assertIn((name.upper(), value), req.headers.items())
assert (name.upper(), value) in req.headers.items()
# Functional test
self.api.add_route('/', testing.SimpleTestResource(headers=headers))
result = self.simulate_get()
client.app.add_route('/', testing.SimpleTestResource(headers=headers))
result = client.simulate_get()
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!')
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'),
# NOTE(kgriffs): This only works because the client defaults to
# ISO-8859-1 IFF the media type is 'text'.
('text/plain', 'Hello ISO-8859-1!'),
)
@ddt.unpack
def test_override_default_media_type(self, content_type, body):
self.api = falcon.API(media_type=content_type)
self.api.add_route('/', testing.SimpleTestResource(body=body))
result = self.simulate_get()
])
def test_override_default_media_type(self, client, content_type, body):
client.app = falcon.API(media_type=content_type)
client.app.add_route('/', testing.SimpleTestResource(body=body))
result = client.simulate_get()
self.assertEqual(result.text, body)
self.assertEqual(result.headers['Content-Type'], content_type)
assert result.text == body
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"}'
self.api = falcon.API(media_type='application/json')
self.api.add_route('/', testing.SimpleTestResource(body=body))
result = self.simulate_get()
client.app = falcon.API(media_type='application/json')
client.app.add_route('/', testing.SimpleTestResource(body=body))
result = client.simulate_get()
self.assertEqual(result.content, body.encode('utf-8'))
self.assertIsInstance(result.text, six.text_type)
self.assertEqual(result.text, body)
self.assertEqual(result.json, {u'msg': u'Hello Unicode! \U0001F638'})
assert result.content == body.encode('utf-8')
assert isinstance(result.text, six.text_type)
assert result.text == body
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)
resource = HeaderHelpersResource(last_modified)
self.api.add_route('/', resource)
result = self.simulate_get()
client.app.add_route('/', resource)
result = client.simulate_get()
resp = resource.resp
content_type = 'x-falcon/peregrine'
self.assertEqual(resp.content_type, content_type)
self.assertEqual(result.headers['Content-Type'], content_type)
assert resp.content_type == content_type
assert result.headers['Content-Type'] == content_type
cache_control = ('public, private, no-cache, no-store, '
'must-revalidate, proxy-revalidate, max-age=3600, '
's-maxage=60, no-transform')
self.assertEqual(resp.cache_control, cache_control)
self.assertEqual(result.headers['Cache-Control'], cache_control)
assert resp.cache_control == cache_control
assert result.headers['Cache-Control'] == cache_control
etag = 'fa0d1a60ef6616bb28038515c8ea4cb2'
self.assertEqual(resp.etag, etag)
self.assertEqual(result.headers['Etag'], etag)
assert resp.etag == etag
assert result.headers['Etag'] == etag
lm_date = 'Tue, 01 Jan 2013 10:30:30 GMT'
self.assertEqual(resp.last_modified, lm_date)
self.assertEqual(result.headers['Last-Modified'], lm_date)
assert resp.last_modified == lm_date
assert result.headers['Last-Modified'] == lm_date
self.assertEqual(resp.retry_after, '3601')
self.assertEqual(result.headers['Retry-After'], '3601')
assert resp.retry_after == '3601'
assert result.headers['Retry-After'] == '3601'
self.assertEqual(resp.location, '/things/87')
self.assertEqual(result.headers['Location'], '/things/87')
assert resp.location == '/things/87'
assert result.headers['Location'] == '/things/87'
self.assertEqual(resp.content_location, '/things/78')
self.assertEqual(result.headers['Content-Location'], '/things/78')
assert resp.content_location == '/things/78'
assert result.headers['Content-Location'] == '/things/78'
content_range = 'bytes 0-499/10240'
self.assertEqual(resp.content_range, content_range)
self.assertEqual(result.headers['Content-Range'], content_range)
assert resp.content_range == content_range
assert result.headers['Content-Range'] == content_range
resp.content_range = (1, 499, 10 * 1024, u'bytes')
self.assertIsInstance(resp.content_range, str)
self.assertEqual(resp.content_range, 'bytes 1-499/10240')
assert isinstance(resp.content_range, str)
assert resp.content_range == 'bytes 1-499/10240'
self.assertEqual(resp.accept_ranges, 'bytes')
self.assertEqual(result.headers['Accept-Ranges'], 'bytes')
assert resp.accept_ranges == 'bytes'
assert result.headers['Accept-Ranges'] == 'bytes'
req_headers = {'Range': 'items=0-25'}
result = self.simulate_get(headers=req_headers)
self.assertEqual(result.headers['Content-Range'], 'items 0-25/100')
result = client.simulate_get(headers=req_headers)
assert result.headers['Content-Range'] == 'items 0-25/100'
# Check for duplicate headers
hist = defaultdict(lambda: 0)
for name, value in result.headers.items():
hist[name] += 1
self.assertEqual(1, hist[name])
assert 1 == hist[name]
def test_unicode_location_headers(self):
self.api.add_route('/', LocationHeaderUnicodeResource())
def test_unicode_location_headers(self, client):
client.app.add_route('/', LocationHeaderUnicodeResource())
result = self.simulate_get()
self.assertEqual(result.headers['Location'], '/%C3%A7runchy/bacon')
self.assertEqual(result.headers['Content-Location'], 'ab%C3%A7')
result = client.simulate_get()
assert result.headers['Location'] == '/%C3%A7runchy/bacon'
assert result.headers['Content-Location'] == 'ab%C3%A7'
# Test with the values swapped
result = self.simulate_head()
self.assertEqual(result.headers['Content-Location'],
'/%C3%A7runchy/bacon')
self.assertEqual(result.headers['Location'], 'ab%C3%A7')
result = client.simulate_head()
assert result.headers['Content-Location'] == '/%C3%A7runchy/bacon'
assert result.headers['Location'] == 'ab%C3%A7'
def test_unicode_headers_convertable(self):
self.api.add_route('/', UnicodeHeaderResource())
def test_unicode_headers_convertable(self, client):
client.app.add_route('/', UnicodeHeaderResource())
result = self.simulate_get('/')
result = client.simulate_get('/')
self.assertEqual(result.headers['Content-Type'], 'application/json')
self.assertEqual(result.headers['X-Auth-Token'], 'toomanysecrets')
self.assertEqual(result.headers['X-Symbol'], '@')
assert result.headers['Content-Type'] == 'application/json'
assert result.headers['X-Auth-Token'] == 'toomanysecrets'
assert result.headers['X-Symbol'] == '@'
def test_unicode_headers_not_convertable(self):
if six.PY3:
self.skipTest('Test only applies to Python 2')
@pytest.mark.skipif(six.PY3, reason='Test only applies to Python 2')
def test_unicode_headers_not_convertable(self, client):
client.app.add_route('/', UnicodeHeaderResource())
with pytest.raises(UnicodeEncodeError):
client.simulate_post('/')
self.api.add_route('/', UnicodeHeaderResource())
self.assertRaises(UnicodeEncodeError, self.simulate_post, '/')
self.assertRaises(UnicodeEncodeError, self.simulate_put, '/')
with pytest.raises(UnicodeEncodeError):
client.simulate_put('/')
def test_response_set_and_get_header(self):
def test_response_set_and_get_header(self, client):
resource = HeaderHelpersResource()
self.api.add_route('/', resource)
client.app.add_route('/', resource)
for method in ('HEAD', 'POST', 'PUT'):
result = self.simulate_request(method=method)
result = client.simulate_request(method=method)
content_type = 'x-falcon/peregrine'
self.assertEqual(result.headers['Content-Type'], content_type)
self.assertEqual(resource.resp.get_header('content-TyPe'),
content_type)
assert result.headers['Content-Type'] == content_type
assert resource.resp.get_header('content-TyPe') == content_type
self.assertEqual(result.headers['Cache-Control'], 'no-store')
self.assertEqual(result.headers['X-Auth-Token'], 'toomanysecrets')
assert result.headers['Cache-Control'] == 'no-store'
assert result.headers['X-Auth-Token'] == 'toomanysecrets'
self.assertEqual(resource.resp.location, None)
self.assertEqual(resource.resp.get_header('not-real'), None)
assert resource.resp.location is None
assert resource.resp.get_header('not-real') is None
# Check for duplicate headers
hist = defaultdict(int)
for name, value in result.headers.items():
hist[name] += 1
self.assertEqual(hist[name], 1)
assert hist[name] == 1
# 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):
self.api.add_route('/', AppendHeaderResource())
def test_response_append_header(self, client):
client.app.add_route('/', AppendHeaderResource())
for method in ('HEAD', 'GET'):
result = self.simulate_request(method=method)
result = client.simulate_request(method=method)
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')
self.assertEqual(result.headers['x-things'], 'thing-1')
result = client.simulate_request(method='POST')
assert result.headers['x-things'] == 'thing-1'
def test_vary_star(self):
self.api.add_route('/', VaryHeaderResource(['*']))
result = self.simulate_get()
self.assertEqual(result.headers['vary'], '*')
def test_vary_star(self, client):
client.app.add_route('/', VaryHeaderResource(['*']))
result = client.simulate_get()
assert result.headers['vary'] == '*'
@ddt.data(
@pytest.mark.parametrize('vary,expected_value', [
(['accept-encoding'], 'accept-encoding'),
([u'accept-encoding', '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, vary, expected_value):
])
def test_vary_header(self, client, vary, expected_value):
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):
self.api.add_route('/', testing.SimpleTestResource())
result = self.simulate_get()
def test_content_type_no_body(self, client):
client.app.add_route('/', testing.SimpleTestResource())
result = client.simulate_get()
# NOTE(kgriffs): Even when there is no body, Content-Type
# should still be included per wsgiref.validate
self.assertIn('Content-Type', result.headers)
self.assertEqual(result.headers['Content-Length'], '0')
assert 'Content-Type' in result.headers
assert result.headers['Content-Length'] == '0'
@ddt.data(falcon.HTTP_204, falcon.HTTP_304)
def test_no_content_type(self, status):
self.api.add_route('/', testing.SimpleTestResource(status=status))
@pytest.mark.parametrize('status', (falcon.HTTP_204, falcon.HTTP_304))
def test_no_content_type(self, client, status):
client.app.add_route('/', testing.SimpleTestResource(status=status))
result = self.simulate_get()
self.assertNotIn('Content-Type', result.headers)
result = client.simulate_get()
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'
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'
resource = LinkHeaderResource()
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 = (
'</things/2842>; rel=next, ' +
'<http://%C3%A7runchy/bacon>; rel=contents, ' +
@ -481,9 +486,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/alt-thing',
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; '
'title="A related thing"')
@ -491,9 +496,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'item',
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; '
"title*=UTF-8''A%20related%20thing, "
'</%C3%A7runchy/thing>; rel=item; '
@ -506,9 +511,9 @@ class TestHeaders(testing.TestCase):
resource.add_link(u'/\u00e7runchy/thing', 'item',
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; '
'anchor="/some%20thing/or-other"')
@ -516,18 +521,18 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'item',
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; '
'hreflang=en')
resource = LinkHeaderResource()
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; '
'hreflang=en-GB; hreflang=de')
@ -535,9 +540,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'about',
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; '
'type="video/mp4; codecs=avc1.640028"')
@ -545,9 +550,9 @@ class TestHeaders(testing.TestCase):
resource.add_link('/related/thing', 'alternate',
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; '
'title="A related thing"; '
"title*=UTF-8'en'A%20%C3%A7runchy%20thing; "
@ -561,23 +566,23 @@ class TestHeaders(testing.TestCase):
type_hint='application/json',
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):
result = self.simulate_options()
def test_content_length_options(self, client):
result = client.simulate_options()
content_length = '0'
self.assertEqual(result.headers['Content-Length'], content_length)
assert result.headers['Content-Length'] == content_length
# ----------------------------------------------------------------------
# Helpers
# ----------------------------------------------------------------------
def _check_link_header(self, resource, expected_value):
self._check_header(resource, 'Link', expected_value)
def _check_link_header(self, client, resource, expected_value):
self._check_header(client, resource, 'Link', expected_value)
def _check_header(self, resource, header, expected_value):
self.api.add_route('/', resource)
def _check_header(self, client, resource, header, expected_value):
client.app.add_route('/', resource)
result = self.simulate_get()
self.assertEqual(result.headers[header], expected_value)
result = client.simulate_get()
assert result.headers[header] == expected_value

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,34 +1,32 @@
import ddt
import pytest
from falcon.request import RequestOptions
import falcon.testing as testing
@ddt.ddt
class TestRequestOptions(testing.TestBase):
class TestRequestOptions(object):
def test_option_defaults(self):
options = RequestOptions()
self.assertFalse(options.keep_blank_qs_values)
self.assertFalse(options.auto_parse_form_urlencoded)
self.assertTrue(options.auto_parse_qs_csv)
self.assertTrue(options.strip_url_path_trailing_slash)
assert not options.keep_blank_qs_values
assert not options.auto_parse_form_urlencoded
assert options.auto_parse_qs_csv
assert options.strip_url_path_trailing_slash
@ddt.data(
@pytest.mark.parametrize('option_name', [
'keep_blank_qs_values',
'auto_parse_form_urlencoded',
'auto_parse_qs_csv',
'strip_url_path_trailing_slash',
)
])
def test_options_toggle(self, option_name):
options = RequestOptions()
setattr(options, option_name, True)
self.assertTrue(getattr(options, option_name))
assert getattr(options, option_name)
setattr(options, option_name, False)
self.assertFalse(getattr(options, option_name))
assert not getattr(options, option_name)
def test_incorrect_options(self):
options = RequestOptions()
@ -36,4 +34,5 @@ class TestRequestOptions(testing.TestBase):
def _assign_invalid():
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.testing as testing
@pytest.fixture
def client():
app = falcon.API()
resource = RedirectingResource()
app.add_route('/', resource)
return testing.TestClient(app)
class RedirectingResource(object):
# NOTE(kgriffs): You wouldn't necessarily use these types of
@ -26,24 +36,18 @@ class RedirectingResource(object):
raise falcon.HTTPPermanentRedirect('/perm/redirect')
@ddt.ddt
class TestRedirects(testing.TestBase):
class TestRedirects(object):
def before(self):
self.api.add_route('/', RedirectingResource())
@ddt.data(
@pytest.mark.parametrize('method,expected_status,expected_location', [
('GET', falcon.HTTP_301, '/moved/perm'),
('POST', falcon.HTTP_302, '/found'),
('PUT', falcon.HTTP_303, '/see/other'),
('DELETE', falcon.HTTP_307, '/tmp/redirect'),
('HEAD', falcon.HTTP_308, '/perm/redirect'),
)
@ddt.unpack
def test_redirect(self, method, expected_status, expected_location):
result = self.simulate_request('/', method=method)
])
def test_redirect(self, client, method, expected_status, expected_location):
result = client.simulate_request(path='/', method=method)
self.assertEqual(result, [])
self.assertEqual(self.srmock.status, expected_status)
self.assertEqual(self.srmock.headers_dict['location'],
expected_location)
assert not result.content
assert result.status == expected_status
assert result.headers['location'] == expected_location

View File

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

View File

@ -1,68 +1,88 @@
import io
from wsgiref.validate import InputWrapper
import pytest
import falcon
from falcon import request_helpers
import falcon.request
import falcon.testing as testing
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):
self.simulate_request('/', body='')
stream = self.resource.req.stream
@pytest.fixture
def client():
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 = '.'
self.simulate_request('', body=expected_body)
stream = self.resource.req.stream
client.simulate_request(path='/', body=expected_body)
stream = self._get_wrapped_stream(resource.req)
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)
self.assertEqual(stream.tell(), 1)
assert stream.tell() == 1
def test_tiny_body_overflow(self):
def test_tiny_body_overflow(self, client, resource):
client.app.add_route('/', resource)
expected_body = '.'
self.simulate_request('', body=expected_body)
stream = self.resource.req.stream
client.simulate_request(path='/', body=expected_body)
stream = self._get_wrapped_stream(resource.req)
# Read too many bytes; shouldn't block
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_len = len(expected_body)
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')
self.assertEqual(content_len, str(expected_len))
content_len = resource.req.get_header('content-length')
assert content_len == str(expected_len)
stream = self.resource.req.stream
stream = self._get_wrapped_stream(resource.req)
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)
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):
"""Test that we can get a bounded stream outside of wsgiref."""
environ = testing.create_environ()
req = falcon.Request(environ)
@ -73,7 +93,7 @@ class TestRequestBody(testing.TestBase):
# coverage of the property implementation.
assert bounded_stream is req.bounded_stream
data = bounded_stream.read()
self.assertEqual(len(data), 0)
assert len(data) == 0
def test_body_stream_wrapper(self):
data = testing.rand_string(SIZE_1_KB / 2, SIZE_1_KB)
@ -91,15 +111,15 @@ class TestRequestBody(testing.TestBase):
stream = io.BytesIO(expected_body)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.read(), expected_body)
assert body.read() == expected_body
stream = io.BytesIO(expected_body)
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)
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
# hang, but returns the empty string.
@ -107,43 +127,43 @@ class TestRequestBody(testing.TestBase):
body = request_helpers.Body(stream, expected_len)
for i in range(expected_len + 1):
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)
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)
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)
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)
body = request_helpers.Body(stream, expected_len)
self.assertEqual(body.readlines(), expected_lines)
assert body.readlines() == expected_lines
stream = io.BytesIO(expected_body)
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)
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)
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)
body = request_helpers.Body(stream, expected_len)
for i, line in enumerate(body):
self.assertEqual(line, expected_lines[i])
assert line == expected_lines[i]
def test_request_repr(self):
environ = testing.create_environ()
req = falcon.Request(environ)
_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
import falcon.testing as testing
class TestRequestContext(testing.TestBase):
class TestRequestContext(object):
def test_default_request_context(self):
env = testing.create_environ()
req = Request(env)
self.assertIsInstance(req.context, dict)
assert isinstance(req.context, dict)
def test_custom_request_context(self):
@ -20,7 +22,7 @@ class TestRequestContext(testing.TestBase):
env = testing.create_environ()
req = MyCustomRequest(env)
self.assertIsInstance(req.context, MyCustomContextType)
assert isinstance(req.context, MyCustomContextType)
def test_custom_request_context_failure(self):
@ -29,7 +31,8 @@ class TestRequestContext(testing.TestBase):
context_type = False
env = testing.create_environ()
self.assertRaises(TypeError, MyCustomRequest, env)
with pytest.raises(TypeError):
MyCustomRequest(env)
def test_custom_request_context_request_access(self):
@ -42,5 +45,5 @@ class TestRequestContext(testing.TestBase):
env = testing.create_environ()
req = MyCustomRequest(env)
self.assertIsInstance(req.context, dict)
self.assertEqual(req.context['uri'], req.uri)
assert isinstance(req.context, dict)
assert req.context['uri'] == req.uri

View File

@ -1,9 +1,8 @@
import falcon
import falcon.testing as testing
class TestResponseBody(testing.TestBase):
class TestResponseBody(object):
def test_append_body(self):
text = 'Hello beautiful world! '
@ -14,9 +13,9 @@ class TestResponseBody(testing.TestBase):
resp.body += token
resp.body += ' '
self.assertEqual(resp.body, text)
assert resp.body == text
def test_response_repr(self):
resp = falcon.Response()
_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
import falcon.testing as testing
class TestRequestContext(testing.TestBase):
class TestRequestContext(object):
def test_default_response_context(self):
resp = Response()
self.assertIsInstance(resp.context, dict)
assert isinstance(resp.context, dict)
def test_custom_response_context(self):
@ -17,14 +18,15 @@ class TestRequestContext(testing.TestBase):
context_type = MyCustomContextType
resp = MyCustomResponse()
self.assertIsInstance(resp.context, MyCustomContextType)
assert isinstance(resp.context, MyCustomContextType)
def test_custom_response_context_failure(self):
class MyCustomResponse(Response):
context_type = False
self.assertRaises(TypeError, MyCustomResponse)
with pytest.raises(TypeError):
MyCustomResponse()
def test_custom_response_context_factory(self):
@ -35,5 +37,5 @@ class TestRequestContext(testing.TestBase):
context_type = create_context
resp = MyCustomResponse()
self.assertIsInstance(resp.context, dict)
self.assertIs(resp.context['resp'], resp)
assert isinstance(resp.context, dict)
assert resp.context['resp'] is resp

View File

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

View File

@ -1,8 +1,10 @@
import pytest
from falcon import Request, Response
import falcon.testing as testing
class TestSlots(testing.TestBase):
class TestSlots(object):
def test_slots_request(self):
env = testing.create_environ()
@ -11,7 +13,7 @@ class TestSlots(testing.TestBase):
try:
req.doesnt = 'exist'
except AttributeError:
self.fail('Unable to add additional variables dynamically')
pytest.fail('Unable to add additional variables dynamically')
def test_slots_response(self):
resp = Response()
@ -19,4 +21,4 @@ class TestSlots(testing.TestBase):
try:
resp.doesnt = 'exist'
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
from falcon import routing
import falcon.testing as testing
@ddt.ddt
class TestUriTemplates(testing.TestBase):
class TestUriTemplates(object):
@ddt.data(42, falcon.API)
@pytest.mark.parametrize('value', (42, falcon.API))
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):
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):
self.assertRaises(ValueError, routing.compile_uri_template, value)
with pytest.raises(ValueError):
routing.compile_uri_template(value)
def test_root(self):
fields, pattern = routing.compile_uri_template('/')
self.assertFalse(fields)
self.assertFalse(pattern.match('/x'))
assert not fields
assert not pattern.match('/x')
result = pattern.match('/')
self.assertTrue(result)
self.assertFalse(result.groupdict())
assert result
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):
fields, pattern = routing.compile_uri_template(path)
self.assertFalse(fields)
self.assertFalse(pattern.match(path[:-1]))
assert not fields
assert not pattern.match(path[:-1])
result = pattern.match(path)
self.assertTrue(result)
self.assertFalse(result.groupdict())
assert result
assert not result.groupdict()
def test_one_field(self):
fields, pattern = routing.compile_uri_template('/{name}')
self.assertEqual(fields, set(['name']))
assert fields == set(['name'])
result = pattern.match('/Kelsier')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
assert result
assert result.groupdict() == {'name': 'Kelsier'}
fields, pattern = routing.compile_uri_template('/character/{name}')
self.assertEqual(fields, set(['name']))
assert fields == set(['name'])
result = pattern.match('/character/Kelsier')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
assert result
assert result.groupdict() == {'name': 'Kelsier'}
fields, pattern = routing.compile_uri_template('/character/{name}/profile')
self.assertEqual(fields, set(['name']))
assert fields == set(['name'])
self.assertFalse(pattern.match('/character'))
self.assertFalse(pattern.match('/character/Kelsier'))
self.assertFalse(pattern.match('/character/Kelsier/'))
assert not pattern.match('/character')
assert not pattern.match('/character/Kelsier')
assert not pattern.match('/character/Kelsier/')
result = pattern.match('/character/Kelsier/profile')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
assert result
assert result.groupdict() == {'name': 'Kelsier'}
def test_one_field_with_digits(self):
fields, pattern = routing.compile_uri_template('/{name123}')
self.assertEqual(fields, set(['name123']))
assert fields == set(['name123'])
result = pattern.match('/Kelsier')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name123': 'Kelsier'})
assert result
assert result.groupdict() == {'name123': 'Kelsier'}
def test_one_field_with_prefixed_digits(self):
fields, pattern = routing.compile_uri_template('/{37signals}')
self.assertEqual(fields, set())
assert fields == set()
result = pattern.match('/s2n')
self.assertFalse(result)
assert not result
@ddt.data('', '/')
@pytest.mark.parametrize('postfix', ('', '/'))
def test_two_fields(self, postfix):
path = '/book/{book_id}/characters/{n4m3}' + postfix
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')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'n4m3': 'Vin', 'book_id': '0765350386'})
assert result
assert result.groupdict() == {'n4m3': 'Vin', 'book_id': '0765350386'}
def test_three_fields(self):
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')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'a': 'one', 'b': '2', 'c': '3'})
assert result
assert result.groupdict() == {'a': 'one', 'b': '2', 'c': '3'}
def test_malformed_field(self):
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')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'a': 'one', 'c': '3'})
assert result
assert result.groupdict() == {'a': 'one', 'c': '3'}

View File

@ -10,7 +10,6 @@ import random
import pytest
import six
import testtools
import falcon
from falcon import testing
@ -27,10 +26,9 @@ def _arbitrary_uris(count, length):
)
class TestFalconUtils(testtools.TestCase):
class TestFalconUtils(object):
def setUp(self):
super(TestFalconUtils, self).setUp()
def setup_method(self, method):
# NOTE(cabrera): for DRYness - used in uri.[de|en]code tests
# below.
self.uris = _arbitrary_uris(count=100, length=32)
@ -55,73 +53,56 @@ class TestFalconUtils(testtools.TestCase):
delta = actual - expected
delta_sec = abs(delta.days * 86400 + delta.seconds)
self.assertLessEqual(delta_sec, 1)
assert delta_sec <= 1
def test_dt_to_http(self):
self.assertEqual(
falcon.dt_to_http(datetime(2013, 4, 4)),
'Thu, 04 Apr 2013 00:00:00 GMT')
assert falcon.dt_to_http(datetime(2013, 4, 4)) == 'Thu, 04 Apr 2013 00:00:00 GMT'
self.assertEqual(
falcon.dt_to_http(datetime(2013, 4, 4, 10, 28, 54)),
'Thu, 04 Apr 2013 10:28:54 GMT')
assert falcon.dt_to_http(
datetime(2013, 4, 4, 10, 28, 54)
) == 'Thu, 04 Apr 2013 10:28:54 GMT'
def test_http_date_to_dt(self):
self.assertEqual(
falcon.http_date_to_dt('Thu, 04 Apr 2013 00:00:00 GMT'),
datetime(2013, 4, 4))
assert falcon.http_date_to_dt('Thu, 04 Apr 2013 00:00:00 GMT') == datetime(2013, 4, 4)
self.assertEqual(
falcon.http_date_to_dt('Thu, 04 Apr 2013 10:28:54 GMT'),
datetime(2013, 4, 4, 10, 28, 54))
assert falcon.http_date_to_dt(
'Thu, 04 Apr 2013 10:28:54 GMT'
) == datetime(2013, 4, 4, 10, 28, 54)
self.assertRaises(
ValueError,
falcon.http_date_to_dt, 'Thu, 04-Apr-2013 10:28:54 GMT')
with pytest.raises(ValueError):
falcon.http_date_to_dt('Thu, 04-Apr-2013 10:28:54 GMT')
self.assertEqual(
falcon.http_date_to_dt('Thu, 04-Apr-2013 10:28:54 GMT',
obs_date=True),
datetime(2013, 4, 4, 10, 28, 54))
assert falcon.http_date_to_dt(
'Thu, 04-Apr-2013 10:28:54 GMT', obs_date=True
) == datetime(2013, 4, 4, 10, 28, 54)
self.assertRaises(
ValueError,
falcon.http_date_to_dt, 'Sun Nov 6 08:49:37 1994')
with pytest.raises(ValueError):
falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994')
self.assertRaises(
ValueError,
falcon.http_date_to_dt, 'Nov 6 08:49:37 1994', obs_date=True)
with pytest.raises(ValueError):
falcon.http_date_to_dt('Nov 6 08:49:37 1994', obs_date=True)
self.assertEqual(
falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994', obs_date=True),
datetime(1994, 11, 6, 8, 49, 37))
assert falcon.http_date_to_dt(
'Sun Nov 6 08:49:37 1994', obs_date=True
) == datetime(1994, 11, 6, 8, 49, 37)
self.assertEqual(
falcon.http_date_to_dt('Sunday, 06-Nov-94 08:49:37 GMT',
obs_date=True),
datetime(1994, 11, 6, 8, 49, 37))
assert falcon.http_date_to_dt(
'Sunday, 06-Nov-94 08:49:37 GMT', obs_date=True
) == datetime(1994, 11, 6, 8, 49, 37)
def test_pack_query_params_none(self):
self.assertEqual(
falcon.to_query_str({}),
'')
assert falcon.to_query_str({}) == ''
def test_pack_query_params_one(self):
self.assertEqual(
falcon.to_query_str({'limit': 10}),
'?limit=10')
assert falcon.to_query_str({'limit': 10}) == '?limit=10'
self.assertEqual(
falcon.to_query_str({'things': [1, 2, 3]}),
'?things=1,2,3')
assert falcon.to_query_str(
{'things': [1, 2, 3]}) == '?things=1,2,3'
self.assertEqual(
falcon.to_query_str({'things': ['a']}),
'?things=a')
assert falcon.to_query_str({'things': ['a']}) == '?things=a'
self.assertEqual(
falcon.to_query_str({'things': ['a', 'b']}),
'?things=a,b')
assert falcon.to_query_str(
{'things': ['a', 'b']}) == '?things=a,b'
expected = ('?things=a&things=b&things=&things=None'
'&things=true&things=false&things=0')
@ -131,7 +112,7 @@ class TestFalconUtils(testtools.TestCase):
comma_delimited_lists=False
)
self.assertEqual(actual, expected)
assert actual == expected
def test_pack_query_params_several(self):
garbage_in = {
@ -157,76 +138,73 @@ class TestFalconUtils(testtools.TestCase):
'y': '0.2',
'doit': 'false'}
self.assertEqual(expected, garbage_out)
assert expected == garbage_out
def test_uri_encode(self):
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'
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'
expected = ('http://example.com/v1/fizbit/messages'
'?limit=3&e%C3%A7ho=true')
self.assertEqual(uri.encode(url), expected)
assert uri.encode(url) == expected
def test_uri_encode_double(self):
url = 'http://example.com/v1/fiz bit/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'
expected = ('http://example.com/v1/fizbit/messages'
'?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/%'
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/%%'
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
url = 'http://something?redirect_uri=http%3A%2F%2Fsite'
self.assertEqual(uri.encode(url), url)
assert uri.encode(url) == url
hex_digits = 'abcdefABCDEF0123456789'
for c1 in hex_digits:
for c2 in hex_digits:
url = 'http://example.com/%' + c1 + c2
encoded = uri.encode(uri.encode(url))
self.assertEqual(encoded, url)
assert encoded == url
def test_uri_encode_value(self):
self.assertEqual(uri.encode_value('abcd'), 'abcd')
self.assertEqual(uri.encode_value(u'abcd'), u'abcd')
self.assertEqual(uri.encode_value(u'ab cd'), u'ab%20cd')
self.assertEqual(uri.encode_value(u'\u00e7'), '%C3%A7')
self.assertEqual(uri.encode_value(u'\u00e7\u20ac'),
'%C3%A7%E2%82%AC')
self.assertEqual(uri.encode_value('ab/cd'), 'ab%2Fcd')
self.assertEqual(uri.encode_value('ab+cd=42,9'),
'ab%2Bcd%3D42%2C9')
assert uri.encode_value('abcd') == 'abcd'
assert uri.encode_value(u'abcd') == u'abcd'
assert uri.encode_value(u'ab cd') == u'ab%20cd'
assert uri.encode_value(u'\u00e7') == '%C3%A7'
assert uri.encode_value(u'\u00e7\u20ac') == '%C3%A7%E2%82%AC'
assert uri.encode_value('ab/cd') == 'ab%2Fcd'
assert uri.encode_value('ab+cd=42,9') == 'ab%2Bcd%3D42%2C9'
def test_uri_decode(self):
self.assertEqual(uri.decode('abcd'), 'abcd')
self.assertEqual(uri.decode(u'abcd'), u'abcd')
self.assertEqual(uri.decode(u'ab%20cd'), u'ab cd')
assert uri.decode('abcd') == 'abcd'
assert uri.decode(u'abcd') == u'abcd'
assert uri.decode(u'ab%20cd') == u'ab cd'
self.assertEqual(uri.decode('This thing is %C3%A7'),
u'This thing is \u00e7')
assert uri.decode('This thing is %C3%A7') == u'This thing is \u00e7'
self.assertEqual(uri.decode('This thing is %C3%A7%E2%82%AC'),
u'This thing is \u00e7\u20ac')
assert uri.decode('This thing is %C3%A7%E2%82%AC') == 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'),
'http://example.com?x=ab+cd=42,9')
assert uri.decode(
'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):
equiv_quote = functools.partial(
@ -235,7 +213,7 @@ class TestFalconUtils(testtools.TestCase):
for case in self.uris:
expect = equiv_quote(case)
actual = uri.encode(case)
self.assertEqual(expect, actual)
assert expect == actual
def test_prop_uri_encode_value_models_stdlib_quote_safe_tilde(self):
equiv_quote = functools.partial(
@ -244,7 +222,7 @@ class TestFalconUtils(testtools.TestCase):
for case in self.uris:
expect = equiv_quote(case)
actual = uri.encode_value(case)
self.assertEqual(expect, actual)
assert expect == actual
def test_prop_uri_decode_models_stdlib_unquote_plus(self):
stdlib_unquote = six.moves.urllib.parse.unquote_plus
@ -253,7 +231,7 @@ class TestFalconUtils(testtools.TestCase):
expect = stdlib_unquote(case)
actual = uri.decode(case)
self.assertEqual(expect, actual)
assert expect == actual
def test_parse_query_string(self):
query_strinq = (
@ -270,114 +248,106 @@ class TestFalconUtils(testtools.TestCase):
decoded_json = '{"test1": "data1", "test2": "data2"}'
result = uri.parse_query_string(query_strinq)
self.assertEqual(result['a'], decoded_url)
self.assertEqual(result['b'], decoded_json)
self.assertEqual(result['c'], ['1', '2', '3'])
self.assertEqual(result['d'], 'test')
self.assertEqual(result['e'], ['a', '&=,'])
self.assertEqual(result['f'], ['a', 'a=b'])
self.assertEqual(result[u'é'], 'a=b')
assert result['a'] == decoded_url
assert result['b'] == decoded_json
assert result['c'] == ['1', '2', '3']
assert result['d'] == 'test'
assert result['e'] == ['a', '&=,']
assert result['f'] == ['a', 'a=b']
assert result[u'é'] == 'a=b'
result = uri.parse_query_string(query_strinq, True)
self.assertEqual(result['a'], decoded_url)
self.assertEqual(result['b'], decoded_json)
self.assertEqual(result['c'], ['1', '2', '3'])
self.assertEqual(result['d'], 'test')
self.assertEqual(result['e'], ['a', '', '&=,'])
self.assertEqual(result['f'], ['a', 'a=b'])
self.assertEqual(result[u'é'], 'a=b')
assert result['a'] == decoded_url
assert result['b'] == decoded_json
assert result['c'] == ['1', '2', '3']
assert result['d'] == 'test'
assert result['e'] == ['a', '', '&=,']
assert result['f'] == ['a', 'a=b']
assert result[u'é'] == 'a=b'
def test_parse_host(self):
self.assertEqual(uri.parse_host('::1'), ('::1', None))
self.assertEqual(uri.parse_host('2001:ODB8:AC10:FE01::'),
('2001:ODB8:AC10:FE01::', None))
self.assertEqual(
uri.parse_host('2001:ODB8:AC10:FE01::', default_port=80),
('2001:ODB8:AC10:FE01::', 80))
assert uri.parse_host('::1') == ('::1', None)
assert uri.parse_host('2001:ODB8:AC10:FE01::') == ('2001:ODB8:AC10:FE01::', None)
assert uri.parse_host(
'2001:ODB8:AC10:FE01::', default_port=80
) == ('2001:ODB8:AC10:FE01::', 80)
ipv6_addr = '2001:4801:1221:101:1c10::f5:116'
self.assertEqual(uri.parse_host(ipv6_addr), (ipv6_addr, None))
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']'),
(ipv6_addr, None))
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:28080'),
(ipv6_addr, 28080))
self.assertEqual(uri.parse_host('[' + ipv6_addr + ']:8080'),
(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))
assert uri.parse_host(ipv6_addr) == (ipv6_addr, None)
assert uri.parse_host('[' + ipv6_addr + ']') == (ipv6_addr, None)
assert uri.parse_host('[' + ipv6_addr + ']:28080') == (ipv6_addr, 28080)
assert uri.parse_host('[' + ipv6_addr + ']:8080') == (ipv6_addr, 8080)
assert uri.parse_host('[' + ipv6_addr + ']:123') == (ipv6_addr, 123)
assert uri.parse_host('[' + ipv6_addr + ']:42') == (ipv6_addr, 42)
self.assertEqual(uri.parse_host('173.203.44.122'),
('173.203.44.122', None))
self.assertEqual(uri.parse_host('173.203.44.122', default_port=80),
('173.203.44.122', 80))
self.assertEqual(uri.parse_host('173.203.44.122:27070'),
('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))
assert uri.parse_host('173.203.44.122') == ('173.203.44.122', None)
assert uri.parse_host('173.203.44.122', default_port=80) == ('173.203.44.122', 80)
assert uri.parse_host('173.203.44.122:27070') == ('173.203.44.122', 27070)
assert uri.parse_host('173.203.44.122:123') == ('173.203.44.122', 123)
assert uri.parse_host('173.203.44.122:42') == ('173.203.44.122', 42)
self.assertEqual(uri.parse_host('example.com'),
('example.com', None))
self.assertEqual(uri.parse_host('example.com', default_port=443),
('example.com', 443))
self.assertEqual(uri.parse_host('falcon.example.com'),
('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))
assert uri.parse_host('example.com') == ('example.com', None)
assert uri.parse_host('example.com', default_port=443) == ('example.com', 443)
assert uri.parse_host('falcon.example.com') == ('falcon.example.com', None)
assert uri.parse_host('falcon.example.com:9876') == ('falcon.example.com', 9876)
assert uri.parse_host('falcon.example.com:42') == ('falcon.example.com', 42)
def test_get_http_status(self):
self.assertEqual(falcon.get_http_status(404), falcon.HTTP_404)
self.assertEqual(falcon.get_http_status(404.3), falcon.HTTP_404)
self.assertEqual(falcon.get_http_status('404.3'), falcon.HTTP_404)
self.assertEqual(falcon.get_http_status(404.9), falcon.HTTP_404)
self.assertEqual(falcon.get_http_status('404'), falcon.HTTP_404)
self.assertEqual(falcon.get_http_status(123), '123 Unknown')
self.assertRaises(ValueError, falcon.get_http_status, 'not_a_number')
self.assertRaises(ValueError, falcon.get_http_status, 0)
self.assertRaises(ValueError, falcon.get_http_status, 99)
self.assertRaises(ValueError, falcon.get_http_status, -404.3)
self.assertRaises(ValueError, falcon.get_http_status, '-404')
self.assertRaises(ValueError, falcon.get_http_status, '-404.3')
self.assertEqual(falcon.get_http_status(123, 'Go Away'), '123 Go Away')
assert falcon.get_http_status(404) == falcon.HTTP_404
assert falcon.get_http_status(404.3) == falcon.HTTP_404
assert falcon.get_http_status('404.3') == falcon.HTTP_404
assert falcon.get_http_status(404.9) == falcon.HTTP_404
assert falcon.get_http_status('404') == falcon.HTTP_404
assert falcon.get_http_status(123) == '123 Unknown'
with pytest.raises(ValueError):
falcon.get_http_status('not_a_number')
with pytest.raises(ValueError):
falcon.get_http_status(0)
with pytest.raises(ValueError):
falcon.get_http_status(0)
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."""
def test_path_escape_chars_in_create_environ(self):
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):
self.assertRaises(ValueError, testing.create_environ,
query_string='?foo=bar')
with pytest.raises(ValueError):
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):
if six.PY3:
self.skip('Test does not apply to Py3K')
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')
self.assertEqual(env['PATH_INFO'], '/simple')
assert env['PATH_INFO'] == '/simple'
def test_none_header_value_in_create_environ(self):
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):
body = self.simulate_request('/', decode='utf-8')
self.assertEqual(body, '')
app = falcon.API()
client = testing.TestClient(app)
response = client.simulate_request(path='/')
assert response.text == ''
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(
@ -408,36 +378,40 @@ def test_simulate_request_protocol(protocol, method):
pass
class TestFalconTestCase(testing.TestCase):
class TestFalconTestCase(object):
"""Verify some branches not covered elsewhere."""
def test_status(self):
app = falcon.API()
resource = testing.SimpleTestResource(status=falcon.HTTP_702)
self.api.add_route('/', resource)
app.add_route('/', resource)
client = testing.TestClient(app)
result = self.simulate_get()
self.assertEqual(result.status, falcon.HTTP_702)
result = client.simulate_get()
assert result.status == falcon.HTTP_702
def test_wsgi_iterable_not_closeable(self):
result = testing.Result([], falcon.HTTP_200, [])
self.assertFalse(result.content)
assert not result.content
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):
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()
self.assertEqual(result.text, result.text)
result = client.simulate_get()
assert result.text == result.text
def test_simple_resource_body_json_xor(self):
self.assertRaises(
ValueError,
testing.SimpleTestResource,
body='',
json={},
)
with pytest.raises(ValueError):
testing.SimpleTestResource(body='', json={})
def test_query_string(self):
class SomeResource(object):
@ -451,47 +425,71 @@ class TestFalconTestCase(testing.TestCase):
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')
self.assertEqual(result.json['oid'], 42)
self.assertFalse(result.json['detailed'])
self.assertEqual(result.json['things'], [1])
result = client.simulate_get(query_string='oid=42&detailed=no&things=1')
assert result.json['oid'] == 42
assert not result.json['detailed']
assert result.json['things'] == [1]
params = {'oid': 42, 'detailed': False}
result = self.simulate_get(params=params)
self.assertEqual(result.json['oid'], params['oid'])
self.assertFalse(result.json['detailed'])
self.assertEqual(result.json['things'], None)
result = client.simulate_get(params=params)
assert result.json['oid'] == params['oid']
assert not result.json['detailed']
assert result.json['things'] is None
params = {'oid': 1978, 'detailed': 'yes', 'things': [1, 2, 3]}
result = self.simulate_get(params=params)
self.assertEqual(result.json['oid'], params['oid'])
self.assertTrue(result.json['detailed'])
self.assertEqual(result.json['things'], params['things'])
result = client.simulate_get(params=params)
assert result.json['oid'] == params['oid']
assert result.json['detailed']
assert result.json['things'] == params['things']
expected_qs = 'things=1,2,3'
result = self.simulate_get(params={'things': [1, 2, 3]})
self.assertEqual(result.json['query_string'], expected_qs)
result = client.simulate_get(params={'things': [1, 2, 3]})
assert result.json['query_string'] == expected_qs
expected_qs = 'things=1&things=2&things=3'
result = self.simulate_get(params={'things': [1, 2, 3]},
params_csv=False)
self.assertEqual(result.json['query_string'], expected_qs)
result = client.simulate_get(params={'things': [1, 2, 3]},
params_csv=False)
assert result.json['query_string'] == expected_qs
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):
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):
pass
class FancyTestCase(testing.TestCase):
class TestCaseFancyAPI(testing.TestCase):
api_class = FancyAPI
def test_something(self):
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 pytest
import six
import falcon
import falcon.testing as testing
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:
def on_get(self, req, resp):
@ -16,13 +27,9 @@ class LoggerResource:
req.log_error(unicode_message.encode('utf-8'))
class TestWSGIError(testing.TestBase):
def before(self):
self.tehlogger = LoggerResource()
self.api.add_route('/logger', self.tehlogger)
class TestWSGIError(object):
def setup_method(self, method):
self.wsgierrors_buffer = io.BytesIO()
if six.PY3:
@ -35,21 +42,20 @@ class TestWSGIError(testing.TestBase):
# with undefined encoding, so do the encoding manually.
self.wsgierrors = self.wsgierrors_buffer
def test_responder_logged_bytestring(self):
self.simulate_request('/logger', wsgierrors=self.wsgierrors,
query_string='amount=10')
def test_responder_logged_bytestring(self, client):
client.simulate_request(path='/logger',
wsgierrors=self.wsgierrors,
query_string='amount=10')
log = self.wsgierrors_buffer.getvalue()
self.assertIn(unicode_message.encode('utf-8'), log)
self.assertIn(b'?amount=10', 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')
assert unicode_message.encode('utf-8') in log
assert b'?amount=10' in log
@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()
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')})
class TestWsgiRefInputWrapper(testing.TestCase):
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())
class TestWsgiRefInputWrapper(object):
def test_resources_can_read_request_stream_during_tests(self):
"""Make sure we can perform a simple request during testing.
Originally, testing would fail after performing a request because no
size was specified when calling `wsgiref.validate.InputWrapper.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)
self.assertEqual(result.json, {'data': 'hello'})
result = client.simulate_post(path=type_route, body='hello')
assert result.status == falcon.HTTP_200
assert result.json == {'data': 'hello'}