|
9 | 9 | See: https://github.com/modelcontextprotocol/python-sdk/issues/1269 |
10 | 10 | """ |
11 | 11 |
|
12 | | -import logging |
13 | | -from collections.abc import AsyncGenerator |
14 | | -from contextlib import asynccontextmanager |
15 | | - |
16 | | -import anyio |
17 | 12 | import httpx |
18 | 13 | import pytest |
19 | 14 | from starlette.applications import Starlette |
|
24 | 19 |
|
25 | 20 |
|
26 | 21 | def _create_app(*, stateless: bool) -> Starlette: |
27 | | - """Create a minimal Starlette app backed by a StreamableHTTPSessionManager.""" |
| 22 | + """Create a minimal Starlette app backed by a StreamableHTTPSessionManager. |
| 23 | +
|
| 24 | + No lifespan is needed because unsupported methods are rejected before |
| 25 | + the session manager checks for a running task group. |
| 26 | + """ |
28 | 27 | server = Server("test_head_crash") |
29 | 28 | session_manager = StreamableHTTPSessionManager( |
30 | 29 | app=server, |
31 | 30 | stateless=stateless, |
32 | 31 | ) |
33 | 32 |
|
34 | | - @asynccontextmanager |
35 | | - async def lifespan(app: Starlette) -> AsyncGenerator[None, None]: |
36 | | - async with session_manager.run(): |
37 | | - yield |
38 | | - |
39 | 33 | return Starlette( |
40 | 34 | routes=[Mount("/", app=session_manager.handle_request)], |
41 | | - lifespan=lifespan, |
42 | 35 | ) |
43 | 36 |
|
44 | 37 |
|
45 | 38 | @pytest.mark.anyio |
46 | 39 | @pytest.mark.parametrize("stateless", [True, False]) |
47 | | -async def test_head_request_returns_405_without_error( |
48 | | - stateless: bool, |
49 | | - caplog: pytest.LogCaptureFixture, |
50 | | -) -> None: |
51 | | - """HEAD / must return 405 and must not produce ClosedResourceError.""" |
| 40 | +async def test_head_request_returns_405(stateless: bool) -> None: |
| 41 | + """HEAD / must return 405 without creating a transport.""" |
52 | 42 | app = _create_app(stateless=stateless) |
53 | 43 |
|
54 | | - with caplog.at_level(logging.ERROR): |
55 | | - async with httpx.AsyncClient( |
56 | | - transport=httpx.ASGITransport(app=app), |
57 | | - base_url="http://testserver", |
58 | | - timeout=5.0, |
59 | | - ) as client: |
60 | | - response = await client.head("/") |
61 | | - assert response.status_code == 405 |
62 | | - |
63 | | - # Give any lingering background tasks a chance to log errors |
64 | | - await anyio.sleep(0.3) |
65 | | - |
66 | | - # Ensure no ClosedResourceError was logged |
67 | | - for record in caplog.records: |
68 | | - msg = record.getMessage() |
69 | | - assert "ClosedResourceError" not in msg, f"ClosedResourceError found in logs: {msg}" |
70 | | - assert "Error in message router" not in msg, f"Message router error found in logs: {msg}" |
| 44 | + async with httpx.AsyncClient( |
| 45 | + transport=httpx.ASGITransport(app=app), |
| 46 | + base_url="http://testserver", |
| 47 | + timeout=5.0, |
| 48 | + ) as client: |
| 49 | + response = await client.head("/") |
| 50 | + assert response.status_code == 405 |
71 | 51 |
|
72 | 52 |
|
73 | 53 | @pytest.mark.anyio |
|
0 commit comments