Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test aiohttp #57

Merged
merged 1 commit into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions examples/aiohttp_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class GatewayProvider(Provider):


GatewayDepends = Annotated[Gateway, Depends()]
app = Application()
router = RouteTableDef()


Expand All @@ -34,7 +33,7 @@ async def endpoint(request: str, gateway: GatewayDepends) -> Response:
data = await gateway.get()
return Response(text=f'gateway data: {data}')


app = Application()
app.add_routes(router)
setup_dishka(GatewayProvider(), app=app)
setup_dishka(providers=[GatewayProvider()], app=app)
run_app(app)
26 changes: 15 additions & 11 deletions src/dishka/integrations/aiohttp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from typing import Callable, Final
__all__ = [
"Depends", "inject", "setup_dishka",
]

from typing import Callable, Final, Sequence

from aiohttp import web
from aiohttp.typedefs import Handler
Expand All @@ -7,17 +11,17 @@
from aiohttp.web_response import StreamResponse

from dishka import Provider, make_async_container
from dishka.async_container import AsyncContextWrapper
from dishka.integrations.base import wrap_injection
from dishka.async_container import AsyncContainer, AsyncContextWrapper
from dishka.integrations.base import Depends, wrap_injection

CONTAINER_KEY: Final = 'dishka_container'
CONTAINER_KEY: Final = web.AppKey('dishka_container', AsyncContainer)


def inject(func: Callable) -> Callable:
return wrap_injection(
func=func,
remove_depends=True,
container_getter=lambda p, _: p[0].app[CONTAINER_KEY],
container_getter=lambda p, _: p[0][CONTAINER_KEY],
is_async=True,
)

Expand All @@ -26,16 +30,16 @@ def inject(func: Callable) -> Callable:
async def container_middleware(
request: Request, handler: Handler,
) -> StreamResponse:
container = request.app['__container__']
async with container() as container_:
request.app[CONTAINER_KEY] = container_
container = request.app[CONTAINER_KEY]
async with container(context={Request: request}) as request_container:
request[CONTAINER_KEY] = request_container
res = await handler(request)
return res


def startup(wrapper_container: AsyncContextWrapper):
async def wrapper(app: Application) -> None:
app['__container__'] = await wrapper_container.__aenter__()
app[CONTAINER_KEY] = await wrapper_container.__aenter__()
return wrapper


Expand All @@ -45,8 +49,8 @@ async def wrapper(app: Application) -> None:
return wrapper


def setup_dishka(*provides: Provider, app: Application) -> None:
wrapper_container = make_async_container(*provides)
def setup_dishka(providers: Sequence[Provider], app: Application) -> None:
wrapper_container = make_async_container(*providers)
app.middlewares.append(container_middleware)
app.on_startup.append(startup(wrapper_container))
app.on_shutdown.append(shutdown(wrapper_container))
3 changes: 3 additions & 0 deletions tests/integrations/aiohttp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pytest

pytest.importorskip("aiohttp")
86 changes: 86 additions & 0 deletions tests/integrations/aiohttp/test_aiohttp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from contextlib import asynccontextmanager
from typing import Annotated
from unittest.mock import Mock

import pytest
from aiohttp.test_utils import TestClient, TestServer
from aiohttp.web_app import Application
from aiohttp.web_response import Response
from aiohttp.web_routedef import RouteTableDef

from dishka.integrations.aiohttp import (
Depends,
inject,
setup_dishka,
)
from ..common import (
APP_DEP_VALUE,
REQUEST_DEP_VALUE,
AppDep,
AppProvider,
RequestDep,
)


@asynccontextmanager
async def dishka_app(view, provider) -> TestClient:
app = Application()

router = RouteTableDef()
router.get("/")(inject(view))

app.add_routes(router)
setup_dishka(providers=[provider], app=app)
client = TestClient(TestServer(app))
await client.start_server()
yield client
await client.close()


async def get_with_app(
_,
a: Annotated[AppDep, Depends()],
mock: Annotated[Mock, Depends()],
) -> Response:
mock(a)
return Response(text='passed')


@pytest.mark.asyncio
async def test_app_dependency(app_provider: AppProvider):
async with dishka_app(get_with_app, app_provider) as client:
await client.get("/")
app_provider.mock.assert_called_with(APP_DEP_VALUE)
app_provider.app_released.assert_not_called()
app_provider.app_released.assert_called()


async def get_with_request(
_,
a: Annotated[RequestDep, Depends()],
mock: Annotated[Mock, Depends()],
) -> Response:
mock(a)
return Response(text='passed')



@pytest.mark.asyncio
async def test_request_dependency(app_provider: AppProvider):
async with dishka_app(get_with_request, app_provider) as client:
await client.get("/")
app_provider.mock.assert_called_with(REQUEST_DEP_VALUE)
app_provider.request_released.assert_called_once()


@pytest.mark.asyncio
async def test_request_dependency2(app_provider: AppProvider):
async with dishka_app(get_with_request, app_provider) as client:
await client.get("/")
app_provider.mock.assert_called_with(REQUEST_DEP_VALUE)
app_provider.mock.reset_mock()
app_provider.request_released.assert_called_once()
app_provider.request_released.reset_mock()
await client.get("/")
app_provider.mock.assert_called_with(REQUEST_DEP_VALUE)
app_provider.request_released.assert_called_once()
4 changes: 4 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ env_list =
unit,
real_world_example,
fastapi-{0096,0109},
aiohttp-393,
flask-302,
litestar-230,
aiogram-330,
Expand All @@ -16,6 +17,8 @@ addopts = --cov=dishka --cov-append --cov-report term-missing -v

[testenv]
deps =
aiohttp-393: -r requirements/aiohttp-393.txt
aiohttp-latest: -r requirements/aiohttp-latest.txt
fastapi-latest: -r requirements/fastapi-latest.txt
fastapi-0096: -r requirements/fastapi-0096.txt
fastapi-0109: -r requirements/fastapi-0109.txt
Expand All @@ -31,6 +34,7 @@ deps =
starlette-0270: -r requirements/starlette-0270.txt

commands =
aiohttp: pytest tests/integrations/aiohttp
fastapi: pytest tests/integrations/fastapi
aiogram: pytest tests/integrations/aiogram
telebot: pytest tests/integrations/telebot
Expand Down
Loading