Jeremiah Lowin commited on
Commit
752553b
·
1 Parent(s): 66a37df

Fix static issues and platform dependencies

Browse files
Files changed (1) hide show
  1. tests/cli/test_cli.py +177 -121
tests/cli/test_cli.py CHANGED
@@ -1,6 +1,5 @@
1
  """Tests for the CLI module."""
2
 
3
-
4
  import subprocess
5
  from pathlib import Path
6
  from unittest.mock import MagicMock, Mock, patch
@@ -10,7 +9,6 @@ from typer.testing import CliRunner
10
 
11
  from fastmcp.cli import cli
12
 
13
-
14
  # Set up test runner
15
  runner = CliRunner()
16
 
@@ -107,7 +105,10 @@ class TestHelperFunctions:
107
  """Test parsing valid environment variables."""
108
  assert cli._parse_env_var("KEY=VALUE") == ("KEY", "VALUE")
109
  assert cli._parse_env_var("KEY=") == ("KEY", "")
110
- assert cli._parse_env_var("KEY=VALUE=WITH=EQUALS") == ("KEY", "VALUE=WITH=EQUALS")
 
 
 
111
  assert cli._parse_env_var(" KEY = VALUE ") == ("KEY", "VALUE")
112
 
113
  def test_build_uv_command_basic(self):
@@ -117,96 +118,125 @@ class TestHelperFunctions:
117
 
118
  def test_build_uv_command_with_editable(self):
119
  """Test building uv command with editable flag."""
120
- cmd = cli._build_uv_command("file.py", with_editable=Path("/path/to/project"))
 
121
  assert cmd == [
122
- "uv", "run", "--with", "fastmcp",
123
- "--with-editable", "/path/to/project",
124
- "fastmcp", "run", "file.py"
 
 
 
 
 
 
125
  ]
126
 
127
  def test_build_uv_command_with_packages(self):
128
  """Test building uv command with additional packages."""
129
  cmd = cli._build_uv_command("file.py", with_packages=["pkg1", "pkg2"])
130
  assert cmd == [
131
- "uv", "run", "--with", "fastmcp",
132
- "--with", "pkg1", "--with", "pkg2",
133
- "fastmcp", "run", "file.py"
 
 
 
 
 
 
 
 
134
  ]
135
 
136
  def test_build_uv_command_full(self):
137
  """Test building full uv command with all options."""
 
138
  cmd = cli._build_uv_command(
139
- "file.py:server",
140
- with_editable=Path("/path/to/project"),
141
  with_packages=["pkg1", "pkg2"],
142
  )
143
  assert cmd == [
144
- "uv", "run", "--with", "fastmcp",
145
- "--with-editable", "/path/to/project",
146
- "--with", "pkg1", "--with", "pkg2",
147
- "fastmcp", "run", "file.py:server"
 
 
 
 
 
 
 
 
 
148
  ]
149
 
150
  def test_parse_file_path_simple(self):
151
  """Test parsing simple file path."""
152
- with patch("pathlib.Path.exists") as mock_exists, \
153
- patch("pathlib.Path.is_file") as mock_is_file, \
154
- patch("pathlib.Path.expanduser") as mock_expanduser, \
155
- patch("pathlib.Path.resolve") as mock_resolve:
156
-
 
157
  mock_exists.return_value = True
158
  mock_is_file.return_value = True
159
  mock_expanduser.return_value = Path("file.py")
160
  mock_resolve.return_value = Path("file.py")
161
-
162
  path, obj = cli._parse_file_path("file.py")
163
  assert path == Path("file.py")
164
  assert obj is None
165
 
166
  def test_parse_file_path_with_object(self):
167
  """Test parsing file path with object."""
168
- with patch("pathlib.Path.exists") as mock_exists, \
169
- patch("pathlib.Path.is_file") as mock_is_file, \
170
- patch("pathlib.Path.expanduser") as mock_expanduser, \
171
- patch("pathlib.Path.resolve") as mock_resolve:
172
-
 
173
  mock_exists.return_value = True
174
  mock_is_file.return_value = True
175
  mock_expanduser.return_value = Path("file.py")
176
  mock_resolve.return_value = Path("file.py")
177
-
178
  path, obj = cli._parse_file_path("file.py:server")
179
  assert path == Path("file.py")
180
  assert obj == "server"
181
 
182
  def test_parse_file_path_windows(self):
183
  """Test parsing Windows file path."""
184
- with patch("pathlib.Path.exists") as mock_exists, \
185
- patch("pathlib.Path.is_file") as mock_is_file, \
186
- patch("pathlib.Path.expanduser") as mock_expanduser, \
187
- patch("pathlib.Path.resolve") as mock_resolve:
188
-
 
189
  mock_exists.return_value = True
190
  mock_is_file.return_value = True
191
  mock_expanduser.return_value = Path("C:/path/file.py")
192
  mock_resolve.return_value = Path("C:/path/file.py")
193
-
194
  path, obj = cli._parse_file_path("C:/path/file.py:server")
195
  assert path == Path("C:/path/file.py")
196
  assert obj == "server"
197
 
198
  def test_parse_file_path_not_file(self, mock_exit, mock_logger):
199
  """Test parsing path that is not a file."""
200
- with patch("pathlib.Path.exists") as mock_exists, \
201
- patch("pathlib.Path.is_file") as mock_is_file, \
202
- patch("pathlib.Path.expanduser") as mock_expanduser, \
203
- patch("pathlib.Path.resolve") as mock_resolve:
204
-
 
205
  mock_exists.return_value = True
206
  mock_is_file.return_value = False
207
  mock_expanduser.return_value = Path("directory")
208
  mock_resolve.return_value = Path("directory")
209
-
210
  cli._parse_file_path("directory")
211
  mock_logger.error.assert_called_once()
212
  mock_exit.assert_called_once_with(1)
@@ -228,12 +258,13 @@ class TestDevCommand:
228
 
229
  def test_dev_command_success(self, temp_python_file, mock_logger):
230
  """Test successful dev command execution."""
231
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
232
- patch("fastmcp.cli.cli._import_server") as mock_import, \
233
- patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx, \
234
- patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv, \
235
- patch("subprocess.run") as mock_run:
236
-
 
237
  mock_parse.return_value = (temp_python_file, None)
238
  mock_server = MagicMock()
239
  mock_server.dependencies = ["extra_dep"]
@@ -241,35 +272,36 @@ class TestDevCommand:
241
  mock_get_npx.return_value = "npx"
242
  mock_build_uv.return_value = ["uv", "command"]
243
  mock_run.return_value = MagicMock(returncode=0)
244
-
245
  result = runner.invoke(cli.app, ["dev", str(temp_python_file)])
246
  assert result.exit_code == 0
247
  mock_run.assert_called_once()
248
-
249
  # Check dependencies were passed correctly
250
  mock_build_uv.assert_called_once_with(
251
- str(temp_python_file),
252
- None,
253
- ["extra_dep"]
254
  )
255
 
256
  def test_dev_command_with_ui_port(self, temp_python_file):
257
  """Test dev command with UI port."""
258
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
259
- patch("fastmcp.cli.cli._import_server") as mock_import, \
260
- patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx, \
261
- patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv, \
262
- patch("subprocess.run") as mock_run:
263
-
 
264
  mock_parse.return_value = (temp_python_file, None)
265
  mock_import.return_value = MagicMock(dependencies=[])
266
  mock_get_npx.return_value = "npx"
267
  mock_build_uv.return_value = ["uv", "command"]
268
  mock_run.return_value = MagicMock(returncode=0)
269
-
270
- result = runner.invoke(cli.app, ["dev", str(temp_python_file), "--ui-port", "3000"])
 
 
271
  assert result.exit_code == 0
272
-
273
  # Check environment variables were set
274
  env = mock_run.call_args[1]["env"]
275
  assert "CLIENT_PORT" in env
@@ -277,21 +309,24 @@ class TestDevCommand:
277
 
278
  def test_dev_command_with_server_port(self, temp_python_file):
279
  """Test dev command with server port."""
280
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
281
- patch("fastmcp.cli.cli._import_server") as mock_import, \
282
- patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx, \
283
- patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv, \
284
- patch("subprocess.run") as mock_run:
285
-
 
286
  mock_parse.return_value = (temp_python_file, None)
287
  mock_import.return_value = MagicMock(dependencies=[])
288
  mock_get_npx.return_value = "npx"
289
  mock_build_uv.return_value = ["uv", "command"]
290
  mock_run.return_value = MagicMock(returncode=0)
291
-
292
- result = runner.invoke(cli.app, ["dev", str(temp_python_file), "--server-port", "8080"])
 
 
293
  assert result.exit_code == 0
294
-
295
  # Check environment variables were set
296
  env = mock_run.call_args[1]["env"]
297
  assert "SERVER_PORT" in env
@@ -299,24 +334,24 @@ class TestDevCommand:
299
 
300
  def test_dev_command_inspector_version(self, temp_python_file):
301
  """Test dev command with specific inspector version."""
302
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
303
- patch("fastmcp.cli.cli._import_server") as mock_import, \
304
- patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx, \
305
- patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv, \
306
- patch("subprocess.run") as mock_run:
307
-
 
308
  mock_parse.return_value = (temp_python_file, None)
309
  mock_import.return_value = MagicMock(dependencies=[])
310
  mock_get_npx.return_value = "npx"
311
  mock_build_uv.return_value = ["uv", "command"]
312
  mock_run.return_value = MagicMock(returncode=0)
313
-
314
- result = runner.invoke(cli.app, [
315
- "dev", str(temp_python_file),
316
- "--inspector-version", "1.0.0"
317
- ])
318
  assert result.exit_code == 0
319
-
320
  # Check inspector version was used
321
  inspector_cmd = mock_run.call_args[0][0][1]
322
  assert inspector_cmd == "@modelcontextprotocol/inspector@1.0.0"
@@ -327,96 +362,117 @@ class TestRunCommand:
327
 
328
  def test_run_command_success(self, temp_python_file, mock_logger):
329
  """Test successful run command execution."""
330
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
331
- patch("fastmcp.cli.cli._import_server") as mock_import:
332
-
 
333
  mock_parse.return_value = (temp_python_file, None)
334
  mock_server = MagicMock()
335
  mock_server.name = "test_server"
336
  mock_import.return_value = mock_server
337
-
338
  result = runner.invoke(cli.app, ["run", str(temp_python_file)])
339
  assert result.exit_code == 0
340
  mock_server.run.assert_called_once_with()
341
- mock_logger.info.assert_called_with(f'Found server "test_server" in {temp_python_file}')
 
 
342
 
343
  def test_run_command_with_transport(self, temp_python_file):
344
  """Test run command with transport option."""
345
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
346
- patch("fastmcp.cli.cli._import_server") as mock_import:
347
-
 
348
  mock_parse.return_value = (temp_python_file, None)
349
  mock_server = MagicMock()
350
  mock_server.name = "test_server"
351
  mock_import.return_value = mock_server
352
-
353
- result = runner.invoke(cli.app, ["run", str(temp_python_file), "--transport", "sse"])
 
 
354
  assert result.exit_code == 0
355
  mock_server.run.assert_called_once_with(transport="sse")
356
 
357
  def test_run_command_with_host(self, temp_python_file):
358
  """Test run command with host option."""
359
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
360
- patch("fastmcp.cli.cli._import_server") as mock_import:
361
-
 
362
  mock_parse.return_value = (temp_python_file, None)
363
  mock_server = MagicMock()
364
  mock_server.name = "test_server"
365
  mock_import.return_value = mock_server
366
-
367
- result = runner.invoke(cli.app, ["run", str(temp_python_file), "--host", "0.0.0.0"])
 
 
368
  assert result.exit_code == 0
369
  mock_server.run.assert_called_once_with(host="0.0.0.0")
370
 
371
  def test_run_command_with_port(self, temp_python_file):
372
  """Test run command with port option."""
373
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
374
- patch("fastmcp.cli.cli._import_server") as mock_import:
375
-
 
376
  mock_parse.return_value = (temp_python_file, None)
377
  mock_server = MagicMock()
378
  mock_server.name = "test_server"
379
  mock_import.return_value = mock_server
380
-
381
- result = runner.invoke(cli.app, ["run", str(temp_python_file), "--port", "8080"])
 
 
382
  assert result.exit_code == 0
383
  mock_server.run.assert_called_once_with(port=8080)
384
 
385
  def test_run_command_with_log_level(self, temp_python_file):
386
  """Test run command with log level option."""
387
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
388
- patch("fastmcp.cli.cli._import_server") as mock_import:
389
-
 
390
  mock_parse.return_value = (temp_python_file, None)
391
  mock_server = MagicMock()
392
  mock_server.name = "test_server"
393
  mock_import.return_value = mock_server
394
-
395
- result = runner.invoke(cli.app, ["run", str(temp_python_file), "--log-level", "DEBUG"])
 
 
396
  assert result.exit_code == 0
397
  mock_server.run.assert_called_once_with(log_level="DEBUG")
398
 
399
  def test_run_command_with_multiple_options(self, temp_python_file):
400
  """Test run command with multiple options."""
401
- with patch("fastmcp.cli.cli._parse_file_path") as mock_parse, \
402
- patch("fastmcp.cli.cli._import_server") as mock_import:
403
-
 
404
  mock_parse.return_value = (temp_python_file, None)
405
  mock_server = MagicMock()
406
  mock_server.name = "test_server"
407
  mock_import.return_value = mock_server
408
-
409
- result = runner.invoke(cli.app, [
410
- "run", str(temp_python_file),
411
- "--transport", "sse",
412
- "--host", "0.0.0.0",
413
- "--port", "8080",
414
- "--log-level", "DEBUG"
415
- ])
 
 
 
 
 
 
 
 
416
  assert result.exit_code == 0
417
  mock_server.run.assert_called_once_with(
418
- transport="sse",
419
- host="0.0.0.0",
420
- port=8080,
421
- log_level="DEBUG"
422
  )
 
1
  """Tests for the CLI module."""
2
 
 
3
  import subprocess
4
  from pathlib import Path
5
  from unittest.mock import MagicMock, Mock, patch
 
9
 
10
  from fastmcp.cli import cli
11
 
 
12
  # Set up test runner
13
  runner = CliRunner()
14
 
 
105
  """Test parsing valid environment variables."""
106
  assert cli._parse_env_var("KEY=VALUE") == ("KEY", "VALUE")
107
  assert cli._parse_env_var("KEY=") == ("KEY", "")
108
+ assert cli._parse_env_var("KEY=VALUE=WITH=EQUALS") == (
109
+ "KEY",
110
+ "VALUE=WITH=EQUALS",
111
+ )
112
  assert cli._parse_env_var(" KEY = VALUE ") == ("KEY", "VALUE")
113
 
114
  def test_build_uv_command_basic(self):
 
118
 
119
  def test_build_uv_command_with_editable(self):
120
  """Test building uv command with editable flag."""
121
+ project_path = Path("/path/to/project")
122
+ cmd = cli._build_uv_command("file.py", with_editable=project_path)
123
  assert cmd == [
124
+ "uv",
125
+ "run",
126
+ "--with",
127
+ "fastmcp",
128
+ "--with-editable",
129
+ str(project_path),
130
+ "fastmcp",
131
+ "run",
132
+ "file.py",
133
  ]
134
 
135
  def test_build_uv_command_with_packages(self):
136
  """Test building uv command with additional packages."""
137
  cmd = cli._build_uv_command("file.py", with_packages=["pkg1", "pkg2"])
138
  assert cmd == [
139
+ "uv",
140
+ "run",
141
+ "--with",
142
+ "fastmcp",
143
+ "--with",
144
+ "pkg1",
145
+ "--with",
146
+ "pkg2",
147
+ "fastmcp",
148
+ "run",
149
+ "file.py",
150
  ]
151
 
152
  def test_build_uv_command_full(self):
153
  """Test building full uv command with all options."""
154
+ project_path = Path("/path/to/project")
155
  cmd = cli._build_uv_command(
156
+ "file.py:server",
157
+ with_editable=project_path,
158
  with_packages=["pkg1", "pkg2"],
159
  )
160
  assert cmd == [
161
+ "uv",
162
+ "run",
163
+ "--with",
164
+ "fastmcp",
165
+ "--with-editable",
166
+ str(project_path),
167
+ "--with",
168
+ "pkg1",
169
+ "--with",
170
+ "pkg2",
171
+ "fastmcp",
172
+ "run",
173
+ "file.py:server",
174
  ]
175
 
176
  def test_parse_file_path_simple(self):
177
  """Test parsing simple file path."""
178
+ with (
179
+ patch("pathlib.Path.exists") as mock_exists,
180
+ patch("pathlib.Path.is_file") as mock_is_file,
181
+ patch("pathlib.Path.expanduser") as mock_expanduser,
182
+ patch("pathlib.Path.resolve") as mock_resolve,
183
+ ):
184
  mock_exists.return_value = True
185
  mock_is_file.return_value = True
186
  mock_expanduser.return_value = Path("file.py")
187
  mock_resolve.return_value = Path("file.py")
188
+
189
  path, obj = cli._parse_file_path("file.py")
190
  assert path == Path("file.py")
191
  assert obj is None
192
 
193
  def test_parse_file_path_with_object(self):
194
  """Test parsing file path with object."""
195
+ with (
196
+ patch("pathlib.Path.exists") as mock_exists,
197
+ patch("pathlib.Path.is_file") as mock_is_file,
198
+ patch("pathlib.Path.expanduser") as mock_expanduser,
199
+ patch("pathlib.Path.resolve") as mock_resolve,
200
+ ):
201
  mock_exists.return_value = True
202
  mock_is_file.return_value = True
203
  mock_expanduser.return_value = Path("file.py")
204
  mock_resolve.return_value = Path("file.py")
205
+
206
  path, obj = cli._parse_file_path("file.py:server")
207
  assert path == Path("file.py")
208
  assert obj == "server"
209
 
210
  def test_parse_file_path_windows(self):
211
  """Test parsing Windows file path."""
212
+ with (
213
+ patch("pathlib.Path.exists") as mock_exists,
214
+ patch("pathlib.Path.is_file") as mock_is_file,
215
+ patch("pathlib.Path.expanduser") as mock_expanduser,
216
+ patch("pathlib.Path.resolve") as mock_resolve,
217
+ ):
218
  mock_exists.return_value = True
219
  mock_is_file.return_value = True
220
  mock_expanduser.return_value = Path("C:/path/file.py")
221
  mock_resolve.return_value = Path("C:/path/file.py")
222
+
223
  path, obj = cli._parse_file_path("C:/path/file.py:server")
224
  assert path == Path("C:/path/file.py")
225
  assert obj == "server"
226
 
227
  def test_parse_file_path_not_file(self, mock_exit, mock_logger):
228
  """Test parsing path that is not a file."""
229
+ with (
230
+ patch("pathlib.Path.exists") as mock_exists,
231
+ patch("pathlib.Path.is_file") as mock_is_file,
232
+ patch("pathlib.Path.expanduser") as mock_expanduser,
233
+ patch("pathlib.Path.resolve") as mock_resolve,
234
+ ):
235
  mock_exists.return_value = True
236
  mock_is_file.return_value = False
237
  mock_expanduser.return_value = Path("directory")
238
  mock_resolve.return_value = Path("directory")
239
+
240
  cli._parse_file_path("directory")
241
  mock_logger.error.assert_called_once()
242
  mock_exit.assert_called_once_with(1)
 
258
 
259
  def test_dev_command_success(self, temp_python_file, mock_logger):
260
  """Test successful dev command execution."""
261
+ with (
262
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
263
+ patch("fastmcp.cli.cli._import_server") as mock_import,
264
+ patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx,
265
+ patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv,
266
+ patch("subprocess.run") as mock_run,
267
+ ):
268
  mock_parse.return_value = (temp_python_file, None)
269
  mock_server = MagicMock()
270
  mock_server.dependencies = ["extra_dep"]
 
272
  mock_get_npx.return_value = "npx"
273
  mock_build_uv.return_value = ["uv", "command"]
274
  mock_run.return_value = MagicMock(returncode=0)
275
+
276
  result = runner.invoke(cli.app, ["dev", str(temp_python_file)])
277
  assert result.exit_code == 0
278
  mock_run.assert_called_once()
279
+
280
  # Check dependencies were passed correctly
281
  mock_build_uv.assert_called_once_with(
282
+ str(temp_python_file), None, ["extra_dep"]
 
 
283
  )
284
 
285
  def test_dev_command_with_ui_port(self, temp_python_file):
286
  """Test dev command with UI port."""
287
+ with (
288
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
289
+ patch("fastmcp.cli.cli._import_server") as mock_import,
290
+ patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx,
291
+ patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv,
292
+ patch("subprocess.run") as mock_run,
293
+ ):
294
  mock_parse.return_value = (temp_python_file, None)
295
  mock_import.return_value = MagicMock(dependencies=[])
296
  mock_get_npx.return_value = "npx"
297
  mock_build_uv.return_value = ["uv", "command"]
298
  mock_run.return_value = MagicMock(returncode=0)
299
+
300
+ result = runner.invoke(
301
+ cli.app, ["dev", str(temp_python_file), "--ui-port", "3000"]
302
+ )
303
  assert result.exit_code == 0
304
+
305
  # Check environment variables were set
306
  env = mock_run.call_args[1]["env"]
307
  assert "CLIENT_PORT" in env
 
309
 
310
  def test_dev_command_with_server_port(self, temp_python_file):
311
  """Test dev command with server port."""
312
+ with (
313
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
314
+ patch("fastmcp.cli.cli._import_server") as mock_import,
315
+ patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx,
316
+ patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv,
317
+ patch("subprocess.run") as mock_run,
318
+ ):
319
  mock_parse.return_value = (temp_python_file, None)
320
  mock_import.return_value = MagicMock(dependencies=[])
321
  mock_get_npx.return_value = "npx"
322
  mock_build_uv.return_value = ["uv", "command"]
323
  mock_run.return_value = MagicMock(returncode=0)
324
+
325
+ result = runner.invoke(
326
+ cli.app, ["dev", str(temp_python_file), "--server-port", "8080"]
327
+ )
328
  assert result.exit_code == 0
329
+
330
  # Check environment variables were set
331
  env = mock_run.call_args[1]["env"]
332
  assert "SERVER_PORT" in env
 
334
 
335
  def test_dev_command_inspector_version(self, temp_python_file):
336
  """Test dev command with specific inspector version."""
337
+ with (
338
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
339
+ patch("fastmcp.cli.cli._import_server") as mock_import,
340
+ patch("fastmcp.cli.cli._get_npx_command") as mock_get_npx,
341
+ patch("fastmcp.cli.cli._build_uv_command") as mock_build_uv,
342
+ patch("subprocess.run") as mock_run,
343
+ ):
344
  mock_parse.return_value = (temp_python_file, None)
345
  mock_import.return_value = MagicMock(dependencies=[])
346
  mock_get_npx.return_value = "npx"
347
  mock_build_uv.return_value = ["uv", "command"]
348
  mock_run.return_value = MagicMock(returncode=0)
349
+
350
+ result = runner.invoke(
351
+ cli.app, ["dev", str(temp_python_file), "--inspector-version", "1.0.0"]
352
+ )
 
353
  assert result.exit_code == 0
354
+
355
  # Check inspector version was used
356
  inspector_cmd = mock_run.call_args[0][0][1]
357
  assert inspector_cmd == "@modelcontextprotocol/inspector@1.0.0"
 
362
 
363
  def test_run_command_success(self, temp_python_file, mock_logger):
364
  """Test successful run command execution."""
365
+ with (
366
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
367
+ patch("fastmcp.cli.cli._import_server") as mock_import,
368
+ ):
369
  mock_parse.return_value = (temp_python_file, None)
370
  mock_server = MagicMock()
371
  mock_server.name = "test_server"
372
  mock_import.return_value = mock_server
373
+
374
  result = runner.invoke(cli.app, ["run", str(temp_python_file)])
375
  assert result.exit_code == 0
376
  mock_server.run.assert_called_once_with()
377
+ mock_logger.info.assert_called_with(
378
+ f'Found server "test_server" in {temp_python_file}'
379
+ )
380
 
381
  def test_run_command_with_transport(self, temp_python_file):
382
  """Test run command with transport option."""
383
+ with (
384
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
385
+ patch("fastmcp.cli.cli._import_server") as mock_import,
386
+ ):
387
  mock_parse.return_value = (temp_python_file, None)
388
  mock_server = MagicMock()
389
  mock_server.name = "test_server"
390
  mock_import.return_value = mock_server
391
+
392
+ result = runner.invoke(
393
+ cli.app, ["run", str(temp_python_file), "--transport", "sse"]
394
+ )
395
  assert result.exit_code == 0
396
  mock_server.run.assert_called_once_with(transport="sse")
397
 
398
  def test_run_command_with_host(self, temp_python_file):
399
  """Test run command with host option."""
400
+ with (
401
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
402
+ patch("fastmcp.cli.cli._import_server") as mock_import,
403
+ ):
404
  mock_parse.return_value = (temp_python_file, None)
405
  mock_server = MagicMock()
406
  mock_server.name = "test_server"
407
  mock_import.return_value = mock_server
408
+
409
+ result = runner.invoke(
410
+ cli.app, ["run", str(temp_python_file), "--host", "0.0.0.0"]
411
+ )
412
  assert result.exit_code == 0
413
  mock_server.run.assert_called_once_with(host="0.0.0.0")
414
 
415
  def test_run_command_with_port(self, temp_python_file):
416
  """Test run command with port option."""
417
+ with (
418
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
419
+ patch("fastmcp.cli.cli._import_server") as mock_import,
420
+ ):
421
  mock_parse.return_value = (temp_python_file, None)
422
  mock_server = MagicMock()
423
  mock_server.name = "test_server"
424
  mock_import.return_value = mock_server
425
+
426
+ result = runner.invoke(
427
+ cli.app, ["run", str(temp_python_file), "--port", "8080"]
428
+ )
429
  assert result.exit_code == 0
430
  mock_server.run.assert_called_once_with(port=8080)
431
 
432
  def test_run_command_with_log_level(self, temp_python_file):
433
  """Test run command with log level option."""
434
+ with (
435
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
436
+ patch("fastmcp.cli.cli._import_server") as mock_import,
437
+ ):
438
  mock_parse.return_value = (temp_python_file, None)
439
  mock_server = MagicMock()
440
  mock_server.name = "test_server"
441
  mock_import.return_value = mock_server
442
+
443
+ result = runner.invoke(
444
+ cli.app, ["run", str(temp_python_file), "--log-level", "DEBUG"]
445
+ )
446
  assert result.exit_code == 0
447
  mock_server.run.assert_called_once_with(log_level="DEBUG")
448
 
449
  def test_run_command_with_multiple_options(self, temp_python_file):
450
  """Test run command with multiple options."""
451
+ with (
452
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse,
453
+ patch("fastmcp.cli.cli._import_server") as mock_import,
454
+ ):
455
  mock_parse.return_value = (temp_python_file, None)
456
  mock_server = MagicMock()
457
  mock_server.name = "test_server"
458
  mock_import.return_value = mock_server
459
+
460
+ result = runner.invoke(
461
+ cli.app,
462
+ [
463
+ "run",
464
+ str(temp_python_file),
465
+ "--transport",
466
+ "sse",
467
+ "--host",
468
+ "0.0.0.0",
469
+ "--port",
470
+ "8080",
471
+ "--log-level",
472
+ "DEBUG",
473
+ ],
474
+ )
475
  assert result.exit_code == 0
476
  mock_server.run.assert_called_once_with(
477
+ transport="sse", host="0.0.0.0", port=8080, log_level="DEBUG"
 
 
 
478
  )