1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
import re
import pytest
import falcon
import falcon.testing as testing
class Proxy:
def forward(self, req):
return falcon.HTTP_503
class Sink:
def __init__(self):
self._proxy = Proxy()
def __call__(self, req, resp, **kwargs):
resp.status = self._proxy.forward(req)
self.kwargs = kwargs
class SinkAsync(Sink):
async def __call__(self, req, resp, **kwargs):
super().__call__(req, resp, **kwargs)
def kitchen_sink(req, resp, **kwargs):
resp.set_header('X-Missing-Feature', 'kitchen-sink')
async def async_kitchen_sink(req, resp, **kwargs):
kitchen_sink(req, resp, **kwargs)
class BookCollection(testing.SimpleTestResource):
pass
@pytest.fixture
def resource():
return BookCollection()
@pytest.fixture
def sink(asgi):
return SinkAsync() if asgi else Sink()
@pytest.fixture
def client(asgi, util):
app = util.create_app(asgi)
return testing.TestClient(app)
class TestDefaultRouting:
def test_single_default_pattern(self, client, sink, resource):
client.app.add_sink(sink)
response = client.simulate_request(path='/')
assert response.status == falcon.HTTP_503
def test_single_simple_pattern(self, client, sink, resource):
client.app.add_sink(sink, r'/foo')
response = client.simulate_request(path='/foo/bar')
assert response.status == falcon.HTTP_503
def test_single_compiled_pattern(self, client, sink, resource):
client.app.add_sink(sink, re.compile(r'/foo'))
response = client.simulate_request(path='/foo/bar')
assert response.status == falcon.HTTP_503
response = client.simulate_request(path='/auth')
assert response.status == falcon.HTTP_404
def test_named_groups(self, client, sink, resource):
client.app.add_sink(sink, r'/user/(?P<id>\d+)')
response = client.simulate_request(path='/user/309')
assert response.status == falcon.HTTP_503
assert sink.kwargs['id'] == '309'
response = client.simulate_request(path='/user/sally')
assert response.status == falcon.HTTP_404
def test_multiple_patterns(self, asgi, client, sink, resource):
if asgi:
async def sink_too(req, resp):
resp.status = falcon.HTTP_781
else:
def sink_too(req, resp):
resp.status = falcon.HTTP_781
client.app.add_sink(sink, r'/foo')
client.app.add_sink(sink_too, r'/foo') # Last duplicate wins
client.app.add_sink(sink, r'/katza')
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.
client.app.add_route('/books', resource)
client.app.add_sink(sink, '/books')
response = client.simulate_request(path='/books')
assert resource.called
assert response.status == falcon.HTTP_200
def test_route_precedence_with_id(self, client, sink, resource):
# NOTE(kgriffs): In case of collision, the route takes precedence.
client.app.add_route('/books/{id}', resource)
client.app.add_sink(sink, '/books')
response = client.simulate_request(path='/books')
assert not resource.called
assert response.status == falcon.HTTP_503
def test_route_precedence_with_both_id(self, client, sink, resource):
# NOTE(kgriffs): In case of collision, the route takes precedence.
client.app.add_route('/books/{id}', resource)
client.app.add_sink(sink, r'/books/\d+')
response = client.simulate_request(path='/books/123')
assert resource.called
assert response.status == falcon.HTTP_200
class TestSinkMethodCompatibility:
def _verify_kitchen_sink(self, client):
resp = client.simulate_request('BREW', '/features')
assert resp.status_code == 200
assert resp.headers.get('X-Missing-Feature') == 'kitchen-sink'
def test_add_async_sink(self, client, asgi):
if not asgi:
with pytest.raises(falcon.CompatibilityError):
client.app.add_sink(async_kitchen_sink)
else:
client.app.add_sink(async_kitchen_sink, '/features')
self._verify_kitchen_sink(client)
def test_add_sync_sink(self, client, asgi, util):
if asgi:
with util.disable_asgi_non_coroutine_wrapping():
with pytest.raises(falcon.CompatibilityError):
client.app.add_sink(kitchen_sink)
else:
client.app.add_sink(kitchen_sink, '/features')
self._verify_kitchen_sink(client)
def test_add_sync_sink_with_wrapping(self, client, asgi):
client.app.add_sink(kitchen_sink, '/features')
self._verify_kitchen_sink(client)
|