test: interaction-model end-to-end suite with a requirements manifest (#2691)

This commit is contained in:
Max 2026-05-28 19:48:30 +01:00 • committed by GitHub
commit 24725633f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 14355 additions and 121 deletions

View file

@ -360,10 +360,10 @@ class OAuthClientProvider(httpx.Auth):
auth_code, returned_state = await self.context.callback_handler()
if returned_state is None or not secrets.compare_digest(returned_state, state):
raise OAuthFlowError(f"State parameter mismatch: {returned_state} != {state}") # pragma: no cover
raise OAuthFlowError(f"State parameter mismatch: {returned_state} != {state}")
if not auth_code:
raise OAuthFlowError("No authorization code received") # pragma: no cover
raise OAuthFlowError("No authorization code received")
# Return auth code and code verifier for token exchange
return auth_code, pkce_params.code_verifier
@ -452,7 +452,7 @@ class OAuthClientProvider(httpx.Auth):
return httpx.Request("POST", token_url, data=refresh_data, headers=headers)
async def _handle_refresh_response(self, response: httpx.Response) -> bool: # pragma: no cover
async def _handle_refresh_response(self, response: httpx.Response) -> bool:
"""Handle token refresh response. Returns True if successful."""
if response.status_code != 200:
logger.warning(f"Token refresh failed: {response.status_code}")
@ -468,12 +468,12 @@ class OAuthClientProvider(httpx.Auth):
await self.context.storage.set_tokens(token_response)
return True
except ValidationError:
except ValidationError: # pragma: no cover
logger.exception("Invalid refresh response")
self.context.clear_tokens()
return False
async def _initialize(self) -> None: # pragma: no cover
async def _initialize(self) -> None:
"""Load stored tokens and client info."""
self.context.current_tokens = await self.context.storage.get_tokens()
self.context.client_info = await self.context.storage.get_client_info()
@ -507,17 +507,17 @@ class OAuthClientProvider(httpx.Auth):
"""HTTPX auth flow integration."""
async with self.context.lock:
if not self._initialized:
await self._initialize() # pragma: no cover
await self._initialize()
# Capture protocol version from request headers
self.context.protocol_version = request.headers.get(MCP_PROTOCOL_VERSION)
if not self.context.is_token_valid() and self.context.can_refresh_token():
# Try to refresh token
refresh_request = await self._refresh_token() # pragma: no cover
refresh_response = yield refresh_request # pragma: no cover
refresh_request = await self._refresh_token()
refresh_response = yield refresh_request
if not await self._handle_refresh_response(refresh_response): # pragma: no cover
if not await self._handle_refresh_response(refresh_response):
# Refresh failed, need full re-authentication
self._initialized = False
@ -612,7 +612,7 @@ class OAuthClientProvider(httpx.Auth):
# Step 5: Perform authorization and complete token exchange
token_response = yield await self._perform_authorization()
await self._handle_token_response(token_response)
except Exception: # pragma: no cover
except Exception:
logger.exception("OAuth flow error")
raise

View file

@ -305,4 +305,4 @@ class Client:
async def send_roots_list_changed(self) -> None:
"""Send a notification that the roots list has changed."""
# TODO(Marcelo): Currently, there is no way for the server to handle this. We should add support.
await self.session.send_roots_list_changed() # pragma: no cover
await self.session.send_roots_list_changed()

View file

@ -74,7 +74,7 @@ async def _default_elicitation_callback(
context: RequestContext[ClientSession],
params: types.ElicitRequestParams,
) -> types.ElicitResult | types.ErrorData:
return types.ErrorData( # pragma: no cover
return types.ErrorData(
code=types.INVALID_REQUEST,
message="Elicitation not supported",
)
@ -337,9 +337,7 @@ class ClientSession(
from jsonschema import SchemaError, ValidationError, validate
if result.structured_content is None:
raise RuntimeError(
f"Tool {name} has an output schema but did not return structured content"
) # pragma: no cover
raise RuntimeError(f"Tool {name} has an output schema but did not return structured content")
try:
validate(result.structured_content, output_schema)
except ValidationError as e:
@ -408,7 +406,7 @@ class ClientSession(
return result
async def send_roots_list_changed(self) -> None: # pragma: no cover
async def send_roots_list_changed(self) -> None:
"""Send a roots/list_changed notification."""
await self.send_notification(types.RootsListChangedNotification())
@ -449,7 +447,7 @@ class ClientSession(
client_response = ClientResponse.validate_python(response)
await responder.respond(client_response)
case types.PingRequest(): # pragma: no cover
case types.PingRequest():
with responder:
return await responder.respond(types.EmptyResult())

View file

@ -210,7 +210,7 @@ class StreamableHTTPTransport:
# Stream ended normally (server closed) - reset attempt counter
attempt = 0
except Exception: # pragma: lax no cover
except Exception:
logger.debug("GET stream error", exc_info=True)
attempt += 1
@ -267,8 +267,8 @@ class StreamableHTTPTransport:
logger.debug("Received 202 Accepted")
return
if response.status_code == 404: # pragma: no branch
if isinstance(message, JSONRPCRequest): # pragma: no branch
if response.status_code == 404:
if isinstance(message, JSONRPCRequest):
error_data = ErrorData(code=INVALID_REQUEST, message="Session terminated")
session_message = SessionMessage(JSONRPCError(jsonrpc="2.0", id=message.id, error=error_data))
await ctx.read_stream_writer.send(session_message)
@ -492,17 +492,17 @@ class StreamableHTTPTransport:
async def terminate_session(self, client: httpx.AsyncClient) -> None:
"""Terminate the session by sending a DELETE request."""
if not self.session_id: # pragma: lax no cover
return
if not self.session_id:
return # pragma: no cover
try:
headers = self._prepare_headers()
response = await client.delete(self.url, headers=headers)
if response.status_code == 405: # pragma: lax no cover
if response.status_code == 405:
logger.debug("Server does not allow session termination")
elif response.status_code not in (200, 204): # pragma: lax no cover
logger.warning(f"Session termination failed: {response.status_code}")
elif response.status_code not in (200, 204):
logger.warning(f"Session termination failed: {response.status_code}") # pragma: no cover
except Exception as exc: # pragma: no cover
logger.warning(f"Session termination failed: {exc}")