async def test_responders(method): class Resource: async def on_get(self, req, resp): resp.set_header('method', 'get') async def on_head(self, req, resp): resp.set_header('method', 'head') async def on_post(self, req, resp): resp.set_header('method', 'post') async def on_put(self, req, resp): resp.set_header('method', 'put') async def on_options(self, req, resp): resp.set_header('method', 'options') async def on_patch(self, req, resp): resp.set_header('method', 'patch') async def on_delete(self, req, resp): resp.set_header('method', 'delete') resource = Resource() app = App() app.add_route('/', resource) async with testing.ASGIConductor(app) as conductor: simulate = getattr(conductor, 'simulate_' + method) result = await simulate('/') assert result.headers['method'] == method
async def test_wsgi_not_supported(): with pytest.raises(falcon.CompatibilityError): async with testing.TestClient(falcon.App()): pass with pytest.raises(falcon.CompatibilityError): async with testing.ASGIConductor(falcon.App()): pass
async def test_callback(callback_app, simple_resource, method, uri, expected): async with testing.ASGIConductor(callback_app) as conductor: resp = await conductor.simulate_request(method, uri) assert resp.status_code == 200 assert resp.text == expected await asyncio.wait_for(simple_resource.event.wait(), 3.0) assert simple_resource.called == 1
async def _test(): conductor = testing.ASGIConductor(app) result = await conductor.simulate_get() assert 'data: whassup' in result.text async with testing.ASGIConductor(app) as conductor: async with conductor.simulate_get_stream() as sr: event_count = 0 result_text = '' while event_count < 5: chunk = (await sr.stream.read()).decode() if not chunk: continue result_text += chunk event_count += len(chunk.strip().split('\n\n')) assert result_text.startswith('data: whassup\n\n' * 5) assert event_count == 5
def conductor(): app = falcon.asgi.App() return testing.ASGIConductor(app)
async def test_echo(): # noqa: C901 consumer_sleep = 0.01 producer_loop = 10 producer_sleep_factor = consumer_sleep / (producer_loop / 2) class Resource: def __init__(self): self.caught_operation_not_allowed = False async def on_websocket(self, req, ws, p1, p2, injected): # NOTE(kgriffs): Normally the receiver task is not started # until the websocket is started. But here we start it # first in order to simulate a potential race condition # that ASGIWebSocketSimulator._emit() guards against, # in case it ever arises due to the way the target ASGI # app may be implemented. ws._buffered_receiver.start() await asyncio.sleep(0) if ws.unaccepted: await ws.accept() try: await ws.accept() except falcon.OperationNotAllowed: self.caught_operation_not_allowed = True if ws.ready: await ws.send_text( f'{p1}:{p2}:{req.context.message}:{injected}') messages = deque() sink_task = falcon.create_task(self._sink(ws, messages)) while not sink_task.done(): if not messages: await asyncio.sleep(0) continue try: await ws.send_text(messages.popleft()) except falcon.WebSocketDisconnected: break sink_task.cancel() try: await sink_task except asyncio.CancelledError: pass async def _sink(self, ws, messages): while True: # NOTE(kgriffs): Throttle slightly to allow messages to # fill up the buffer. await asyncio.sleep(consumer_sleep) try: message = await ws.receive_text() except falcon.WebSocketDisconnected: break if message != 'ignore': messages.append(message) class MiddlewareA: async def process_resource_ws(self, req, ws, resource, params): assert isinstance(resource, Resource) assert isinstance(ws, falcon.asgi.WebSocket) params['injected'] = '42' class MiddlewareB: async def process_request_ws(self, req, ws): assert isinstance(ws, falcon.asgi.WebSocket) req.context.message = 'hello' resource = Resource() # NOTE(kgriffs): The two methods are split across different middleware # classes so that we can test code paths that check for the existence # of one WebSocket middleware method vs the other, and also so that # we can make sure both the kwarg and the add_middlware() paths # succeed. app = App(middleware=MiddlewareA()) app.add_middleware(MiddlewareB()) app.add_route('/{p1}/{p2}', resource) async with testing.ASGIConductor(app) as c: async with c.simulate_ws('/v1/v2', headers={}) as ws: assert (await ws.receive_text()) == 'v1:v2:hello:42' for i in range(producer_loop): message = str( i ) if i else '' # Test round-tripping the empty string as well for i in range(100): await ws.send_text('ignore') # NOTE(kgriffs): For part of the time, cause the buffer on the # server side to fill up, and for the remainder of the time # for the buffer to be empty and wait on the client for # additional messages. await asyncio.sleep(i * producer_sleep_factor) await ws.send_text(message) assert (await ws.receive_text()) == message await ws.close() assert ws.closed assert ws.close_code == CloseCode.NORMAL assert resource.caught_operation_not_allowed
async def t(): with pytest.raises(SomeException): async with testing.ASGIConductor(app): raise SomeException()