Skip to content

Commit 0bd1b20

Browse files
committed
backends tests
1 parent 0dc3fcd commit 0bd1b20

18 files changed

Lines changed: 361 additions & 25 deletions

extapi/http/abc.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import abc
2+
from collections.abc import Mapping
23
from typing import Any, Generic, Protocol, Self, TypeVar, runtime_checkable
34

45
from multidict import CIMultiDict
@@ -42,7 +43,7 @@ async def get(
4243
params: dict[str, str] | None = None,
4344
json: Any = None,
4445
data: Any = None,
45-
headers: CIMultiDict | None = None,
46+
headers: CIMultiDict | Mapping[str, Any] | None = None,
4647
timeout: Any | float | None = None,
4748
**kwargs,
4849
) -> Response[T_co]:
@@ -53,7 +54,7 @@ async def get(
5354
params=params,
5455
json=json,
5556
data=data,
56-
headers=headers,
57+
headers=_map_headers(headers),
5758
timeout=timeout,
5859
kwargs=kwargs,
5960
)
@@ -66,7 +67,7 @@ async def post(
6667
params: dict[str, str] | None = None,
6768
json: Any = None,
6869
data: Any = None,
69-
headers: CIMultiDict | None = None,
70+
headers: CIMultiDict | Mapping[str, Any] | None = None,
7071
timeout: Any | float | None = None,
7172
**kwargs,
7273
) -> Response[T_co]:
@@ -77,7 +78,7 @@ async def post(
7778
params=params,
7879
json=json,
7980
data=data,
80-
headers=headers,
81+
headers=_map_headers(headers),
8182
timeout=timeout,
8283
kwargs=kwargs,
8384
)
@@ -90,7 +91,7 @@ async def delete(
9091
params: dict[str, str] | None = None,
9192
json: Any = None,
9293
data: Any = None,
93-
headers: CIMultiDict | None = None,
94+
headers: CIMultiDict | Mapping[str, Any] | None = None,
9495
timeout: Any | float | None = None,
9596
**kwargs,
9697
) -> Response[T_co]:
@@ -101,7 +102,7 @@ async def delete(
101102
params=params,
102103
json=json,
103104
data=data,
104-
headers=headers,
105+
headers=_map_headers(headers),
105106
timeout=timeout,
106107
kwargs=kwargs,
107108
)
@@ -114,7 +115,7 @@ async def put(
114115
params: dict[str, str] | None = None,
115116
json: Any = None,
116117
data: Any = None,
117-
headers: CIMultiDict | None = None,
118+
headers: CIMultiDict | Mapping[str, Any] | None = None,
118119
timeout: Any | float | None = None,
119120
**kwargs,
120121
) -> Response[T_co]:
@@ -125,7 +126,7 @@ async def put(
125126
params=params,
126127
json=json,
127128
data=data,
128-
headers=headers,
129+
headers=_map_headers(headers),
129130
timeout=timeout,
130131
kwargs=kwargs,
131132
)
@@ -138,7 +139,7 @@ async def patch(
138139
params: dict[str, str] | None = None,
139140
json: Any = None,
140141
data: Any = None,
141-
headers: CIMultiDict | None = None,
142+
headers: CIMultiDict | Mapping[str, Any] | None = None,
142143
timeout: Any | float | None = None,
143144
**kwargs,
144145
) -> Response[T_co]:
@@ -149,13 +150,23 @@ async def patch(
149150
params=params,
150151
json=json,
151152
data=data,
152-
headers=headers,
153+
headers=_map_headers(headers),
153154
timeout=timeout,
154155
kwargs=kwargs,
155156
)
156157
)
157158

158159

160+
def _map_headers(headers: CIMultiDict | Mapping[str, Any] | None) -> CIMultiDict | None:
161+
if headers is None:
162+
return None
163+
164+
if isinstance(headers, CIMultiDict):
165+
return headers
166+
167+
return CIMultiDict(headers)
168+
169+
159170
@runtime_checkable
160171
class Retryable(Protocol[T_contr]):
161172
async def need_retry(

extapi/http/backends/aiohttp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ async def execute(self, request: RequestData) -> Response[aiohttp.ClientResponse
105105
)
106106

107107
return Response[aiohttp.ClientResponse](
108+
method=request.method,
108109
url=request.url,
109110
status=response.status,
110111
headers=response.headers.copy(),

extapi/http/backends/httpx.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ class HttpxExecutor(AbstractExecutor[httpx.Response], metaclass=abc.ABCMeta):
4646
def __init__(
4747
self,
4848
*,
49-
check_ssl: bool = True,
49+
ssl: bool = True,
5050
default_timeout: float = 10.0,
5151
follow_redirects: bool = True,
5252
**kwargs,
5353
):
5454
super().__init__()
5555
verify = kwargs.pop("verify", None)
5656
if verify is None:
57-
verify = check_ssl
57+
verify = ssl
5858
self._client = self._make_client(
5959
verify=verify, follow_redirects=follow_redirects, **kwargs
6060
)
@@ -96,6 +96,7 @@ async def execute(self, request: RequestData) -> Response[httpx.Response]:
9696
).__aenter__()
9797

9898
return Response[httpx.Response](
99+
method=request.method,
99100
url=request.url,
100101
status=response.status_code,
101102
headers=CIMultiDict(response.headers),

extapi/http/executors/wrapped.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,16 @@ def __init__(self, executor: AbstractExecutor[T]):
1414

1515
async def execute(self, request: RequestData) -> Response[T]:
1616
return await self._executor.execute(request)
17+
18+
19+
def unwrap_executor(executor: AbstractExecutor[T]) -> AbstractExecutor[T]:
20+
seen = set()
21+
while True:
22+
if executor in seen:
23+
raise RuntimeError("Circular reference in executors detected")
24+
25+
if isinstance(executor, WrappedExecutor):
26+
seen.add(executor)
27+
executor = executor._executor
28+
else:
29+
return executor

extapi/http/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ async def json(
5959

6060
@dataclass(kw_only=True)
6161
class Response(Generic[T]):
62+
method: str
6263
url: URL
6364
status: int
6465
headers: CIMultiDict = field(default_factory=lambda: CIMultiDict())

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ prometheus = [
4949
tests = [
5050
"pytest",
5151
"pytest-asyncio",
52+
"pytest-aiohttp",
5253
"coverage",
5354
]
5455

tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
from collections.abc import AsyncIterable
12
from typing import Any
23

34
import pytest
5+
from aiohttp import web
6+
from aiohttp.test_utils import TestServer
47
from multidict import CIMultiDict
58
from yarl import URL
69

@@ -32,8 +35,24 @@ def request_filled() -> RequestData:
3235
@pytest.fixture
3336
def response_simple(request_simple: RequestData) -> Response[Any]:
3437
return Response(
38+
method=request_simple.method,
3539
url=request_simple.url,
3640
status=200,
3741
backend_response=DummyBackendResponse(),
3842
_data=b"",
3943
)
44+
45+
46+
@pytest.fixture
47+
async def dummy_server(
48+
aiohttp_unused_port, aiohttp_server
49+
) -> AsyncIterable[TestServer]:
50+
app = web.Application()
51+
52+
async def get(request):
53+
return web.json_response({"status": "ok"}, headers=request.headers)
54+
55+
app.router.add_get("/get", get)
56+
57+
server = await aiohttp_server(app, port=aiohttp_unused_port())
58+
yield server

tests/exthttp/_helpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(self, status: int = 200):
2323
async def execute(self, request: RequestData) -> Response[bytes]:
2424
return Response(
2525
status=self._status,
26+
method=request.method,
2627
url=request.url,
2728
backend_response=DummyBackendResponse(),
2829
)

tests/exthttp/addons/test_auth.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def getter() -> str:
8181
addon = BearerAuthAddon[Any](getter)
8282
need_retry, timeout = await addon.need_retry(
8383
Response(
84+
method=request_simple.method,
8485
url=request_simple.url,
8586
status=200,
8687
backend_response=DummyBackendResponse(),
@@ -97,6 +98,7 @@ def getter() -> str:
9798
addon = BearerAuthAddon[Any](getter)
9899
need_retry, timeout = await addon.need_retry(
99100
Response(
101+
method=request_simple.method,
100102
url=request_simple.url,
101103
status=401,
102104
backend_response=DummyBackendResponse(),
@@ -119,6 +121,7 @@ async def test_need_retry(self, request_simple: RequestData):
119121
addon = StaticBearerAuthAddon[Any]("test-token")
120122
need_retry, timeout = await addon.need_retry(
121123
Response(
124+
method=request_simple.method,
122125
url=request_simple.url,
123126
status=401,
124127
backend_response=DummyBackendResponse(),
@@ -148,6 +151,7 @@ async def test_need_retry(self, request_simple: RequestData):
148151
addon = StaticBasicAuthAddon[Any](login=login, password=password)
149152
need_retry, timeout = await addon.need_retry(
150153
Response(
154+
method=request_simple.method,
151155
url=request_simple.url,
152156
status=401,
153157
backend_response=DummyBackendResponse(),

tests/exthttp/addons/test_retry.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ async def test_ok(self, status: int):
1515
addon = Retry5xxAddon[Any]()
1616

1717
response = Response(
18+
method="GET",
1819
url=URL("http://example.com"),
1920
status=status,
2021
backend_response=DummyBackendResponse(),
@@ -31,6 +32,7 @@ async def test_5xx(self, status: int):
3132
addon = Retry5xxAddon[Any]()
3233

3334
response = Response(
35+
method="GET",
3436
url=URL("http://example.com"),
3537
status=status,
3638
backend_response=DummyBackendResponse(),
@@ -55,6 +57,7 @@ async def test_429_no_header(self):
5557
addon = Retry429Addon[Any]()
5658

5759
response = Response(
60+
method="GET",
5861
url=URL("http://example.com"),
5962
status=429,
6063
backend_response=DummyBackendResponse(),
@@ -69,6 +72,7 @@ async def test_429_with_header(self):
6972
addon = Retry429Addon[Any]()
7073

7174
response = Response(
75+
method="GET",
7276
url=URL("http://example.com"),
7377
status=429,
7478
backend_response=DummyBackendResponse(),
@@ -84,6 +88,7 @@ async def test_429_with_header_invalid(self):
8488
addon = Retry429Addon[Any]()
8589

8690
response = Response(
91+
method="GET",
8792
url=URL("http://example.com"),
8893
status=429,
8994
backend_response=DummyBackendResponse(),

0 commit comments

Comments
 (0)