Get baseline 100% clean coverage (#1553)

This commit is contained in:
Max Isbey 2025-11-11 13:09:32 +00:00 • committed by GitHub
commit 89e9c43acf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1032 additions and 1084 deletions

View file

@ -15,16 +15,16 @@ class MockTokenStorage:
self._tokens: OAuthToken | None = None
self._client_info: OAuthClientInformationFull | None = None
async def get_tokens(self) -> OAuthToken | None:
async def get_tokens(self) -> OAuthToken | None: # pragma: no cover
return self._tokens
async def set_tokens(self, tokens: OAuthToken) -> None:
async def set_tokens(self, tokens: OAuthToken) -> None: # pragma: no cover
self._tokens = tokens
async def get_client_info(self) -> OAuthClientInformationFull | None:
async def get_client_info(self) -> OAuthClientInformationFull | None: # pragma: no cover
return self._client_info
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None: # pragma: no cover
self._client_info = client_info
@ -45,11 +45,11 @@ def client_metadata():
@pytest.fixture
def rfc7523_oauth_provider(client_metadata: OAuthClientMetadata, mock_storage: MockTokenStorage):
async def redirect_handler(url: str) -> None:
async def redirect_handler(url: str) -> None: # pragma: no cover
"""Mock redirect handler."""
pass
async def callback_handler() -> tuple[str, str | None]:
async def callback_handler() -> tuple[str, str | None]: # pragma: no cover
"""Mock callback handler."""
return "test_auth_code", "test_state"

View file

@ -40,7 +40,7 @@ class StreamSpyCollection:
self.client.sent_messages.clear()
self.server.sent_messages.clear()
def get_client_requests(self, method: str | None = None) -> list[JSONRPCRequest]:
def get_client_requests(self, method: str | None = None) -> list[JSONRPCRequest]: # pragma: no cover
"""Get client-sent requests, optionally filtered by method."""
return [
req.message.root
@ -48,15 +48,15 @@ class StreamSpyCollection:
if isinstance(req.message.root, JSONRPCRequest) and (method is None or req.message.root.method == method)
]
def get_server_requests(self, method: str | None = None) -> list[JSONRPCRequest]:
def get_server_requests(self, method: str | None = None) -> list[JSONRPCRequest]: # pragma: no cover
"""Get server-sent requests, optionally filtered by method."""
return [
return [ # pragma: no cover
req.message.root
for req in self.server.sent_messages
if isinstance(req.message.root, JSONRPCRequest) and (method is None or req.message.root.method == method)
]
def get_client_notifications(self, method: str | None = None) -> list[JSONRPCNotification]:
def get_client_notifications(self, method: str | None = None) -> list[JSONRPCNotification]: # pragma: no cover
"""Get client-sent notifications, optionally filtered by method."""
return [
notif.message.root
@ -65,7 +65,7 @@ class StreamSpyCollection:
and (method is None or notif.message.root.method == method)
]
def get_server_notifications(self, method: str | None = None) -> list[JSONRPCNotification]:
def get_server_notifications(self, method: str | None = None) -> list[JSONRPCNotification]: # pragma: no cover
"""Get server-sent notifications, optionally filtered by method."""
return [
notif.message.root

View file

@ -22,13 +22,13 @@ class MockTokenStorage:
self._client_info: OAuthClientInformationFull | None = None
async def get_tokens(self) -> OAuthToken | None:
return self._tokens
return self._tokens # pragma: no cover
async def set_tokens(self, tokens: OAuthToken) -> None:
self._tokens = tokens
async def get_client_info(self) -> OAuthClientInformationFull | None:
return self._client_info
return self._client_info # pragma: no cover
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
self._client_info = client_info
@ -64,11 +64,11 @@ def valid_tokens():
def oauth_provider(client_metadata: OAuthClientMetadata, mock_storage: MockTokenStorage):
async def redirect_handler(url: str) -> None:
"""Mock redirect handler."""
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
"""Mock callback handler."""
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
return OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",
@ -247,10 +247,10 @@ class TestOAuthFlow:
"""Test protected resource metadata discovery URL building with fallback."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com",
@ -663,7 +663,7 @@ class TestRegistrationResponse:
@property
def text(self):
if not self._aread_called:
if not self._aread_called: # pragma: no cover
raise RuntimeError("Response.text accessed before response.aread()")
return self._text
@ -846,10 +846,10 @@ class TestAuthFlow:
# In the buggy version, this would yield the request AGAIN unconditionally
# In the fixed version, this should end the generator
try:
await auth_flow.asend(response) # extra request
request_yields += 1
# If we reach here, the bug is present
pytest.fail(
await auth_flow.asend(response) # extra request # pragma: no cover
request_yields += 1 # pragma: no cover
# If we reach here, the bug is present # pragma: no cover
pytest.fail( # pragma: no cover
f"Unnecessary retry detected! Request was yielded {request_yields} times. "
f"This indicates the retry logic bug that caused 2x performance degradation. "
f"The request should only be yielded once for successful responses."
@ -949,7 +949,7 @@ class TestAuthFlow:
success_response = httpx.Response(200, request=final_request)
try:
await auth_flow.asend(success_response)
pytest.fail("Should have stopped after successful response")
pytest.fail("Should have stopped after successful response") # pragma: no cover
except StopAsyncIteration:
pass # Expected
@ -1043,10 +1043,10 @@ class TestSEP985Discovery:
"""Test that client falls back to path-based well-known URI when WWW-Authenticate is absent."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",
@ -1076,10 +1076,10 @@ class TestSEP985Discovery:
"""Test that client falls back to root-based URI when path-based returns 404."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",
@ -1177,10 +1177,10 @@ class TestSEP985Discovery:
"""Test that WWW-Authenticate header resource_metadata takes priority over well-known URIs."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",
@ -1269,10 +1269,10 @@ class TestWWWAuthenticate:
"""Test extraction of various fields from valid WWW-Authenticate headers."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",
@ -1317,10 +1317,10 @@ class TestWWWAuthenticate:
"""Test extraction returns None for invalid cases."""
async def redirect_handler(url: str) -> None:
pass
pass # pragma: no cover
async def callback_handler() -> tuple[str, str | None]:
return "test_auth_code", "test_state"
return "test_auth_code", "test_state" # pragma: no cover
provider = OAuthClientProvider(
server_url="https://api.example.com/v1/mcp",

View file

@ -44,7 +44,7 @@ def test_command_execution(mock_config_path: Path):
test_args = [command] + args + ["--help"]
result = subprocess.run(test_args, capture_output=True, text=True, timeout=5, check=False)
result = subprocess.run(test_args, capture_output=True, text=True, timeout=20, check=False)
assert result.returncode == 0
assert "usage" in result.stdout.lower()

View file

@ -35,7 +35,7 @@ UNICODE_TEST_STRINGS = {
}
def run_unicode_server(port: int) -> None:
def run_unicode_server(port: int) -> None: # pragma: no cover
"""Run the Unicode test server in a separate process."""
# Import inside the function since this runs in a separate process
from collections.abc import AsyncGenerator
@ -167,7 +167,7 @@ def running_unicode_server(unicode_server_port: int) -> Generator[str, None, Non
# Clean up - try graceful termination first
proc.terminate()
proc.join(timeout=2)
if proc.is_alive():
if proc.is_alive(): # pragma: no cover
proc.kill()
proc.join(timeout=1)

View file

@ -19,27 +19,27 @@ async def full_featured_server():
server = FastMCP("test")
@server.tool(name="test_tool_1")
async def test_tool_1() -> str:
async def test_tool_1() -> str: # pragma: no cover
"""First test tool"""
return "Result 1"
@server.tool(name="test_tool_2")
async def test_tool_2() -> str:
async def test_tool_2() -> str: # pragma: no cover
"""Second test tool"""
return "Result 2"
@server.resource("resource://test/data")
async def test_resource() -> str:
async def test_resource() -> str: # pragma: no cover
"""Test resource"""
return "Test data"
@server.prompt()
async def test_prompt(name: str) -> str:
async def test_prompt(name: str) -> str: # pragma: no cover
"""Test prompt"""
return f"Hello, {name}!"
@server.resource("resource://test/{name}")
async def test_template(name: str) -> str:
async def test_template(name: str) -> str: # pragma: no cover
"""Test resource template"""
return f"Data for {name}"
@ -209,7 +209,7 @@ async def test_list_tools_with_strict_server_validation():
server = Server("strict_server")
@server.list_tools()
async def handle_list_tools(request: ListToolsRequest) -> ListToolsResult:
async def handle_list_tools(request: ListToolsRequest) -> ListToolsResult: # pragma: no cover
"""Strict handler that validates params field exists"""
# Simulate strict server validation

View file

@ -51,7 +51,7 @@ async def test_logging_callback():
async def message_handler(
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
) -> None:
if isinstance(message, Exception):
if isinstance(message, Exception): # pragma: no cover
raise message
async with create_session(

View file

@ -24,7 +24,7 @@ from mcp.types import ClientNotification, RootsListChangedNotification
from tests.test_helpers import wait_for_server
def create_non_sdk_server_app() -> Starlette:
def create_non_sdk_server_app() -> Starlette: # pragma: no cover
"""Create a minimal server that doesn't follow SDK conventions."""
async def handle_mcp_request(request: Request) -> Response:
@ -67,7 +67,7 @@ def create_non_sdk_server_app() -> Starlette:
return app
def run_non_sdk_server(port: int) -> None:
def run_non_sdk_server(port: int) -> None: # pragma: no cover
"""Run the non-SDK server in a separate process."""
app = create_non_sdk_server_app()
config = uvicorn.Config(
@ -95,9 +95,9 @@ def non_sdk_server(non_sdk_server_port: int) -> Generator[None, None, None]:
proc.start()
# Wait for server to be ready
try:
try: # pragma: no cover
wait_for_server(non_sdk_server_port, timeout=10.0)
except TimeoutError:
except TimeoutError: # pragma: no cover
proc.kill()
proc.join(timeout=2)
pytest.fail("Server failed to start within 10 seconds")
@ -120,7 +120,7 @@ async def test_non_compliant_notification_response(non_sdk_server: None, non_sdk
server_url = f"http://127.0.0.1:{non_sdk_server_port}/mcp"
returned_exception = None
async def message_handler(
async def message_handler( # pragma: no cover
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
):
nonlocal returned_exception
@ -141,5 +141,5 @@ async def test_non_compliant_notification_response(non_sdk_server: None, non_sdk
ClientNotification(RootsListChangedNotification(method="notifications/roots/list_changed"))
)
if returned_exception:
if returned_exception: # pragma: no cover
pytest.fail(f"Server encountered an exception: {returned_exception}")

View file

@ -19,7 +19,9 @@ async def test_send_request_stream_cleanup():
# Create a mock session with the minimal required functionality
class TestSession(BaseSession[ClientRequest, ClientNotification, ClientResult, Any, Any]):
async def _send_response(self, request_id: RequestId, response: SendResultT | ErrorData) -> None:
async def _send_response(
self, request_id: RequestId, response: SendResultT | ErrorData
) -> None: # pragma: no cover
pass
# Create streams

View file

@ -82,7 +82,7 @@ async def test_client_session_initialize():
)
# Create a message handler to catch exceptions
async def message_handler(
async def message_handler( # pragma: no cover
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
) -> None:
if isinstance(message, Exception):
@ -426,7 +426,7 @@ async def test_client_capabilities_with_custom_callbacks():
received_capabilities = None
async def custom_sampling_callback(
async def custom_sampling_callback( # pragma: no cover
context: RequestContext["ClientSession", Any],
params: types.CreateMessageRequestParams,
) -> types.CreateMessageResult | types.ErrorData:
@ -436,7 +436,7 @@ async def test_client_capabilities_with_custom_callbacks():
model="test-model",
)
async def custom_list_roots_callback(
async def custom_list_roots_callback( # pragma: no cover
context: RequestContext["ClientSession", Any],
) -> types.ListRootsResult | types.ErrorData:
return types.ListRootsResult(roots=[])

View file

@ -50,7 +50,7 @@ class TestClientSessionGroup:
mock_session = mock.AsyncMock()
# --- Prepare Session Group ---
def hook(name: str, server_info: types.Implementation) -> str:
def hook(name: str, server_info: types.Implementation) -> str: # pragma: no cover
return f"{(server_info.name)}-{name}"
mcp_session_group = ClientSessionGroup(component_name_hook=hook)
@ -344,7 +344,7 @@ class TestClientSessionGroup:
timeout=server_params_instance.timeout,
sse_read_timeout=server_params_instance.sse_read_timeout,
)
elif client_type_name == "streamablehttp":
elif client_type_name == "streamablehttp": # pragma: no branch
assert isinstance(server_params_instance, StreamableHttpParameters)
mock_specific_client_func.assert_called_once_with(
url=server_params_instance.url,

View file

@ -54,7 +54,7 @@ async def test_stdio_client():
read_messages: list[JSONRPCMessage] = []
async with read_stream:
async for message in read_stream:
if isinstance(message, Exception):
if isinstance(message, Exception): # pragma: no cover
raise message
read_messages.append(message.message)
@ -93,7 +93,7 @@ async def test_stdio_client_nonexistent_command():
# Should raise an error when trying to start the process
with pytest.raises(OSError) as exc_info:
async with stdio_client(server_params) as (_, _):
pass
pass # pragma: no cover
# The error should indicate the command was not found (ENOENT: No such file or directory)
assert exc_info.value.errno == errno.ENOENT
@ -144,7 +144,7 @@ async def test_stdio_client_universal_cleanup():
)
# Check if we timed out
if cancel_scope.cancelled_caught:
if cancel_scope.cancelled_caught: # pragma: no cover
pytest.fail(
"stdio_client cleanup timed out after 8.0 seconds. "
"This indicates the cleanup mechanism is hanging and needs fixing."
@ -153,7 +153,7 @@ async def test_stdio_client_universal_cleanup():
@pytest.mark.anyio
@pytest.mark.skipif(sys.platform == "win32", reason="Windows signal handling is different")
async def test_stdio_client_sigint_only_process():
async def test_stdio_client_sigint_only_process(): # pragma: no cover
"""
Test cleanup with a process that ignores SIGTERM but responds to SIGINT.
"""
@ -195,7 +195,7 @@ async def test_stdio_client_sigint_only_process():
# Exit context triggers cleanup - this should not hang
pass
if cancel_scope.cancelled_caught:
if cancel_scope.cancelled_caught: # pragma: no cover
raise TimeoutError("Test timed out")
end_time = time.time()
@ -208,7 +208,7 @@ async def test_stdio_client_sigint_only_process():
f"Expected < {SIGTERM_IGNORING_PROCESS_TIMEOUT} seconds. "
"This suggests the cleanup needs SIGINT/SIGKILL fallback."
)
except (TimeoutError, Exception) as e:
except (TimeoutError, Exception) as e: # pragma: no cover
if isinstance(e, TimeoutError) or "timed out" in str(e):
pytest.fail(
f"stdio_client cleanup timed out after {SIGTERM_IGNORING_PROCESS_TIMEOUT} seconds "
@ -303,7 +303,7 @@ class TestChildProcessCleanup:
assert os.path.exists(parent_marker), "Parent process didn't start"
# Verify child is writing
if os.path.exists(marker_file):
if os.path.exists(marker_file): # pragma: no branch
initial_size = os.path.getsize(marker_file)
await anyio.sleep(0.3)
size_after_wait = os.path.getsize(marker_file)
@ -318,7 +318,7 @@ class TestChildProcessCleanup:
# Verify processes stopped
await anyio.sleep(0.5)
if os.path.exists(marker_file):
if os.path.exists(marker_file): # pragma: no branch
size_after_cleanup = os.path.getsize(marker_file)
await anyio.sleep(0.5)
final_size = os.path.getsize(marker_file)
@ -335,7 +335,7 @@ class TestChildProcessCleanup:
for f in [marker_file, parent_marker]:
try:
os.unlink(f)
except OSError:
except OSError: # pragma: no cover
pass
@pytest.mark.anyio
@ -406,7 +406,7 @@ class TestChildProcessCleanup:
# Verify all are writing
for file_path, name in [(parent_file, "parent"), (child_file, "child"), (grandchild_file, "grandchild")]:
if os.path.exists(file_path):
if os.path.exists(file_path): # pragma: no branch
initial_size = os.path.getsize(file_path)
await anyio.sleep(0.3)
new_size = os.path.getsize(file_path)
@ -420,7 +420,7 @@ class TestChildProcessCleanup:
# Verify all stopped
await anyio.sleep(0.5)
for file_path, name in [(parent_file, "parent"), (child_file, "child"), (grandchild_file, "grandchild")]:
if os.path.exists(file_path):
if os.path.exists(file_path): # pragma: no branch
size1 = os.path.getsize(file_path)
await anyio.sleep(0.3)
size2 = os.path.getsize(file_path)
@ -433,7 +433,7 @@ class TestChildProcessCleanup:
for f in [parent_file, child_file, grandchild_file]:
try:
os.unlink(f)
except OSError:
except OSError: # pragma: no cover
pass
@pytest.mark.anyio
@ -487,7 +487,7 @@ class TestChildProcessCleanup:
await anyio.sleep(0.5)
# Verify child is writing
if os.path.exists(marker_file):
if os.path.exists(marker_file): # pragma: no cover
size1 = os.path.getsize(marker_file)
await anyio.sleep(0.3)
size2 = os.path.getsize(marker_file)
@ -500,7 +500,7 @@ class TestChildProcessCleanup:
# Verify child stopped
await anyio.sleep(0.5)
if os.path.exists(marker_file):
if os.path.exists(marker_file): # pragma: no branch
size3 = os.path.getsize(marker_file)
await anyio.sleep(0.3)
size4 = os.path.getsize(marker_file)
@ -512,7 +512,7 @@ class TestChildProcessCleanup:
# Clean up marker file
try:
os.unlink(marker_file)
except OSError:
except OSError: # pragma: no cover
pass
@ -560,7 +560,7 @@ async def test_stdio_client_graceful_stdin_exit():
pytest.fail(
"stdio_client cleanup timed out after 5.0 seconds. "
"Process should have exited gracefully when stdin was closed."
)
) # pragma: no cover
end_time = time.time()
elapsed = end_time - start_time
@ -619,7 +619,7 @@ async def test_stdio_client_stdin_close_ignored():
pytest.fail(
"stdio_client cleanup timed out after 7.0 seconds. "
"Process should have been terminated via SIGTERM escalation."
)
) # pragma: no cover
end_time = time.time()
elapsed = end_time - start_time