Use 120 characters instead of 88 (#856)

This commit is contained in:
Marcelo Trylesinski 2025-06-11 02:45:50 -07:00 • committed by GitHub
commit 543961968c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 687 additions and 2142 deletions

View file

@ -17,9 +17,7 @@ async def test_list_tools_returns_all_tools():
f"""Tool number {i}"""
return i
globals()[f"dummy_tool_{i}"] = (
dummy_tool_func # Keep reference to avoid garbage collection
)
globals()[f"dummy_tool_{i}"] = dummy_tool_func # Keep reference to avoid garbage collection
# Get all tools
tools = await mcp.list_tools()
@ -30,6 +28,4 @@ async def test_list_tools_returns_all_tools():
# Verify each tool is unique and has the correct name
tool_names = [tool.name for tool in tools]
expected_names = [f"tool_{i}" for i in range(num_tools)]
assert sorted(tool_names) == sorted(
expected_names
), "Tool names don't match expected names"
assert sorted(tool_names) == sorted(expected_names), "Tool names don't match expected names"

View file

@ -24,9 +24,7 @@ async def test_resource_templates():
# Note: list_resource_templates() returns a decorator that wraps the handler
# The handler returns a ServerResult with a ListResourceTemplatesResult inside
result = await mcp._mcp_server.request_handlers[types.ListResourceTemplatesRequest](
types.ListResourceTemplatesRequest(
method="resources/templates/list", params=None
)
types.ListResourceTemplatesRequest(method="resources/templates/list", params=None)
)
assert isinstance(result.root, types.ListResourceTemplatesResult)
templates = result.root.resourceTemplates

View file

@ -61,9 +61,7 @@ async def test_resource_template_edge_cases():
await mcp.read_resource("resource://users/123/posts") # Missing post_id
with pytest.raises(ValueError, match="Unknown resource"):
await mcp.read_resource(
"resource://users/123/posts/456/extra"
) # Extra path component
await mcp.read_resource("resource://users/123/posts/456/extra") # Extra path component
@pytest.mark.anyio
@ -110,11 +108,7 @@ async def test_resource_template_client_interaction():
# Verify invalid resource URIs raise appropriate errors
with pytest.raises(Exception): # Specific exception type may vary
await session.read_resource(
AnyUrl("resource://users/123/posts")
) # Missing post_id
await session.read_resource(AnyUrl("resource://users/123/posts")) # Missing post_id
with pytest.raises(Exception): # Specific exception type may vary
await session.read_resource(
AnyUrl("resource://users/123/invalid")
) # Invalid template
await session.read_resource(AnyUrl("resource://users/123/invalid")) # Invalid template

View file

@ -45,31 +45,19 @@ async def test_fastmcp_resource_mime_type():
bytes_resource = mapping["test://image_bytes"]
# Verify mime types
assert (
string_resource.mimeType == "image/png"
), "String resource mime type not respected"
assert (
bytes_resource.mimeType == "image/png"
), "Bytes resource mime type not respected"
assert string_resource.mimeType == "image/png", "String resource mime type not respected"
assert bytes_resource.mimeType == "image/png", "Bytes resource mime type not respected"
# Also verify the content can be read correctly
string_result = await client.read_resource(AnyUrl("test://image"))
assert len(string_result.contents) == 1
assert (
getattr(string_result.contents[0], "text") == base64_string
), "Base64 string mismatch"
assert (
string_result.contents[0].mimeType == "image/png"
), "String content mime type not preserved"
assert getattr(string_result.contents[0], "text") == base64_string, "Base64 string mismatch"
assert string_result.contents[0].mimeType == "image/png", "String content mime type not preserved"
bytes_result = await client.read_resource(AnyUrl("test://image_bytes"))
assert len(bytes_result.contents) == 1
assert (
base64.b64decode(getattr(bytes_result.contents[0], "blob")) == image_bytes
), "Bytes mismatch"
assert (
bytes_result.contents[0].mimeType == "image/png"
), "Bytes content mime type not preserved"
assert base64.b64decode(getattr(bytes_result.contents[0], "blob")) == image_bytes, "Bytes mismatch"
assert bytes_result.contents[0].mimeType == "image/png", "Bytes content mime type not preserved"
async def test_lowlevel_resource_mime_type():
@ -82,9 +70,7 @@ async def test_lowlevel_resource_mime_type():
# Create test resources with specific mime types
test_resources = [
types.Resource(
uri=AnyUrl("test://image"), name="test image", mimeType="image/png"
),
types.Resource(uri=AnyUrl("test://image"), name="test image", mimeType="image/png"),
types.Resource(
uri=AnyUrl("test://image_bytes"),
name="test image bytes",
@ -101,9 +87,7 @@ async def test_lowlevel_resource_mime_type():
if str(uri) == "test://image":
return [ReadResourceContents(content=base64_string, mime_type="image/png")]
elif str(uri) == "test://image_bytes":
return [
ReadResourceContents(content=bytes(image_bytes), mime_type="image/png")
]
return [ReadResourceContents(content=bytes(image_bytes), mime_type="image/png")]
raise Exception(f"Resource not found: {uri}")
# Test that resources are listed with correct mime type
@ -119,28 +103,16 @@ async def test_lowlevel_resource_mime_type():
bytes_resource = mapping["test://image_bytes"]
# Verify mime types
assert (
string_resource.mimeType == "image/png"
), "String resource mime type not respected"
assert (
bytes_resource.mimeType == "image/png"
), "Bytes resource mime type not respected"
assert string_resource.mimeType == "image/png", "String resource mime type not respected"
assert bytes_resource.mimeType == "image/png", "Bytes resource mime type not respected"
# Also verify the content can be read correctly
string_result = await client.read_resource(AnyUrl("test://image"))
assert len(string_result.contents) == 1
assert (
getattr(string_result.contents[0], "text") == base64_string
), "Base64 string mismatch"
assert (
string_result.contents[0].mimeType == "image/png"
), "String content mime type not preserved"
assert getattr(string_result.contents[0], "text") == base64_string, "Base64 string mismatch"
assert string_result.contents[0].mimeType == "image/png", "String content mime type not preserved"
bytes_result = await client.read_resource(AnyUrl("test://image_bytes"))
assert len(bytes_result.contents) == 1
assert (
base64.b64decode(getattr(bytes_result.contents[0], "blob")) == image_bytes
), "Bytes mismatch"
assert (
bytes_result.contents[0].mimeType == "image/png"
), "Bytes content mime type not preserved"
assert base64.b64decode(getattr(bytes_result.contents[0], "blob")) == image_bytes, "Bytes mismatch"
assert bytes_result.contents[0].mimeType == "image/png", "Bytes content mime type not preserved"

View file

@ -35,15 +35,7 @@ async def test_progress_token_zero_first_call():
await ctx.report_progress(10, 10) # Complete
# Verify progress notifications
assert (
mock_session.send_progress_notification.call_count == 3
), "All progress notifications should be sent"
mock_session.send_progress_notification.assert_any_call(
progress_token=0, progress=0.0, total=10.0, message=None
)
mock_session.send_progress_notification.assert_any_call(
progress_token=0, progress=5.0, total=10.0, message=None
)
mock_session.send_progress_notification.assert_any_call(
progress_token=0, progress=10.0, total=10.0, message=None
)
assert mock_session.send_progress_notification.call_count == 3, "All progress notifications should be sent"
mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=0.0, total=10.0, message=None)
mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=5.0, total=10.0, message=None)
mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=10.0, total=10.0, message=None)

View file

@ -66,9 +66,7 @@ async def test_request_id_match() -> None:
)
await client_writer.send(SessionMessage(JSONRPCMessage(root=init_req)))
response = (
await server_reader.receive()
) # Get init response but don't need to check it
response = await server_reader.receive() # Get init response but don't need to check it
# Send initialized notification
initialized_notification = JSONRPCNotification(
@ -76,14 +74,10 @@ async def test_request_id_match() -> None:
params=NotificationParams().model_dump(by_alias=True, exclude_none=True),
jsonrpc="2.0",
)
await client_writer.send(
SessionMessage(JSONRPCMessage(root=initialized_notification))
)
await client_writer.send(SessionMessage(JSONRPCMessage(root=initialized_notification)))
# Send ping request with custom ID
ping_request = JSONRPCRequest(
id=custom_request_id, method="ping", params={}, jsonrpc="2.0"
)
ping_request = JSONRPCRequest(id=custom_request_id, method="ping", params={}, jsonrpc="2.0")
await client_writer.send(SessionMessage(JSONRPCMessage(root=ping_request)))
@ -91,9 +85,7 @@ async def test_request_id_match() -> None:
response = await server_reader.receive()
# Verify response ID matches request ID
assert (
response.message.root.id == custom_request_id
), "Response ID should match request ID"
assert response.message.root.id == custom_request_id, "Response ID should match request ID"
# Cancel server task
tg.cancel_scope.cancel()

View file

@ -47,11 +47,7 @@ async def test_server_base64_encoding_issue():
# Register a resource handler that returns our test data
@server.read_resource()
async def read_resource(uri: AnyUrl) -> list[ReadResourceContents]:
return [
ReadResourceContents(
content=binary_data, mime_type="application/octet-stream"
)
]
return [ReadResourceContents(content=binary_data, mime_type="application/octet-stream")]
# Get the handler directly from the server
handler = server.request_handlers[ReadResourceRequest]

View file

@ -11,12 +11,7 @@ from anyio.abc import TaskStatus
from mcp.client.session import ClientSession
from mcp.server.lowlevel import Server
from mcp.shared.exceptions import McpError
from mcp.types import (
AudioContent,
EmbeddedResource,
ImageContent,
TextContent,
)
from mcp.types import AudioContent, EmbeddedResource, ImageContent, TextContent
@pytest.mark.anyio
@ -36,9 +31,7 @@ async def test_notification_validation_error(tmp_path: Path):
slow_request_complete = anyio.Event()
@server.call_tool()
async def slow_tool(
name: str, arg
) -> Sequence[TextContent | ImageContent | AudioContent | EmbeddedResource]:
async def slow_tool(name: str, arg) -> Sequence[TextContent | ImageContent | AudioContent | EmbeddedResource]:
nonlocal request_count
request_count += 1
@ -75,9 +68,7 @@ async def test_notification_validation_error(tmp_path: Path):
# - Long enough for fast operations (>10ms)
# - Short enough for slow operations (<200ms)
# - Not too short to avoid flakiness
async with ClientSession(
read_stream, write_stream, read_timeout_seconds=timedelta(milliseconds=50)
) as session:
async with ClientSession(read_stream, write_stream, read_timeout_seconds=timedelta(milliseconds=50)) as session:
await session.initialize()
# First call should work (fast operation)

View file

@ -1,4 +1,4 @@
# Claude Debug
# Claude Debug
"""Test for HackerOne vulnerability report #3156202 - malformed input DOS."""
import anyio
@ -23,12 +23,8 @@ async def test_malformed_initialize_request_does_not_crash_server():
instead of crashing the server (HackerOne #3156202).
"""
# Create in-memory streams for testing
read_send_stream, read_receive_stream = anyio.create_memory_object_stream[
SessionMessage | Exception
](10)
write_send_stream, write_receive_stream = anyio.create_memory_object_stream[
SessionMessage
](10)
read_send_stream, read_receive_stream = anyio.create_memory_object_stream[SessionMessage | Exception](10)
write_send_stream, write_receive_stream = anyio.create_memory_object_stream[SessionMessage](10)
try:
# Create a malformed initialize request (missing required params field)
@ -38,7 +34,7 @@ async def test_malformed_initialize_request_does_not_crash_server():
method="initialize",
# params=None # Missing required params field
)
# Wrap in session message
request_message = SessionMessage(message=JSONRPCMessage(malformed_request))
@ -54,22 +50,22 @@ async def test_malformed_initialize_request_does_not_crash_server():
):
# Send the malformed request
await read_send_stream.send(request_message)
# Give the session time to process the request
await anyio.sleep(0.1)
# Check that we received an error response instead of a crash
try:
response_message = write_receive_stream.receive_nowait()
response = response_message.message.root
# Verify it's a proper JSON-RPC error response
assert isinstance(response, JSONRPCError)
assert response.jsonrpc == "2.0"
assert response.id == "f20fe86132ed4cd197f89a7134de5685"
assert response.error.code == INVALID_PARAMS
assert "Invalid request parameters" in response.error.message
# Verify the session is still alive and can handle more requests
# Send another malformed request to confirm server stability
another_malformed_request = JSONRPCRequest(
@ -78,21 +74,19 @@ async def test_malformed_initialize_request_does_not_crash_server():
method="tools/call",
# params=None # Missing required params
)
another_request_message = SessionMessage(
message=JSONRPCMessage(another_malformed_request)
)
another_request_message = SessionMessage(message=JSONRPCMessage(another_malformed_request))
await read_send_stream.send(another_request_message)
await anyio.sleep(0.1)
# Should get another error response, not a crash
second_response_message = write_receive_stream.receive_nowait()
second_response = second_response_message.message.root
assert isinstance(second_response, JSONRPCError)
assert second_response.id == "test_id_2"
assert second_response.error.code == INVALID_PARAMS
except anyio.WouldBlock:
pytest.fail("No response received - server likely crashed")
finally:
@ -109,12 +103,8 @@ async def test_multiple_concurrent_malformed_requests():
Test that multiple concurrent malformed requests don't crash the server.
"""
# Create in-memory streams for testing
read_send_stream, read_receive_stream = anyio.create_memory_object_stream[
SessionMessage | Exception
](100)
write_send_stream, write_receive_stream = anyio.create_memory_object_stream[
SessionMessage
](100)
read_send_stream, read_receive_stream = anyio.create_memory_object_stream[SessionMessage | Exception](100)
write_send_stream, write_receive_stream = anyio.create_memory_object_stream[SessionMessage](100)
try:
# Start a server session
@ -136,18 +126,16 @@ async def test_multiple_concurrent_malformed_requests():
method="initialize",
# params=None # Missing required params
)
request_message = SessionMessage(
message=JSONRPCMessage(malformed_request)
)
request_message = SessionMessage(message=JSONRPCMessage(malformed_request))
malformed_requests.append(request_message)
# Send all requests
for request in malformed_requests:
await read_send_stream.send(request)
# Give time to process
await anyio.sleep(0.2)
# Verify we get error responses for all requests
error_responses = []
try:
@ -156,10 +144,10 @@ async def test_multiple_concurrent_malformed_requests():
error_responses.append(response_message.message.root)
except anyio.WouldBlock:
pass # No more messages
# Should have received 10 error responses
assert len(error_responses) == 10
for i, response in enumerate(error_responses):
assert isinstance(response, JSONRPCError)
assert response.id == f"malformed_{i}"
@ -169,4 +157,4 @@ async def test_multiple_concurrent_malformed_requests():
await read_send_stream.aclose()
await write_send_stream.aclose()
await read_receive_stream.aclose()
await write_receive_stream.aclose()
await write_receive_stream.aclose()