Files
wehub-resource-sync 5296d0e97c
CI / Ban suppressions and legacy annotations (push) Has been cancelled
CI / pytest (push) Has been cancelled
CI / ruff-check (push) Has been cancelled
CI / ruff-format (push) Has been cancelled
CI / ty (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:44 +08:00

141 lines
4.2 KiB
Python

"""Tests for the pure-ASGI ingress correlation owner."""
import asyncio
from collections.abc import Iterator
from contextlib import contextmanager
from typing import cast
from unittest.mock import patch
import pytest
from fastapi import Request
from starlette.datastructures import Headers
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp, Message, Scope
from free_claude_code.api.request_ids import (
RequestCorrelationMiddleware,
get_request_id,
)
from tests.api.support import create_test_app
def _http_scope(path: str) -> Scope:
return cast(
Scope,
{
"type": "http",
"asgi": {"version": "3.0", "spec_version": "2.4"},
"http_version": "1.1",
"method": "POST",
"scheme": "http",
"path": path,
"raw_path": path.encode(),
"query_string": b"",
"headers": [(b"anthropic-session-id", b"session_test")],
"client": None,
"server": None,
},
)
def test_application_uses_the_pure_asgi_correlation_owner() -> None:
app = create_test_app()
middleware_classes = [middleware.cls for middleware in app.user_middleware]
assert sum(cls is RequestCorrelationMiddleware for cls in middleware_classes) == 1
assert all(cls is not BaseHTTPMiddleware for cls in middleware_classes)
@pytest.mark.asyncio
async def test_correlation_context_and_headers_span_the_complete_stream() -> None:
response_started = asyncio.Event()
allow_body = asyncio.Event()
sent: list[Message] = []
context_entries: list[dict[str, object]] = []
context_exits: list[dict[str, object]] = []
app_request_id: str | None = None
async def app(scope: Scope, _receive, send) -> None:
nonlocal app_request_id
app_request_id = get_request_id(Request(scope))
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [],
}
)
response_started.set()
await allow_body.wait()
await send(
{
"type": "http.response.body",
"body": b"done",
"more_body": False,
}
)
@contextmanager
def contextualize(**fields: object) -> Iterator[None]:
context_entries.append(fields)
try:
yield
finally:
context_exits.append(fields)
async def receive() -> Message:
raise AssertionError("Test application does not receive messages")
async def send(message: Message) -> None:
sent.append(message)
middleware = RequestCorrelationMiddleware(cast(ASGIApp, app))
with patch(
"free_claude_code.api.request_ids.logger.contextualize",
side_effect=contextualize,
):
request = asyncio.create_task(
middleware(_http_scope("/v1/responses"), receive, send)
)
await response_started.wait()
assert context_exits == []
assert app_request_id is not None
headers = Headers(raw=sent[0]["headers"])
assert headers["request-id"] == app_request_id
assert headers["x-request-id"] == app_request_id
assert context_entries == [
{
"http_method": "POST",
"http_path": "/v1/responses",
"claude_session_id": "session_test",
"request_id": app_request_id,
}
]
allow_body.set()
await request
assert context_exits == context_entries
@pytest.mark.asyncio
async def test_correlation_middleware_passes_non_http_scopes_unchanged() -> None:
observed_scope: Scope | None = None
async def app(scope: Scope, _receive, _send) -> None:
nonlocal observed_scope
observed_scope = scope
async def receive() -> Message:
return {"type": "lifespan.startup"}
async def send(_message: Message) -> None:
return None
scope = cast(Scope, {"type": "lifespan", "asgi": {"version": "3.0"}})
await RequestCorrelationMiddleware(cast(ASGIApp, app))(scope, receive, send)
assert observed_scope is scope
assert "state" not in scope