zzstoatzz commited on
Commit
cb34145
·
1 Parent(s): 1b89739

add tests

Browse files
Files changed (1) hide show
  1. tests/server/test_logging.py +176 -0
tests/server/test_logging.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import logging
3
+ from unittest.mock import AsyncMock, Mock, patch
4
+
5
+ import pytest
6
+
7
+ from fastmcp.server.server import FastMCP
8
+
9
+
10
+ class CustomLogFormatterForTest(logging.Formatter):
11
+ def format(self, record: logging.LogRecord) -> str:
12
+ return f"TEST_FORMAT::{record.levelname}::{record.name}::{record.getMessage()}"
13
+
14
+
15
+ @pytest.fixture
16
+ def mcp_server() -> FastMCP:
17
+ return FastMCP(name="TestLogServer")
18
+
19
+
20
+ @patch("fastmcp.server.server.uvicorn.Server")
21
+ @patch("fastmcp.server.server.uvicorn.Config")
22
+ async def test_uvicorn_logging_default_level(
23
+ mock_uvicorn_config_constructor: Mock,
24
+ mock_uvicorn_server_constructor: Mock,
25
+ mcp_server: FastMCP,
26
+ ):
27
+ """Tests that FastMCP passes log_level to uvicorn.Config if no log_config is given."""
28
+ mock_server_instance = AsyncMock()
29
+ mock_uvicorn_server_constructor.return_value = mock_server_instance
30
+ serve_finished_event = asyncio.Event()
31
+ mock_server_instance.serve.side_effect = serve_finished_event.wait
32
+
33
+ test_log_level = "warning"
34
+
35
+ server_task = asyncio.create_task(
36
+ mcp_server.run_http_async(log_level=test_log_level, port=8003)
37
+ )
38
+ await asyncio.sleep(0.01)
39
+
40
+ mock_uvicorn_config_constructor.assert_called_once()
41
+ _, kwargs_config = mock_uvicorn_config_constructor.call_args
42
+
43
+ assert kwargs_config.get("log_level") == test_log_level.lower()
44
+ assert "log_config" not in kwargs_config
45
+
46
+ mock_uvicorn_server_constructor.assert_called_once_with(
47
+ mock_uvicorn_config_constructor.return_value
48
+ )
49
+ mock_server_instance.serve.assert_awaited_once()
50
+
51
+ server_task.cancel()
52
+ with pytest.raises(asyncio.CancelledError):
53
+ await server_task
54
+
55
+
56
+ @patch("fastmcp.server.server.uvicorn.Server")
57
+ @patch("fastmcp.server.server.uvicorn.Config")
58
+ async def test_uvicorn_logging_with_custom_log_config(
59
+ mock_uvicorn_config_constructor: Mock,
60
+ mock_uvicorn_server_constructor: Mock,
61
+ mcp_server: FastMCP,
62
+ ):
63
+ """Tests that FastMCP passes log_config to uvicorn.Config and not log_level."""
64
+ mock_server_instance = AsyncMock()
65
+ mock_uvicorn_server_constructor.return_value = mock_server_instance
66
+ serve_finished_event = asyncio.Event()
67
+ mock_server_instance.serve.side_effect = serve_finished_event.wait
68
+
69
+ sample_log_config = {
70
+ "version": 1,
71
+ "disable_existing_loggers": False,
72
+ "formatters": {
73
+ "test_formatter": {
74
+ "()": "tests.server.test_logging.CustomLogFormatterForTest"
75
+ }
76
+ },
77
+ "handlers": {
78
+ "test_handler": {
79
+ "formatter": "test_formatter",
80
+ "class": "logging.StreamHandler",
81
+ "stream": "ext://sys.stdout",
82
+ }
83
+ },
84
+ "loggers": {
85
+ "uvicorn.error": {
86
+ "handlers": ["test_handler"],
87
+ "level": "INFO",
88
+ "propagate": False,
89
+ }
90
+ },
91
+ }
92
+
93
+ server_task = asyncio.create_task(
94
+ mcp_server.run_http_async(
95
+ uvicorn_config={"log_config": sample_log_config}, port=8004
96
+ )
97
+ )
98
+ await asyncio.sleep(0.01)
99
+
100
+ mock_uvicorn_config_constructor.assert_called_once()
101
+ _, kwargs_config = mock_uvicorn_config_constructor.call_args
102
+
103
+ assert kwargs_config.get("log_config") == sample_log_config
104
+ assert "log_level" not in kwargs_config
105
+
106
+ mock_uvicorn_server_constructor.assert_called_once_with(
107
+ mock_uvicorn_config_constructor.return_value
108
+ )
109
+ mock_server_instance.serve.assert_awaited_once()
110
+
111
+ server_task.cancel()
112
+ with pytest.raises(asyncio.CancelledError):
113
+ await server_task
114
+
115
+
116
+ @patch("fastmcp.server.server.uvicorn.Server")
117
+ @patch("fastmcp.server.server.uvicorn.Config")
118
+ async def test_uvicorn_logging_custom_log_config_overrides_log_level_param(
119
+ mock_uvicorn_config_constructor: Mock,
120
+ mock_uvicorn_server_constructor: Mock,
121
+ mcp_server: FastMCP,
122
+ ):
123
+ """Tests log_config precedence if log_level is also passed to run_http_async."""
124
+ mock_server_instance = AsyncMock()
125
+ mock_uvicorn_server_constructor.return_value = mock_server_instance
126
+ serve_finished_event = asyncio.Event()
127
+ mock_server_instance.serve.side_effect = serve_finished_event.wait
128
+
129
+ sample_log_config = {
130
+ "version": 1,
131
+ "disable_existing_loggers": False,
132
+ "formatters": {
133
+ "test_formatter": {
134
+ "()": "tests.server.test_logging.CustomLogFormatterForTest"
135
+ }
136
+ },
137
+ "handlers": {
138
+ "test_handler": {
139
+ "formatter": "test_formatter",
140
+ "class": "logging.StreamHandler",
141
+ "stream": "ext://sys.stdout",
142
+ }
143
+ },
144
+ "loggers": {
145
+ "uvicorn.error": {
146
+ "handlers": ["test_handler"],
147
+ "level": "INFO",
148
+ "propagate": False,
149
+ }
150
+ },
151
+ }
152
+ explicit_log_level = "debug"
153
+
154
+ server_task = asyncio.create_task(
155
+ mcp_server.run_http_async(
156
+ log_level=explicit_log_level,
157
+ uvicorn_config={"log_config": sample_log_config},
158
+ port=8005,
159
+ )
160
+ )
161
+ await asyncio.sleep(0.01)
162
+
163
+ mock_uvicorn_config_constructor.assert_called_once()
164
+ _, kwargs_config = mock_uvicorn_config_constructor.call_args
165
+
166
+ assert kwargs_config.get("log_config") == sample_log_config
167
+ assert "log_level" not in kwargs_config
168
+
169
+ mock_uvicorn_server_constructor.assert_called_once_with(
170
+ mock_uvicorn_config_constructor.return_value
171
+ )
172
+ mock_server_instance.serve.assert_awaited_once()
173
+
174
+ server_task.cancel()
175
+ with pytest.raises(asyncio.CancelledError):
176
+ await server_task