Repository: litestar-org/litestar

Issue:
Dependency: Brotli missing from test dependencies
All `full` extras dependencies are available in dev-deps, except for `brotli`.

To reproduce:

```shell
$ poetry env remove python
$ poetry install
$ poetry run pytest .
```

This should mean we don't need to require `poetry install -extras full` in `CONTRIBUTING.md` and `ci.yaml`



Discussion:

Resolving patch:
diff --git a/examples/__init__.py b/examples/__init__.py
new file mode 100644
diff --git a/examples/hello_world.py b/examples/hello_world.py
new file mode 100644
--- /dev/null
+++ b/examples/hello_world.py
@@ -0,0 +1,11 @@
+"""Minimal Starlite application."""
+from starlite import Starlite, get
+
+
+@get("/")
+def hello_world() -> dict[str, str]:
+    """Hi."""
+    return {"hello": "world"}
+
+
+app = Starlite(route_handlers=[hello_world])
diff --git a/examples/startup_and_shutdown.py b/examples/startup_and_shutdown.py
new file mode 100644
--- /dev/null
+++ b/examples/startup_and_shutdown.py
@@ -0,0 +1,33 @@
+from typing import cast
+
+from pydantic import BaseSettings
+from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
+
+from starlite import Starlite, State
+
+
+class AppSettings(BaseSettings):
+    DATABASE_URI: str = "postgresql+asyncpg://postgres:mysecretpassword@pg.db:5432/db"
+
+
+settings = AppSettings()
+
+
+def get_db_connection(state: State) -> AsyncEngine:
+    """Returns the db engine.
+
+    If it doesn't exist, creates it and saves it in on the application
+    state object
+    """
+    if not getattr(state, "engine", None):
+        state.engine = create_async_engine(settings.DATABASE_URI)
+    return cast("AsyncEngine", state.engine)
+
+
+async def close_db_connection(state: State) -> None:
+    """Closes the db connection stored in the application State object."""
+    if getattr(state, "engine", None):
+        await cast("AsyncEngine", state.engine).dispose()
+
+
+app = Starlite(route_handlers=[], on_startup=[get_db_connection], on_shutdown=[close_db_connection])


Test patch:
diff --git a/examples/tests/__init__.py b/examples/tests/__init__.py
new file mode 100644
diff --git a/examples/tests/test_hello_world.py b/examples/tests/test_hello_world.py
new file mode 100644
--- /dev/null
+++ b/examples/tests/test_hello_world.py
@@ -0,0 +1,11 @@
+from starlette.status import HTTP_200_OK
+
+from examples import hello_world
+from starlite.testing import TestClient
+
+
+def test_hello_world_example() -> None:
+    with TestClient(app=hello_world.app) as client:
+        r = client.get("/")
+    assert r.status_code == HTTP_200_OK
+    assert r.json() == {"hello": "world"}
diff --git a/examples/tests/test_startup_and_shutdown.py b/examples/tests/test_startup_and_shutdown.py
new file mode 100644
--- /dev/null
+++ b/examples/tests/test_startup_and_shutdown.py
@@ -0,0 +1,29 @@
+from typing import TYPE_CHECKING
+from unittest.mock import AsyncMock, MagicMock
+
+from examples import startup_and_shutdown
+from starlite import State, get
+from starlite.testing import TestClient
+
+if TYPE_CHECKING:
+    from pytest import MonkeyPatch  # noqa:PT013
+
+
+class FakeAsyncEngine:
+    dispose = AsyncMock()
+
+
+async def test_startup_and_shutdown_example(monkeypatch: "MonkeyPatch") -> None:
+
+    monkeypatch.setattr(startup_and_shutdown, "create_async_engine", MagicMock(return_value=FakeAsyncEngine))
+
+    @get("/")
+    def handler(state: State) -> None:
+        assert state.engine is FakeAsyncEngine
+        return None
+
+    startup_and_shutdown.app.register(handler)
+
+    with TestClient(app=startup_and_shutdown.app) as client:
+        client.get("/")
+    FakeAsyncEngine.dispose.assert_awaited_once()