BlackSheep icon indicating copy to clipboard operation
BlackSheep copied to clipboard

unconsistent behavior between uvicorn and pytest

Open ticapix opened this issue 1 month ago • 2 comments

Hello,

In a server.py, I have a simple health check

# in server.py

from datetime import datetime, timezone
from blacksheep import Application, get, ok

app = Application()

@get("/health")
async def health():
    return ok(f"{datetime.now(timezone.utc).isoformat()}")

and reusing the TestClient from https://www.neoteroi.dev/blacksheep/testing/#using-the-testclient-with-pytest, I have a simple test

# in test_api.py

import pytest
from blacksheep.testing import TestClient

@pytest.mark.asyncio(scope="session")
async def test_health_check(test_client: TestClient) -> None:
    response = await test_client.get("/health")
    assert response is not None
    assert response.status is 200
    print(response.headers)
    assert response.has_header(b'content-type')

Bug

When running with pytest -sv, I'm getting an error because response.headers is empty.

$ PYTHONPATH=. pytest -sv
============================= test session starts ==============================
platform linux -- Python 3.11.2, pytest-8.2.2, pluggy-1.5.0 -- /home/debian/workspace/cde/cde-global/applications/ontology-server/.direnv/python-3.11.2/bin/python3
cachedir: .pytest_cache
rootdir: /home/debian/workspace/cde/cde-global/applications/ontology-server
plugins: asyncio-0.23.7
asyncio: mode=Mode.STRICT
collected 1 item

tests/test_api.py::test_health_check <Headers []>
FAILED

=================================== FAILURES ===================================
______________________________ test_health_check _______________________________

test_client = <blacksheep.testing.client.TestClient object at 0x7f2d176763d0>

    @pytest.mark.asyncio(scope="session")
    async def test_health_check(test_client: TestClient) -> None:
        response = await test_client.get("/health")
        assert response is not None
        assert response.status is 200
        print(response.headers)
>       assert response.has_header(b'content-type')
E       AssertionError: assert False
E        +  where False = <bound method Message.has_header of <Response 200>>(b'content-type')
E        +    where <bound method Message.has_header of <Response 200>> = <Response 200>.has_header

tests/test_api.py:13: AssertionError
=========================== short test summary info ============================
FAILED tests/test_api.py::test_health_check - AssertionError: assert False
============================== 1 failed in 0.04s ===============================

Expected

However, when I run the server with uvicorn server:app and I try with wget, I do get a content-type header.

$ wget -qS -O - http://127.0.0.1:8000/health
  HTTP/1.1 200 OK
  date: Sun, 23 Jun 2024 21:11:01 GMT
  server: uvicorn
  content-type: text/plain; charset=utf-8
  content-length: 32
2024-06-23T21:11:02.151666+00:00

Has anyone an idea of the component/code adding this header after I return my response object and not present when testing ?

Thank you, Pierre

ticapix avatar Jun 23 '24 21:06 ticapix