Skip to content

feat(testing): build_test_client(platform) async context manager#554

Merged
bokelley merged 1 commit into
mainfrom
claude/issue-549-build-test-client
May 4, 2026
Merged

feat(testing): build_test_client(platform) async context manager#554
bokelley merged 1 commit into
mainfrom
claude/issue-549-build-test-client

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 4, 2026

Closes #549

Summary

Adds build_test_client(platform) to adcp.testing — a single async with block that replaces the 4-line boilerplate every in-process integration test previously needed:

# Before
app = build_asgi_app(platform)
async with LifespanManager(app):
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test") as client:
        resp = await client.post("/mcp/", json=...)

# After
async with build_test_client(platform) as client:
    resp = await client.post("/mcp/", json=...)

Also adds allowed_hosts: Sequence[str] | None = None to build_asgi_app so callers can configure FastMCP's transport-security layer directly. build_test_client sets this automatically from the base_url hostname.

Key implementation notes:

  • asgi_lifespan is lazy-imported with an actionable ImportError message (pip install 'adcp[dev]')
  • build_asgi_app runs inside asyncio.to_thread() because validate_capabilities_response_shape calls asyncio.run() internally, which fails if a loop is already running in the calling async test
  • follow_redirects=True default matches all existing ASGI test fixtures (FastMCP can issue a 307 on /mcp/mcp/)
  • headers= accepted as Mapping[str, str] | None and forwarded directly to httpx.AsyncClient

Nits surfaced in pre-PR review (not fixed — low priority):

  • asgi-lifespan lives in [dev] rather than a [test] extra; the ImportError message will need updating if the extra is renamed

What was tested

  • pytest tests/test_testing_decisioning.py — 20 passed (12 existing + 8 new)
  • ruff check src/adcp/testing/ tests/test_testing_decisioning.py — clean
  • Includes end-to-end test: test_build_test_client_can_make_request makes a real MCP initialize call through the in-process ASGI stack and asserts HTTP 200
  • Includes ImportError test: patches asgi_lifespan out of sys.modules and asserts the error message fires

Pre-PR review:

  • code-reviewer: approved — no blockers; all raised issues fixed before push
  • dx-expert: approved — asyncio.to_thread pattern is correct, DX score 5/5 on time-to-hello-world

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See adcp#3121
for context.

Session: https://claude.ai/code/session_01UZyqrAkoJPCEz5VLerzxFv


Generated by Claude Code

@bokelley bokelley force-pushed the claude/issue-549-build-test-client branch 2 times, most recently from d659bd4 to ca55abe Compare May 4, 2026 08:53
@bokelley bokelley marked this pull request as ready for review May 4, 2026 08:59
Closes #549

Adds build_test_client(platform) to adcp.testing — a single async
context manager that wraps build_asgi_app + LifespanManager +
httpx.AsyncClient, eliminating 4 lines of boilerplate from every
in-process integration test.

Also adds allowed_hosts parameter to build_asgi_app so callers can
configure FastMCP's transport-security layer without manual server
manipulation.

https://claude.ai/code/session_01UZyqrAkoJPCEz5VLerzxFv
@bokelley bokelley force-pushed the claude/issue-549-build-test-client branch from ca55abe to 65134a8 Compare May 4, 2026 13:09
@bokelley bokelley merged commit 5f038e5 into main May 4, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(testing): build_test_client(platform) — async context manager wrapping lifespan + httpx client

2 participants