Skip to content

Commit 744731e

Browse files
committed
add unit tests
1 parent 12da0c9 commit 744731e

File tree

2 files changed

+95
-17
lines changed

2 files changed

+95
-17
lines changed

tests/server/fastmcp/test_server.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,21 +1283,40 @@ def prompt_fn(name: str) -> str:
12831283
with pytest.raises(McpError, match="Missing required arguments"):
12841284
await client.get_prompt("prompt_fn")
12851285

1286+
def test_single_tenant_default_false(self):
1287+
"""Test that single_tenant defaults to False."""
1288+
mcp = FastMCP("test")
1289+
assert mcp.settings.single_tenant is False
1290+
1291+
def test_single_tenant_can_be_set_true(self):
1292+
"""Test that single_tenant can be set to True."""
1293+
mcp = FastMCP("test", single_tenant=True)
1294+
assert mcp.settings.single_tenant is True
1295+
1296+
def test_single_tenant_passed_to_session_manager(self):
1297+
"""Test that single_tenant is passed to StreamableHTTPSessionManager."""
1298+
mcp = FastMCP("test", single_tenant=True)
1299+
1300+
# Access the session manager to trigger its creation
1301+
# We need to call streamable_http_app() first to initialize the session manager
1302+
mcp.streamable_http_app()
1303+
session_manager = mcp.session_manager
1304+
assert session_manager.single_tenant is True
1305+
1306+
def test_streamable_http_no_redirect(self) -> None:
1307+
"""Test that streamable HTTP routes are correctly configured."""
1308+
mcp = FastMCP()
1309+
app = mcp.streamable_http_app()
1310+
1311+
# Find routes by type - streamable_http_app creates Route objects, not Mount objects
1312+
streamable_routes = [
1313+
r
1314+
for r in app.routes
1315+
if isinstance(r, Route) and hasattr(r, "path") and r.path == mcp.settings.streamable_http_path
1316+
]
12861317

1287-
def test_streamable_http_no_redirect() -> None:
1288-
"""Test that streamable HTTP routes are correctly configured."""
1289-
mcp = FastMCP()
1290-
app = mcp.streamable_http_app()
1291-
1292-
# Find routes by type - streamable_http_app creates Route objects, not Mount objects
1293-
streamable_routes = [
1294-
r
1295-
for r in app.routes
1296-
if isinstance(r, Route) and hasattr(r, "path") and r.path == mcp.settings.streamable_http_path
1297-
]
1298-
1299-
# Verify routes exist
1300-
assert len(streamable_routes) == 1, "Should have one streamable route"
1318+
# Verify routes exist
1319+
assert len(streamable_routes) == 1, "Should have one streamable route"
13011320

1302-
# Verify path values
1303-
assert streamable_routes[0].path == "/mcp", "Streamable route path should be /mcp"
1321+
# Verify path values
1322+
assert streamable_routes[0].path == "/mcp", "Streamable route path should be /mcp"

tests/server/test_streamable_http_manager.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,73 @@
55

66
import anyio
77
import pytest
8-
from starlette.types import Message
8+
from starlette.requests import Request
9+
from starlette.types import Message, Scope
910

1011
from mcp.server import streamable_http_manager
1112
from mcp.server.lowlevel import Server
1213
from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, StreamableHTTPServerTransport
1314
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
1415

1516

17+
def test_single_tenant_validation():
18+
"""Test that single_tenant=True with stateless=True raises ValueError."""
19+
app = Server("test")
20+
21+
with pytest.raises(ValueError, match="A single-tenant server must stateful"):
22+
StreamableHTTPSessionManager(app=app, single_tenant=True, stateless=True)
23+
24+
25+
def test_single_tenant_default_false():
26+
"""Test that single_tenant defaults to False."""
27+
app = Server("test")
28+
manager = StreamableHTTPSessionManager(app=app)
29+
assert manager.single_tenant is False
30+
31+
32+
def test_single_tenant_can_be_set_true():
33+
"""Test that single_tenant can be set to True."""
34+
app = Server("test")
35+
manager = StreamableHTTPSessionManager(app=app, single_tenant=True)
36+
assert manager.single_tenant is True
37+
38+
39+
@pytest.mark.anyio
40+
async def test_single_tenant_reuses_existing_session():
41+
"""Test that single_tenant mode reuses existing session."""
42+
app = Server("test")
43+
manager = StreamableHTTPSessionManager(app=app, single_tenant=True)
44+
45+
mock_mcp_run = AsyncMock(return_value=None)
46+
# This will be called by StreamableHTTPSessionManager's run_server -> self.app.run
47+
app.run = mock_mcp_run
48+
49+
# Manually add a session to simulate existing session
50+
existing_session_id = "existing-session-123"
51+
mock_transport = AsyncMock()
52+
manager._server_instances[existing_session_id] = mock_transport
53+
54+
# Create a request with different session ID
55+
request_mcp_session_id = "different-session-id"
56+
scope: Scope = {
57+
"type": "http",
58+
"method": "POST",
59+
"path": "/mcp",
60+
"headers": [
61+
(b"content-type", b"application/json"),
62+
(MCP_SESSION_ID_HEADER.encode("latin-1"), request_mcp_session_id.encode("latin-1")),
63+
],
64+
}
65+
66+
async with manager.run():
67+
await manager.handle_request(scope, AsyncMock(), AsyncMock())
68+
headers = Request(scope).headers
69+
modified_session_id = headers[MCP_SESSION_ID_HEADER]
70+
71+
assert modified_session_id == existing_session_id
72+
assert len(manager._server_instances) == 1
73+
74+
1675
@pytest.mark.anyio
1776
async def test_run_can_only_be_called_once():
1877
"""Test that run() can only be called once per instance."""

0 commit comments

Comments
 (0)