|
4 | 4 |
|
5 | 5 | import asyncio |
6 | 6 | import json |
7 | | -from typing import Annotated |
| 7 | +from typing import Annotated, Any |
8 | 8 |
|
9 | 9 | import pytest |
10 | 10 | from pydantic import BaseModel, Field |
|
17 | 17 | pytestmark = pytest.mark.filterwarnings("ignore:HttpResolverAlpha is an alpha feature") |
18 | 18 |
|
19 | 19 |
|
| 20 | +# ============================================================================= |
| 21 | +# ASGI Test Helpers |
| 22 | +# ============================================================================= |
| 23 | + |
| 24 | + |
| 25 | +def make_asgi_receive(body: bytes = b""): |
| 26 | + """Create an ASGI receive callable.""" |
| 27 | + |
| 28 | + async def receive() -> dict[str, Any]: |
| 29 | + await asyncio.sleep(0) |
| 30 | + return {"type": "http.request", "body": body, "more_body": False} |
| 31 | + |
| 32 | + return receive |
| 33 | + |
| 34 | + |
| 35 | +def make_asgi_send(): |
| 36 | + """Create an ASGI send callable that captures response.""" |
| 37 | + captured: dict[str, Any] = {"status_code": None, "body": b""} |
| 38 | + |
| 39 | + async def send(message: dict[str, Any]) -> None: |
| 40 | + await asyncio.sleep(0) |
| 41 | + if message["type"] == "http.response.start": |
| 42 | + captured["status_code"] = message["status"] |
| 43 | + elif message["type"] == "http.response.body": |
| 44 | + captured["body"] = message["body"] |
| 45 | + |
| 46 | + return send, captured |
| 47 | + |
| 48 | + |
20 | 49 | class UserModel(BaseModel): |
21 | 50 | name: str = Field(min_length=1, max_length=100) |
22 | 51 | age: int = Field(ge=0, le=150) |
@@ -198,26 +227,15 @@ async def create_user(user: UserModel) -> UserResponse: |
198 | 227 | "headers": [(b"content-type", b"application/json")], |
199 | 228 | } |
200 | 229 |
|
201 | | - request_body = b'{"name": "AsyncUser", "age": 25}' |
202 | | - response_body = b"" |
203 | | - status_code = None |
204 | | - |
205 | | - async def receive(): |
206 | | - return {"type": "http.request", "body": request_body, "more_body": False} |
207 | | - |
208 | | - async def send(message): |
209 | | - nonlocal response_body, status_code |
210 | | - if message["type"] == "http.response.start": |
211 | | - status_code = message["status"] |
212 | | - elif message["type"] == "http.response.body": |
213 | | - response_body = message["body"] |
| 230 | + receive = make_asgi_receive(b'{"name": "AsyncUser", "age": 25}') |
| 231 | + send, captured = make_asgi_send() |
214 | 232 |
|
215 | 233 | # WHEN called via ASGI interface |
216 | 234 | await app(scope, receive, send) |
217 | 235 |
|
218 | 236 | # THEN validation works with async handler |
219 | | - assert status_code == 200 |
220 | | - body = json.loads(response_body) |
| 237 | + assert captured["status_code"] == 200 |
| 238 | + body = json.loads(captured["body"]) |
221 | 239 | assert body["id"] == "async-123" |
222 | 240 | assert body["user"]["name"] == "AsyncUser" |
223 | 241 |
|
|
0 commit comments