sanic-org / sanic-testing

Test clients for Sanic
https://sanic.dev/en/plugins/sanic-testing/getting-started.html
MIT License
31 stars 19 forks source link

KeyError when used multiple apps and ReusableClient #59

Closed zerc closed 1 year ago

zerc commented 1 year ago

Error

[2022-11-02 16:00:05 +0000] [82325] [ERROR] Experienced exception while trying to serve
Traceback (most recent call last):
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/mixins/startup.py", line 921, in serve_single
    worker_serve(monitor_publisher=None, **kwargs)
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/worker/serve.py", line 106, in worker_serve
    return _serve_http_1(
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/server/runners.py", line 231, in _serve_http_1
    loop.run_until_complete(app._server_event("init", "before"))
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/app.py", line 1549, in _server_event
    await self.dispatch(
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/signals.py", line 197, in dispatch
    return await dispatch
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/signals.py", line 167, in _dispatch
    retval = await maybe_coroutine
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/app.py", line 1140, in _listener
    await maybe_coro
  File "/Users/vsavin/repos/sanic_testing/venv/lib/python3.8/site-packages/sanic/mixins/startup.py", line 1056, in _start_servers
    if not server_info.settings["loop"]:
KeyError: 'loop'

How to reproduce

  1. Create a new env with the following dependencies:
sanic = "22.9.0"
pytest = "^7.2.0"
sanic-testing = "==22.9.0"
  1. Create a test file like this:
import pytest
from sanic import Sanic, response
from sanic_testing.reusable import ReusableClient

@pytest.fixture
def app():
    sanic_app = Sanic("app")

    @sanic_app.get("/")
    def basic(request):
        return response.text("foo")

    yield sanic_app

@pytest.fixture
def client(app):
    client = ReusableClient(app, port=9999)
    client.run()
    yield client
    client.stop()

@pytest.fixture()
def app_2():
    app = Sanic("app_2")

    @app.route("/")
    def handler(request):
        return response.text("OK")

    yield app

def test_example_1(client):
    _, response = client.get("/")

    assert response.body == b"foo"
    assert response.status == 200

def test_example_2(app_2):
    _, response = app_2.test_client.get("/")

    assert response.body == b"OK"
    assert response.status == 200

Notes

I'm not familiar with the code but it looks like a bug in ReusableClient - it creates ApplicationServerInfo with the default settings dictionary which misses the loop key there.

The tests pass if I change that to:

self.app.state.server_info.append(
    ApplicationServerInfo(
        settings={
            "version": "1.1",
            "ssl": None,
            "unix": None,
            "sock": None,
            "host": self.host,
            "port": self.port,
            "loop": None,
        }
    )
)