[v1.x] tests: backport interaction-model suite from main (527 tests, src/ untouched)#2717
Draft
maxisbey wants to merge 18 commits into
Draft
[v1.x] tests: backport interaction-model suite from main (527 tests, src/ untouched)#2717maxisbey wants to merge 18 commits into
maxisbey wants to merge 18 commits into
Conversation
Excludes tests/interaction from pyright until the backport restores type correctness phase by phase.
…ses, RecordingTransport→Recording stream-pair wrapper
…ker registration - conftest.py: add pytest_configure registering the 'requirement' marker (round-1 adv-3 S1 fix; without this every file is a PytestUnknownMarkWarning collection error under filterwarnings=['error']). - _connect.py imports: drop Client/MCPServer/jsonrpc_message_adapter (not in v1); add timedelta, ClientSession, FastMCP. Mount KEPT (adversarial-v2-gate S1: SSE build_sse_app needs it). - _connect.py annotations: Connect Protocol + all factory signatures retyped to Server[Any]|FastMCP / timedelta / ClientSession; kwarg order matches v1 ClientSession.__init__; add _lowlevel() helper. Function bodies untouched per plan-v2 H2; the 7 dead Client/MCPServer/adapter refs carry temporary noqa:F821 until H3/H4/H5 rewrite them.
…_sse, parse_sse_messages, initialize_body bodies
…ver_streamable_http, client_via_http; gate: 3 smoke legs pass Also: - pyproject: add [tool.inline-snapshot] default-flags=["disable"] (matches main; without it -n0 runs use inline-snapshot active mode whose pydantic comparison mishandles extra="allow" models) - conftest: suppress PytestUnraisableExceptionWarning/ResourceWarning — v1 streamable-HTTP server transport leaks memory streams on teardown (e.g. _handle_get_request only closes sse_stream_reader on the exception path); fixes are src/-side on main, out of scope here - test_ping.py: convert to v1 decorator pattern using session-access pattern B (request_ctx contextvar) per the file→pattern assignment Gate: tests/interaction/lowlevel/test_ping.py 6/6 pass (both tests × 3 transport legs).
…d_app body Mirrors FastMCP.streamable_http_app()'s auth gating exactly (verifier derivation, middleware, AS routes, RequireAuthMiddleware wrap, PRM routes); mounted_app now calls the public builder. _connect.py is feature-complete; gate still 6/6.
…AS+RS app, ClientSession yield); auth smoke passes
…er/transports/auth - transports/test_bridge (4, no edits), test_stdio (2 + _stdio_server rewrite) - lowlevel/test_completion (5), test_logging (3), test_tools b1/3 (5) - mcpserver/test_completion (1) - auth/test_flow (5) - _requirements.py: tools:call:unknown-name + protocol:error:internal-error divergences updated for v1
- lowlevel/{cancellation,flows,meta,progress,prompts,tools} complete
- mcpserver/test_prompts complete
- transports/{sse,streamable_http} complete (1 deferred: stateless-mode server-initiated guard absent in v1)
- _requirements.py: 3 divergence updates (tools:call:unknown-name already in w1; client:output-schema:auto-list)
… / 3 deferred Parallel lane (14 N-risk files) + sequential lane (8 Y-risk files), all batches ≤5 tests. Deferred: roots:list-changed (no v1 lowlevel decorator), resources:templates:pagination (nullary-only), client-auth:authorize:offline-access-consent (v1 lacks SEP-2207). Several expected gaps did not materialize (InMemoryTransport, REQUEST_TIMEOUT, FastMCP ctor kwargs, verify_tokens=False) — all solvable with helpers or snapshot regen.
Adds the one N-risk deferral (transport:streamable-http:stateless-restrictions) that phase-4 wave 2 logged in cant-express-v1.md but did not apply to the manifest. The other three v1-API-gap deferrals (roots:list-changed, resources:templates:pagination, client-auth:authorize:offline-access-consent) were already applied by their Y-risk sequential-lane batches. test_deferral_reasons_cite_existing_paths was already green (no v2-only paths cited). Full suite: 524 pass / 0 fail. 67 deferred entries (4 v1-API-gap).
…rning filter, ASGIApp note
…yproject exclude note
…ding + 3.11 dead-zone lines - pyproject: add 'pragma: lax no cover' to exclude_lines (matches main) - _connect.py: Protocol stub body, verifier-gate no-branch, SSE return-Response no-cover - _harness.py: resource_server_url gate no-branch - test_initialize/test_wire: tg.cancel_scope.cancel() lax-no-cover (cpython#106749, 3.11 only) - test_sse: nested-async-with no-branch (3.11 arc) All harness scaffolding; no port bugs. Local ./scripts/test → 100.00%.
…st 8.4, sse-starlette 2.1) lowest-direct only; runtime floors unchanged. - pytest>=8.4.0: tests/interaction/auth uses pytest.RaisesGroup (8.4+); matches main. - sse-starlette>=2.1.0 (dev group): 1.x keeps a module-global anyio.Event (AppStatus.should_exit_event) bound to the first test's event loop, which breaks every subsequent in-process SSE response under the streaming bridge. v1's own tests run uvicorn in a subprocess so don't observe this. Runtime floor stays >=1.6.1 (the SDK works with 1.6.1 under uvicorn).
…etween tests sse-starlette <3.0 stores an anyio.Event on AppStatus the first time an EventSourceResponse runs, bound to that test's event loop. Under the in-process bridge (one process, per-test event loops) every subsequent SSE response — both the [sse] leg and connect_with_oauth's streamable-HTTP responses — fails with 'bound to a different event loop'. v1's own transport tests run uvicorn in a subprocess and so never share a process across event loops. The previous commit's >=2.1.0 dev floor was insufficient (2.1.0 still has the class attribute; 3.x switched to a ContextVar). An autouse fixture that resets the attribute after each test handles all versions including the 1.6.1 runtime floor, so the dev floor is reverted and lowest-direct CI again exercises the runtime constraint. Verified: 1598 tests / 100.00% coverage on both highest and lowest-direct.
…direct The 3.11 zero-cost-exception tracer dead-zone (python/cpython#106749) drops line events for sync statements between a cancel-on-exit __aexit__ and the next real await. On anyio 4.5.0 (lowest-direct) the unwind path differs from anyio 4.10 just enough that three additional post-async-with assertions fall in the dead-zone on 3.11 only. Same family as the markers added in 361bb9d.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backports
tests/interaction/(the end-to-end interaction-model suite from #2691) tov1.x, rewritten against the v1 public API. The result is a parity baseline: oncemainadds backwards-compatibility shims, this suite can be copied over verbatim and the diff between "v1 suite on v1" and "v1 suite on v2-with-shims" is the shim gap.Where to look
tests/interaction/README.md— conventions, manifest model, transport matrix (unchanged frommain).tests/interaction/_requirements.py— same 362-entry manifest;behaviortext is SDK-neutral so it carries over. Thedivergence/deferreddata is updated where v1 behaves differently.tests/interaction/_connect.py— the connection harness, rewritten for v1: factories yieldClientSessiondirectly (v1 has no high-levelClient), andmounted_app/build_streamable_http_apphand-assemble the Starlette app fromStreamableHTTPSessionManager+ auth routes (v1's lowlevelServerhas nostreamable_http_app()builder).tests/interaction/auth/_provider.py— unchanged frommain; v1'sOAuthAuthorizationServerProviderProtocol is identical.src/— untouched (git diff v1.x -- src/is empty).What's covered
527 tests (same coverage as the 529 on
mainminus the deferrals below). Every transport-agnostic test runs over {in-memory, streamable HTTP, SSE} via theconnectfixture; transport-specific tests, hosting/resumability/session management, and the full OAuth flow are exercised the same as onmain. The suite deliberately spreads server→client session-access across v1's several public idioms (server.request_context, therequest_ctxcontextvar, FastMCP'sContextparam,ctx.request_context,mcp.get_context()) so each path is exercised.Deferred — not expressible via the v1 public API (3)
resources:templates:pagination@server.list_resource_templates()only accepts a nullary handler; the inbound cursor cannot be readtransport:streamable-http:stateless-restrictionsclient-auth:authorize:offline-access-consentOAuthClientProviderpredates SEP-2207 (nooffline_access/prompthandling)How Has This Been Tested?
uv run --frozen pytest tests/interaction/ -n 0— 527 passed in ~5 s. Coverage andstrict-no-coverare intentionally not enforced for this suite onv1.x.Breaking Changes
None — tests-only.
pyproject.tomladds[tool.inline-snapshot](matchingmain) and excludestests/interaction/from pyright strict mode.Types of changes
Checklist
tests/interaction/README.md)AI Disclaimer