sanic icon indicating copy to clipboard operation
sanic copied to clipboard

Tests fail because of not validated indexing

Open SerGeRybakov opened this issue 3 years ago • 5 comments

After updating to latest versions tried to run simple test on a simple application.

# tmp.py
from sanic import Sanic
from sanic.response import json

app = Sanic('app')


@app.get('/')
async def hand1(req):
    return json({})


if __name__ == '__main__':
    app.run()
# conftest.py
from pytest import fixture
from sanic_testing.manager import TestManager, SanicASGITestClient

from tmp import app

@fixture()
def client() -> SanicASGITestClient:
    """Sanic test app."""
    manager = TestManager(app)
    yield manager.asgi_client
# test_app.py
import pytest
from sanic_testing.testing import SanicASGITestClient

async def test_sanic_app(client: SanicASGITestClient):
    req, res = await client.get("/")
    assert res.json == {}

Got the following exception trace:

============================= test session starts ==============================
collecting ... collected 1 item

test_app.py::test_sanic_app FAILED                                       [100%]
tests/test_app.py:4 (test_sanic_app)
client = <sanic_testing.testing.SanicASGITestClient object at 0x7f1f5bfa4610>

    async def test_sanic_app(client: SanicASGITestClient):
>       req, res = await client.get("/")

test_app.py:6: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../venv/lib/python3.10/site-packages/httpx/_client.py:1751: in get
    return await self.request(
../venv/lib/python3.10/site-packages/sanic_testing/testing.py:364: in request
    await self.sanic_app._startup()  # type: ignore
../venv/lib/python3.10/site-packages/sanic/app.py:1513: in _startup
    self.ext._display()
../venv/lib/python3.10/site-packages/sanic_ext/bootstrap.py:110: in _display
    f"  > {extension.name} {extension.render_label()}"
../venv/lib/python3.10/site-packages/sanic_ext/extensions/base.py:56: in render_label
    label = self.label()
../venv/lib/python3.10/site-packages/sanic_ext/extensions/openapi/extension.py:25: in label
    return self._make_url()
../venv/lib/python3.10/site-packages/sanic_ext/extensions/openapi/extension.py:34: in _make_url
    else self.app.serve_location
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Sanic(name="app")

    @property
    def serve_location(self) -> str:
>       server_settings = self.state.server_info[0].settings
E       IndexError: list index out of range

../venv/lib/python3.10/site-packages/sanic/mixins/runner.py:574: IndexError



============================== 1 failed in 0.10s ===============================

It seems that this line server_settings = self.state.server_info[0].settings should be inside a validation clause:

    @property
    def serve_location(self) -> str:
        if self.state.server_info:
            server_settings = self.state.server_info[0].settings
        else:
            server_settings = None
        return self.get_server_location(server_settings)

In this case test runs successfully.

SerGeRybakov avatar Jul 04 '22 03:07 SerGeRybakov

Python 3.10.5 pytest 7.1.2 pytest-asyncio 0.18.3 sanic 22.6.0 sanic-ext 22.6.1 sanic-routing 22.3.0 sanic-testing 22.6.0

SerGeRybakov avatar Jul 04 '22 03:07 SerGeRybakov

Yes, that makes sense. Probably even better we need to be setting that object in ASGI. I made a similar change in the reusable client in sanic-testing.

ahopkins avatar Jul 04 '22 03:07 ahopkins

Adding app.prepare() appears to correct the issue as well

import pytest
from sanic import Sanic, response

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

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

    sanic_app.prepare() # Fixes IndexError: list index out of range
    return sanic_app

@pytest.mark.asyncio
async def test_basic_asgi_client(app):
    request, response = await app.asgi_client.get("/")

    assert request.method.lower() == "get"
    assert response.body == b"foo"
    assert response.status == 200

notzippy avatar Jul 15 '22 19:07 notzippy

Adding app.prepare() appears to correct the issue as well

import pytest
from sanic import Sanic, response

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

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

    sanic_app.prepare() # Fixes IndexError: list index out of range
    return sanic_app

@pytest.mark.asyncio
async def test_basic_asgi_client(app):
    request, response = await app.asgi_client.get("/")

    assert request.method.lower() == "get"
    assert response.body == b"foo"
    assert response.status == 200

O_o what's the sort of magic?!?

SerGeRybakov avatar Jul 15 '22 23:07 SerGeRybakov

O_o what's the sort of magic?!?

https://docs.pytest.org/en/7.1.x/how-to/fixtures.html

ahopkins avatar Jul 31 '22 11:07 ahopkins