Ejemplo n.º 1
0
def test_asgi_conductor_raised_error_skips_shutdown():
    class SomeException(Exception):
        pass

    class Foo:
        def __init__(self):
            self.called_startup = False
            self.called_shutdown = False

        async def process_startup(self, scope, event):
            self.called_startup = True

        async def process_shutdown(self, scope, event):
            self.called_shutdown = True

    foo = Foo()

    app = App()
    app.add_middleware(foo)

    async def t():
        with pytest.raises(SomeException):
            async with testing.ASGIConductor(app):
                raise SomeException()

    falcon.invoke_coroutine_sync(t)
    assert foo.called_startup
    assert not foo.called_shutdown
Ejemplo n.º 2
0
def test_at_least_one_event_method_required():
    class Foo:
        pass

    app = App()

    with pytest.raises(TypeError):
        app.add_middleware(Foo())
Ejemplo n.º 3
0
def test_startup_only():
    class Foo:
        async def process_startup(self, scope, event):
            self._called = True

    foo = Foo()

    app = App()
    app.add_middleware(foo)
    client = testing.TestClient(app)

    client.simulate_get()

    assert foo._called
Ejemplo n.º 4
0
def test_mw_methods_must_be_coroutines():
    class MiddlewareA:
        def process_resource_ws(self, req, ws, resource, params):
            pass

    class MiddlewareB:
        def process_request_ws(self, req, ws):
            pass

    app = App()

    for mw in [MiddlewareA(), MiddlewareB()]:
        with pytest.raises(falcon.CompatibilityError):
            app.add_middleware(mw)

        with pytest.raises(falcon.CompatibilityError):
            App(middleware=mw)
Ejemplo n.º 5
0
def test_shutdown_raises():
    class HandlerA:
        def __init__(self):
            self._startup_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            raise Exception('testing 321')

    class HandlerB:
        def __init__(self):
            self._startup_called = False
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    a = HandlerA()
    b1 = HandlerB()
    b2 = HandlerB()

    app = App()
    app.add_middleware(b1)
    app.add_middleware([a, b2])
    client = testing.TestClient(app)

    with pytest.raises(RuntimeError) as excinfo:
        client.simulate_get()

    message = str(excinfo.value)

    assert message.startswith('ASGI app returned lifespan.shutdown.failed.')
    assert 'testing 321' in message

    assert a._startup_called
    assert b1._startup_called
    assert not b1._shutdown_called
    assert b2._startup_called
    assert b2._shutdown_called
Ejemplo n.º 6
0
def test_startup_raises():
    class Foo:
        def __init__(self):
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            raise Exception('testing 123')

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    class Bar:
        def __init__(self):
            self._startup_called = False
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    foo = Foo()
    bar = Bar()

    app = App()
    app.add_middleware([foo, bar])
    client = testing.TestClient(app)

    with pytest.raises(RuntimeError) as excinfo:
        client.simulate_get()

    message = str(excinfo.value)

    assert message.startswith('ASGI app returned lifespan.startup.failed.')
    assert 'testing 123' in message

    assert not foo._shutdown_called
    assert not bar._startup_called
    assert not bar._shutdown_called
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
def test_multiple_handlers():
    counter = 0

    class HandlerA:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

    class HandlerB:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

    class HandlerC:
        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

    class HandlerD:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

    class HandlerE:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

        async def process_request(self, req, resp):
            self._called_request = True

    app = App()

    a = HandlerA()
    b = HandlerB()
    c = HandlerC()
    d = HandlerD()
    e = HandlerE()

    app.add_middleware([a, b, c, d, e])

    client = testing.TestClient(app)
    client.simulate_get()

    assert a._called_startup == 0
    assert b._called_startup == 1
    assert d._called_startup == 2
    assert e._called_startup == 3

    assert e._called_shutdown == 4
    assert c._called_shutdown == 5
    assert b._called_shutdown == 6

    assert e._called_request