Skip to content

Commit

Permalink
DEV-19025: nox (#5)
Browse files Browse the repository at this point in the history
* DEV-19025: nox

* DEV-19025: nox

* DEV-19025: nox

* DEV-19025: codestyle

* [DEV-19025]: framework versions tests

* [DEV-19025]: testpaths

* DEV-19025: codestyle
  • Loading branch information
alexreznikoff authored Feb 12, 2025
1 parent 9014c7e commit f4223a7
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 49 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ jobs:
- name: Run Tests
run: pdm run pytest

test_frameworks_compability:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.11.7' ]

steps:
- uses: actions/checkout@v4
- name: Set up nox
uses: wntrblm/[email protected]
with:
python-version: ${{ matrix.python-version }}

- name: Run nox
run: nox

24 changes: 24 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import nox

DEPENDENCIES = [".", "pytest", "pytest_asyncio", "httpx"]


@nox.session()
@nox.parametrize("fastapi_version", ["0.66", "0.115.8"])
def test_fastapi_versions(session, fastapi_version):
session.install(*DEPENDENCIES, f"fastapi=={fastapi_version}")
session.run("pytest", "tests", "--framework=fastapi", "-k", "test_frameworks")


@nox.session()
@nox.parametrize("litestar_version", ["2.13.0", "2.14.0"])
def test_litestar_versions(session, litestar_version):
session.install(*DEPENDENCIES, f"litestar=={litestar_version}")
session.run("pytest", "tests", "--framework=litestar", "-k", "test_frameworks")


@nox.session()
@nox.parametrize("aiohttp_version", ["3.9.1", "3.11.12"])
def test_aiohttp_versions(session, aiohttp_version):
session.install(*DEPENDENCIES, f"aiohttp=={aiohttp_version}")
session.run("pytest", "tests", "--framework=aiohttp", "-k", "test_frameworks")
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ fmt = "ruff format ."
pythonpath = [
".", "src"
]
testpaths = ["tests"]
filterwarnings = [
"ignore:.*litestar.contrib.attrs.AttrsSchemaPlugin*:DeprecationWarning"
]
Expand Down
78 changes: 78 additions & 0 deletions tests/test_web_frameworks/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from enum import Enum
from typing import TYPE_CHECKING, Callable, Dict, Optional, Sequence, Union

import pytest

if TYPE_CHECKING:
from aiohttp.test_utils import TestClient as AiohttpTestClient
from httpx import AsyncClient as HttpxClient


def pytest_addoption(parser):
parser.addoption("--framework", action="store", help="run with framework")


def pytest_generate_tests(metafunc):
if "framework" in metafunc.fixturenames:
if metafunc.config.getoption("framework"):
frameworks = [metafunc.config.getoption("framework")]
else:
frameworks = ["fastapi", "aiohttp", "litestar"]
metafunc.parametrize("framework", frameworks)


class Framework(str, Enum):
aiohttp = "aiohttp"
fastapi = "fastapi"
litestar = "litestar"


APP_FACTORIES: Dict[Framework, Optional[Callable]] = {
Framework.fastapi: None,
Framework.aiohttp: None,
Framework.litestar: None,
}


def app_factory(framework: Framework):
factory = APP_FACTORIES[framework]
if factory:
return factory

if framework == Framework.aiohttp:
from tests.test_web_frameworks.aiohttp import aiohttp_app

APP_FACTORIES[framework] = aiohttp_app
elif framework == Framework.fastapi:
from tests.test_web_frameworks.fastapi import fastapi_app

APP_FACTORIES[framework] = fastapi_app
else:
from tests.test_web_frameworks.litestar import litestar_app

APP_FACTORIES[framework] = litestar_app

return APP_FACTORIES[framework]


@pytest.fixture
async def create_app(framework):
factory = app_factory(framework)
aiohttp_client = None

async def create_application(
include_routes: Optional[Sequence[str]] = None,
exclude_routes: Optional[Sequence[str]] = None,
) -> Union["AiohttpTestClient", "HttpxClient"]:
client = factory(include_routes=include_routes, exclude_routes=exclude_routes)
if framework == Framework.aiohttp:
# For aiohttp client implementation we need to start and stop server
nonlocal aiohttp_client
aiohttp_client = client
await client.start_server()
return client

yield create_application

if aiohttp_client:
await aiohttp_client.close()
60 changes: 11 additions & 49 deletions tests/test_web_frameworks/test_frameworks.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,31 @@
from contextlib import suppress
from enum import Enum
from typing import Dict, Optional, Sequence, Union

import pytest
from aiohttp import ClientResponse as AiohttpResponse
from aiohttp.test_utils import TestClient as AiohttpTestClient
from httpx import AsyncClient as HttpxClient
from httpx import Response as HttpxResponse
from inspect import iscoroutine
from typing import TYPE_CHECKING, Dict, Optional, Union

from prometheus_client.exposition import CONTENT_TYPE_LATEST

from huntflow_base_metrics.base import COMMON_LABELS_VALUES, REGISTRY
from tests.test_web_frameworks.aiohttp import aiohttp_app
from tests.test_web_frameworks.fastapi import fastapi_app
from tests.test_web_frameworks.litestar import litestar_app


class Framework(str, Enum):
aiohttp = "aiohttp"
fastapi = "fastapi"
litestar = "litestar"


factories = {
Framework.fastapi: fastapi_app,
Framework.aiohttp: aiohttp_app,
Framework.litestar: litestar_app,
}


@pytest.fixture(params=[Framework.fastapi, Framework.aiohttp, Framework.litestar])
async def create_app(request):
factory = factories[request.param]
aiohttp_client: Optional[AiohttpTestClient] = None

async def create_application(
include_routes: Optional[Sequence[str]] = None,
exclude_routes: Optional[Sequence[str]] = None,
) -> Union[AiohttpTestClient, HttpxClient]:
client = factory(include_routes=include_routes, exclude_routes=exclude_routes)
if request.param == Framework.aiohttp:
# For aiohttp client implementation we need to start and stop server
nonlocal aiohttp_client
aiohttp_client = client
await client.start_server()
return client

yield create_application

if aiohttp_client:
await aiohttp_client.close()
if TYPE_CHECKING:
from aiohttp import ClientResponse as AiohttpResponse
from httpx import Response as HttpxResponse


async def check_response(
resp: Union[HttpxResponse, AiohttpResponse],
resp: Union["HttpxResponse", "AiohttpResponse"],
expected_json: Optional[Dict] = None,
status: int = 200,
) -> None:
"""
There might be a httpx or aiohttp response with different behavior.
"""
if expected_json is not None:
json = await resp.json() if isinstance(resp, AiohttpResponse) else resp.json()
json = resp.json()
if iscoroutine(json):
json = await json
assert json == expected_json

status_code = resp.status if isinstance(resp, AiohttpResponse) else resp.status_code
status_code = resp.status if hasattr(resp, "status") else resp.status_code

assert status_code == status

Expand Down

0 comments on commit f4223a7

Please sign in to comment.