Jeremiah Lowin commited on
Commit
b476cc6
·
1 Parent(s): 188aa1e

Remove heavily mocked tests

Browse files
Files changed (2) hide show
  1. tests/cli/test_cursor.py +3 -1
  2. tests/cli/test_run.py +95 -255
tests/cli/test_cursor.py CHANGED
@@ -257,7 +257,9 @@ class TestInstallCursor:
257
  config_data = json.loads(decoded)
258
 
259
  assert "--with-editable" in config_data["args"]
260
- assert "/local/package" in config_data["args"]
 
 
261
  assert "server.py:custom_app" in " ".join(config_data["args"])
262
 
263
  @patch("fastmcp.cli.install.cursor.open_deeplink")
 
257
  config_data = json.loads(decoded)
258
 
259
  assert "--with-editable" in config_data["args"]
260
+ # Check for the editable path in a platform-agnostic way
261
+ editable_path_str = str(Path("/local/package"))
262
+ assert editable_path_str in config_data["args"]
263
  assert "server.py:custom_app" in " ".join(config_data["args"])
264
 
265
  @patch("fastmcp.cli.install.cursor.open_deeplink")
tests/cli/test_run.py CHANGED
@@ -1,17 +1,11 @@
1
  """Tests for the run module functionality."""
2
 
3
- import sys
4
- from unittest.mock import Mock, patch
5
-
6
  import pytest
7
 
8
  from fastmcp.cli.run import (
9
- create_client_server,
10
  import_server,
11
- import_server_with_args,
12
  is_url,
13
  parse_file_path,
14
- run_command,
15
  )
16
 
17
 
@@ -87,275 +81,121 @@ class TestFilePathParsing:
87
  parse_file_path(str(tmp_path))
88
  assert exc_info.value.code == 1
89
 
90
- @pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test")
91
- def test_parse_file_path_windows_drive(self, tmp_path):
92
- """Test parsing Windows path with drive letter."""
93
- # This test would only work on Windows with actual drive letters
94
- # For now, just test the logic doesn't break with colons
95
- test_file = tmp_path / "server.py"
96
- test_file.write_text("# test server")
97
-
98
- # Should handle paths that might look like Windows drives
99
- file_path, server_object = parse_file_path(str(test_file))
100
- assert file_path == test_file.resolve()
101
- assert server_object is None
102
-
103
 
104
  class TestServerImport:
105
- """Test server import functionality."""
106
 
107
- def test_import_server_with_standard_name(self, tmp_path):
108
- """Test importing server with standard object name."""
109
  test_file = tmp_path / "server.py"
110
  test_file.write_text("""
111
  import fastmcp
 
112
  mcp = fastmcp.FastMCP("TestServer")
 
 
 
 
113
  """)
114
 
115
- with patch("fastmcp.cli.run.sys.path") as mock_path:
116
- mock_path.__contains__ = Mock(return_value=False)
117
- mock_path.insert = Mock()
118
-
119
- # Mock the actual import process
120
- with patch(
121
- "fastmcp.cli.run.importlib.util.spec_from_file_location"
122
- ) as mock_spec_from_file:
123
- with patch(
124
- "fastmcp.cli.run.importlib.util.module_from_spec"
125
- ) as mock_module_from_spec:
126
- # Setup mock module
127
- mock_module = Mock()
128
- mock_module.mcp = Mock()
129
- mock_module_from_spec.return_value = mock_module
130
-
131
- # Setup mock spec
132
- mock_spec = Mock()
133
- mock_spec.loader = Mock()
134
- mock_spec_from_file.return_value = mock_spec
135
-
136
- server = import_server(test_file)
137
- assert server == mock_module.mcp
138
-
139
- def test_import_server_with_custom_object(self, tmp_path):
140
- """Test importing server with custom object name."""
141
  test_file = tmp_path / "server.py"
142
  test_file.write_text("""
143
  import fastmcp
144
- my_app = fastmcp.FastMCP("TestServer")
 
 
 
 
 
 
 
 
145
  """)
146
 
147
- with patch("fastmcp.cli.run.sys.path") as mock_path:
148
- mock_path.__contains__ = Mock(return_value=False)
149
- mock_path.insert = Mock()
150
-
151
- with patch(
152
- "fastmcp.cli.run.importlib.util.spec_from_file_location"
153
- ) as mock_spec_from_file:
154
- with patch(
155
- "fastmcp.cli.run.importlib.util.module_from_spec"
156
- ) as mock_module_from_spec:
157
- mock_module = Mock()
158
- mock_module.my_app = Mock()
159
- mock_module_from_spec.return_value = mock_module
160
-
161
- mock_spec = Mock()
162
- mock_spec.loader = Mock()
163
- mock_spec_from_file.return_value = mock_spec
164
-
165
- server = import_server(test_file, "my_app")
166
- assert server == mock_module.my_app
167
-
168
- def test_import_server_no_standard_names(self, tmp_path):
169
- """Test importing server when no standard names exist."""
170
- test_file = tmp_path / "server.py"
171
- test_file.write_text("# No server objects")
172
-
173
- with patch("fastmcp.cli.run.sys.path"):
174
- with patch(
175
- "fastmcp.cli.run.importlib.util.spec_from_file_location"
176
- ) as mock_spec_from_file:
177
- with patch(
178
- "fastmcp.cli.run.importlib.util.module_from_spec"
179
- ) as mock_module_from_spec:
180
- mock_module = Mock()
181
-
182
- # Mock hasattr behavior for standard names
183
- def mock_hasattr(obj, name):
184
- return name not in ["mcp", "server", "app"]
185
-
186
- with patch("builtins.hasattr", side_effect=mock_hasattr):
187
- mock_module_from_spec.return_value = mock_module
188
-
189
- mock_spec = Mock()
190
- mock_spec.loader = Mock()
191
- mock_spec_from_file.return_value = mock_spec
192
-
193
- with pytest.raises(SystemExit) as exc_info:
194
- import_server(test_file)
195
- assert exc_info.value.code == 1
196
-
197
- def test_import_server_nonexistent_object(self, tmp_path):
198
- """Test importing nonexistent server object."""
199
- test_file = tmp_path / "server.py"
200
- test_file.write_text("# No server objects")
201
-
202
- with patch("fastmcp.cli.run.sys.path"):
203
- with patch(
204
- "fastmcp.cli.run.importlib.util.spec_from_file_location"
205
- ) as mock_spec_from_file:
206
- with patch(
207
- "fastmcp.cli.run.importlib.util.module_from_spec"
208
- ) as mock_module_from_spec:
209
- mock_module = Mock()
210
- mock_module.nonexistent = None
211
- mock_module_from_spec.return_value = mock_module
212
-
213
- mock_spec = Mock()
214
- mock_spec.loader = Mock()
215
- mock_spec_from_file.return_value = mock_spec
216
-
217
- with pytest.raises(SystemExit) as exc_info:
218
- import_server(test_file, "nonexistent")
219
- assert exc_info.value.code == 1
220
-
221
-
222
- class TestServerImportWithArgs:
223
- """Test server import with command line arguments."""
224
-
225
- @patch("fastmcp.cli.run.import_server")
226
- def test_import_server_with_args(self, mock_import_server, tmp_path):
227
- """Test importing server with command line arguments."""
228
  test_file = tmp_path / "server.py"
229
- mock_server = Mock()
230
- mock_import_server.return_value = mock_server
231
 
232
- original_argv = sys.argv[:]
233
- try:
234
- result = import_server_with_args(
235
- test_file, "app", ["--config", "test.json", "--debug"]
236
- )
237
 
238
- assert result == mock_server
239
- mock_import_server.assert_called_once_with(test_file, "app")
 
 
240
 
241
- finally:
242
- sys.argv = original_argv
 
 
243
 
244
- @patch("fastmcp.cli.run.import_server")
245
- def test_import_server_no_args(self, mock_import_server, tmp_path):
246
- """Test importing server without command line arguments."""
247
  test_file = tmp_path / "server.py"
248
- mock_server = Mock()
249
- mock_import_server.return_value = mock_server
250
-
251
- result = import_server_with_args(test_file, "app")
252
-
253
- assert result == mock_server
254
- mock_import_server.assert_called_once_with(test_file, "app")
255
-
256
-
257
- class TestClientServer:
258
- """Test client server creation."""
259
-
260
- def test_create_client_server(self):
261
- """Test creating server from client URL."""
262
- # Patch the import at the builtins level since it's a local import
263
- with patch("builtins.__import__") as mock_import:
264
- mock_fastmcp = Mock()
265
- mock_import.return_value = mock_fastmcp
266
-
267
- mock_client = Mock()
268
- mock_server = Mock()
269
- mock_fastmcp.Client.return_value = mock_client
270
- mock_fastmcp.FastMCP.from_client.return_value = mock_server
271
-
272
- result = create_client_server("http://example.com")
273
-
274
- assert result == mock_server
275
- mock_fastmcp.Client.assert_called_once_with("http://example.com")
276
- mock_fastmcp.FastMCP.from_client.assert_called_once_with(mock_client)
277
-
278
- def test_create_client_server_failure(self):
279
- """Test client server creation failure."""
280
- with patch("builtins.__import__") as mock_import:
281
- mock_fastmcp = Mock()
282
- mock_import.return_value = mock_fastmcp
283
- mock_fastmcp.Client.side_effect = Exception("Connection failed")
284
-
285
- with pytest.raises(SystemExit) as exc_info:
286
- create_client_server("http://example.com")
287
- assert exc_info.value.code == 1
288
-
289
-
290
- class TestRunCommand:
291
- """Test the main run command functionality."""
292
-
293
- @patch("fastmcp.cli.run.create_client_server")
294
- def test_run_command_url(self, mock_create_client_server):
295
- """Test running command with URL."""
296
- mock_server = Mock()
297
- mock_create_client_server.return_value = mock_server
298
-
299
- run_command("http://example.com")
300
-
301
- mock_create_client_server.assert_called_once_with("http://example.com")
302
- mock_server.run.assert_called_once()
303
-
304
- @patch("fastmcp.cli.run.import_server_with_args")
305
- @patch("fastmcp.cli.run.parse_file_path")
306
- def test_run_command_file(self, mock_parse_file_path, mock_import_server):
307
- """Test running command with file path."""
308
- mock_file = Mock()
309
- mock_parse_file_path.return_value = (mock_file, "app")
310
- mock_server = Mock()
311
- mock_server.name = "TestServer"
312
- mock_import_server.return_value = mock_server
313
-
314
- run_command("server.py:app")
315
-
316
- mock_parse_file_path.assert_called_once_with("server.py:app")
317
- mock_import_server.assert_called_once_with(mock_file, "app", None)
318
- mock_server.run.assert_called_once()
319
-
320
- @patch("fastmcp.cli.run.import_server_with_args")
321
- @patch("fastmcp.cli.run.parse_file_path")
322
- def test_run_command_with_options(self, mock_parse_file_path, mock_import_server):
323
- """Test running command with various options."""
324
- mock_file = Mock()
325
- mock_parse_file_path.return_value = (mock_file, None)
326
- mock_server = Mock()
327
- mock_server.name = "TestServer"
328
- mock_import_server.return_value = mock_server
329
-
330
- run_command(
331
- "server.py",
332
- transport="http",
333
- host="localhost",
334
- port=8080,
335
- log_level="DEBUG",
336
- server_args=["--config", "test.json"],
337
- show_banner=False,
338
- )
339
-
340
- mock_server.run.assert_called_once_with(
341
- transport="http",
342
- host="localhost",
343
- port=8080,
344
- log_level="DEBUG",
345
- show_banner=False,
346
- )
347
-
348
- @patch("fastmcp.cli.run.import_server_with_args")
349
- @patch("fastmcp.cli.run.parse_file_path")
350
- def test_run_command_server_failure(self, mock_parse_file_path, mock_import_server):
351
- """Test run command when server run fails."""
352
- mock_file = Mock()
353
- mock_parse_file_path.return_value = (mock_file, None)
354
- mock_server = Mock()
355
- mock_server.name = "TestServer"
356
- mock_server.run.side_effect = Exception("Server failed")
357
- mock_import_server.return_value = mock_server
358
 
359
  with pytest.raises(SystemExit) as exc_info:
360
- run_command("server.py")
361
  assert exc_info.value.code == 1
 
1
  """Tests for the run module functionality."""
2
 
 
 
 
3
  import pytest
4
 
5
  from fastmcp.cli.run import (
 
6
  import_server,
 
7
  is_url,
8
  parse_file_path,
 
9
  )
10
 
11
 
 
81
  parse_file_path(str(tmp_path))
82
  assert exc_info.value.code == 1
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  class TestServerImport:
86
+ """Test server import functionality using real files."""
87
 
88
+ async def test_import_server_basic_mcp(self, tmp_path):
89
+ """Test importing server with basic FastMCP server."""
90
  test_file = tmp_path / "server.py"
91
  test_file.write_text("""
92
  import fastmcp
93
+
94
  mcp = fastmcp.FastMCP("TestServer")
95
+
96
+ @mcp.tool
97
+ def greet(name: str) -> str:
98
+ return f"Hello, {name}!"
99
  """)
100
 
101
+ server = import_server(test_file)
102
+ assert server.name == "TestServer"
103
+ tools = await server.get_tools()
104
+ assert "greet" in tools
105
+
106
+ async def test_import_server_with_main_block(self, tmp_path):
107
+ """Test importing server with if __name__ == '__main__' block."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  test_file = tmp_path / "server.py"
109
  test_file.write_text("""
110
  import fastmcp
111
+
112
+ app = fastmcp.FastMCP("MainServer")
113
+
114
+ @app.tool
115
+ def calculate(x: int, y: int) -> int:
116
+ return x + y
117
+
118
+ if __name__ == "__main__":
119
+ app.run()
120
  """)
121
 
122
+ server = import_server(test_file)
123
+ assert server.name == "MainServer"
124
+ tools = await server.get_tools()
125
+ assert "calculate" in tools
126
+
127
+ def test_import_server_standard_names(self, tmp_path):
128
+ """Test automatic detection of standard names (mcp, server, app)."""
129
+ # Test with 'mcp' name
130
+ mcp_file = tmp_path / "mcp_server.py"
131
+ mcp_file.write_text("""
132
+ import fastmcp
133
+ mcp = fastmcp.FastMCP("MCPServer")
134
+ """)
135
+
136
+ server = import_server(mcp_file)
137
+ assert server.name == "MCPServer"
138
+
139
+ # Test with 'server' name
140
+ server_file = tmp_path / "server_server.py"
141
+ server_file.write_text("""
142
+ import fastmcp
143
+ server = fastmcp.FastMCP("ServerServer")
144
+ """)
145
+
146
+ server = import_server(server_file)
147
+ assert server.name == "ServerServer"
148
+
149
+ # Test with 'app' name
150
+ app_file = tmp_path / "app_server.py"
151
+ app_file.write_text("""
152
+ import fastmcp
153
+ app = fastmcp.FastMCP("AppServer")
154
+ """)
155
+
156
+ server = import_server(app_file)
157
+ assert server.name == "AppServer"
158
+
159
+ async def test_import_server_nonstandard_name(self, tmp_path):
160
+ """Test importing server with non-standard object name."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  test_file = tmp_path / "server.py"
162
+ test_file.write_text("""
163
+ import fastmcp
164
 
165
+ my_custom_server = fastmcp.FastMCP("CustomServer")
 
 
 
 
166
 
167
+ @my_custom_server.tool
168
+ def custom_tool() -> str:
169
+ return "custom"
170
+ """)
171
 
172
+ server = import_server(test_file, "my_custom_server")
173
+ assert server.name == "CustomServer"
174
+ tools = await server.get_tools()
175
+ assert "custom_tool" in tools
176
 
177
+ def test_import_server_no_standard_names_fails(self, tmp_path):
178
+ """Test importing server when no standard names exist fails."""
 
179
  test_file = tmp_path / "server.py"
180
+ test_file.write_text("""
181
+ import fastmcp
182
+
183
+ other_name = fastmcp.FastMCP("OtherServer")
184
+ """)
185
+
186
+ with pytest.raises(SystemExit) as exc_info:
187
+ import_server(test_file)
188
+ assert exc_info.value.code == 1
189
+
190
+ def test_import_server_nonexistent_object_fails(self, tmp_path):
191
+ """Test importing nonexistent server object fails."""
192
+ test_file = tmp_path / "server.py"
193
+ test_file.write_text("""
194
+ import fastmcp
195
+
196
+ mcp = fastmcp.FastMCP("TestServer")
197
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
  with pytest.raises(SystemExit) as exc_info:
200
+ import_server(test_file, "nonexistent")
201
  assert exc_info.value.code == 1