Skip to content
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
# Maintain dependencies for Python
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
day: saturday
time: "07:00"
24 changes: 12 additions & 12 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
-r requirements.txt
black
marshmallow
pytest
pytest-cov
pytest-sugar
pytest-aiohttp
codecov
sphinx
sphinx_issues
sphinx_rtd_theme
isort
typed-ast
anyio~=4.3.0
black~=24.4.0
codecov~=2.1.13
isort~=5.13.2
marshmallow~=3.21.1
pytest~=8.1.1
pytest-cov~=5.0.0
pytest-sugar~=1.0.0
pytest-aiohttp~=1.0.5
sphinx~=7.3.7
sphinx_issues~=4.1.0
sphinx_rtd_theme~=2.0.0
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
aiohttp>=3.0.1,<4.0
apispec>=5.1.1
webargs>=8.0.1
jinja2
aiohttp>=3.9.4,<4.0
apispec~=6.6.1
webargs~=8.4.0
jinja2~=3.1.3
19 changes: 16 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from typing import Any, Dict

import pytest
from aiohttp import web
from aiohttp.test_utils import TestClient
from marshmallow import EXCLUDE, INCLUDE, Schema, fields

from aiohttp_apispec import (
Expand All @@ -16,6 +19,11 @@
)


@pytest.fixture(scope="session")
def anyio_backend():
return "asyncio"


class HeaderSchema(Schema):
class Meta:
unknown = EXCLUDE
Expand Down Expand Up @@ -65,7 +73,7 @@ def __init__(self, message):


@pytest.fixture
def example_for_request_schema():
def example_for_request_schema() -> Dict[str, Any]:
return {
'id': 1,
'name': 'test',
Expand All @@ -85,7 +93,11 @@ def example_for_request_schema():
({"location": "querystring"}, False),
]
)
def aiohttp_app(loop, aiohttp_client, request, example_for_request_schema):
async def aiohttp_app(
aiohttp_client: TestClient,
request: pytest.FixtureRequest,
example_for_request_schema: Dict[str, Any],
):
location, nested = request.param

@docs(
Expand Down Expand Up @@ -235,4 +247,5 @@ async def validated_view(request: web.Request):
)
app.middlewares.extend([intercept_error, validation_middleware])

return loop.run_until_complete(aiohttp_client(app))
client = await aiohttp_client(app)
return client
7 changes: 6 additions & 1 deletion tests/test_documentation.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import json

import pytest
from aiohttp import web
from aiohttp.web_urldispatcher import StaticResource
from yarl import URL

from aiohttp_apispec import setup_aiohttp_apispec


def test_app_swagger_url(aiohttp_app):
@pytest.mark.anyio
async def test_app_swagger_url(aiohttp_app):
def safe_url_for(route):
if isinstance(route._resource, StaticResource):
# url_for on StaticResource requires filename arg
Expand All @@ -21,6 +23,7 @@ def safe_url_for(route):
assert URL("/v1/api/docs/api-docs") in urls


@pytest.mark.anyio
async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
resp = await aiohttp_app.get("/v1/api/docs/api-docs")
docs = await resp.json()
Expand Down Expand Up @@ -174,6 +177,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
)


@pytest.mark.anyio
async def test_not_register_route_for_none_url():
app = web.Application()
routes_count = len(app.router.routes())
Expand All @@ -182,6 +186,7 @@ async def test_not_register_route_for_none_url():
assert routes_count == routes_count_after_setup_apispec


@pytest.mark.anyio
async def test_register_route_for_relative_url():
app = web.Application()
routes_count = len(app.router.routes())
Expand Down
24 changes: 23 additions & 1 deletion tests/test_web_app.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import pytest


@pytest.mark.anyio
async def test_response_200_get(aiohttp_app):
res = await aiohttp_app.get("/v1/test", params={"id": 1, "name": "max"})
assert res.status == 200


@pytest.mark.anyio
async def test_response_400_get(aiohttp_app):
res = await aiohttp_app.get("/v1/test", params={"id": "string", "name": "max"})
assert res.status == 400
Expand All @@ -12,16 +17,19 @@ async def test_response_400_get(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_200_post(aiohttp_app):
res = await aiohttp_app.post("/v1/test", json={"id": 1, "name": "max"})
assert res.status == 200


@pytest.mark.anyio
async def test_response_200_post_callable_schema(aiohttp_app):
res = await aiohttp_app.post("/v1/test_call", json={"id": 1, "name": "max"})
assert res.status == 200


@pytest.mark.anyio
async def test_response_400_post(aiohttp_app):
res = await aiohttp_app.post("/v1/test", json={"id": "string", "name": "max"})
assert res.status == 400
Expand All @@ -31,6 +39,7 @@ async def test_response_400_post(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_400_post_unknown_toplevel_field(aiohttp_app):
# unknown_field is not a field in RequestSchema, default behavior is RAISE exception
res = await aiohttp_app.post(
Expand All @@ -43,6 +52,7 @@ async def test_response_400_post_unknown_toplevel_field(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_400_post_nested_fields(aiohttp_app):
payload = {
'nested_field': {
Expand All @@ -58,18 +68,21 @@ async def test_response_400_post_nested_fields(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_not_docked(aiohttp_app):
res = await aiohttp_app.get("/v1/other", params={"id": 1, "name": "max"})
assert res.status == 200


@pytest.mark.anyio
async def test_response_data_post(aiohttp_app):
res = await aiohttp_app.post(
"/v1/echo", json={"id": 1, "name": "max", "list_field": [1, 2, 3, 4]}
)
assert (await res.json()) == {"id": 1, "name": "max", "list_field": [1, 2, 3, 4]}


@pytest.mark.anyio
async def test_response_data_get(aiohttp_app):
res = await aiohttp_app.get(
"/v1/echo",
Expand All @@ -91,6 +104,7 @@ async def test_response_data_get(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_data_class_get(aiohttp_app):
res = await aiohttp_app.get(
"/v1/class_echo",
Expand All @@ -112,11 +126,13 @@ async def test_response_data_class_get(aiohttp_app):
}


@pytest.mark.anyio
async def test_response_data_class_post(aiohttp_app):
res = await aiohttp_app.post("/v1/class_echo")
assert res.status == 405


@pytest.mark.anyio
async def test_path_variable_described_correctly(aiohttp_app):
if aiohttp_app.app._subapps:
swag = aiohttp_app.app._subapps[0]["swagger_dict"]["paths"][
Expand All @@ -129,22 +145,26 @@ async def test_path_variable_described_correctly(aiohttp_app):
assert swag["get"]["parameters"][0]["schema"]["format"] == "uuid"


@pytest.mark.anyio
async def test_response_data_class_without_spec(aiohttp_app):
res = await aiohttp_app.delete("/v1/class_echo")
assert (await res.json()) == {"hello": "world"}


@pytest.mark.anyio
async def test_swagger_handler_200(aiohttp_app):
res = await aiohttp_app.get("/v1/api/docs/api-docs")
assert res.status == 200


@pytest.mark.anyio
async def test_match_info(aiohttp_app):
res = await aiohttp_app.get("/v1/variable/hello")
assert res.status == 200
assert await res.json() == {}
assert await res.json() == []


@pytest.mark.anyio
async def test_validators(aiohttp_app):
res = await aiohttp_app.post(
"/v1/validate/123456",
Expand Down Expand Up @@ -181,11 +201,13 @@ async def test_validators(aiohttp_app):
}


@pytest.mark.anyio
async def test_swagger_path(aiohttp_app):
res = await aiohttp_app.get("/v1/api/docs")
assert res.status == 200


@pytest.mark.anyio
async def test_swagger_static(aiohttp_app):
assert (await aiohttp_app.get("/static/swagger/swagger-ui.css")).status == 200 or (
await aiohttp_app.get("/v1/static/swagger/swagger-ui.css")
Expand Down