Skip to content

Commit c17a743

Browse files
committed
Tests(cli): add coverage for helper functions
1 parent a027d75 commit c17a743

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

tests/cli/__init__.py

Whitespace-only changes.

tests/cli/test_utils.py

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import subprocess
2+
import sys
3+
from pathlib import Path
4+
5+
import pytest
6+
7+
from mcp.cli.cli import _build_uv_command, _get_npx_command, _parse_file_path
8+
9+
10+
@pytest.mark.parametrize(
11+
"spec, expected_obj",
12+
[
13+
("server.py", None),
14+
("foo.py:srv_obj", "srv_obj"),
15+
],
16+
)
17+
def test_parse_file_path_accepts_valid_specs(tmp_path, spec, expected_obj):
18+
"""Should accept valid file specs."""
19+
file = tmp_path / spec.split(":")[0]
20+
file.write_text("x = 1")
21+
path, obj = _parse_file_path(f"{file}:{expected_obj}" if ":" in spec else str(file))
22+
assert path == file.resolve()
23+
assert obj == expected_obj
24+
25+
26+
def test_parse_file_path_missing(tmp_path):
27+
"""Should system exit if a file is missing."""
28+
with pytest.raises(SystemExit):
29+
_parse_file_path(str(tmp_path / "missing.py"))
30+
31+
32+
def test_parse_file_exit_on_dir(tmp_path):
33+
"""Should system exit if a directory is passed"""
34+
dir_path = tmp_path / "dir"
35+
dir_path.mkdir()
36+
with pytest.raises(SystemExit):
37+
_parse_file_path(str(dir_path))
38+
39+
40+
def test_build_uv_command_minimal():
41+
"""Should emit core command when no extras specified."""
42+
cmd = _build_uv_command("foo.py")
43+
assert cmd == ["uv", "run", "--with", "mcp", "mcp", "run", "foo.py"]
44+
45+
46+
def test_build_uv_command_adds_editable_and_packages():
47+
"""Should include --with-editable and every --with pkg in correct order."""
48+
cmd = _build_uv_command(
49+
"foo.py",
50+
with_editable=Path("/pkg"),
51+
with_packages=["package1", "package2"],
52+
)
53+
assert cmd == [
54+
"uv",
55+
"run",
56+
"--with",
57+
"mcp",
58+
"--with-editable",
59+
"/pkg",
60+
"--with",
61+
"package1",
62+
"--with",
63+
"package2",
64+
"mcp",
65+
"run",
66+
"foo.py",
67+
]
68+
69+
70+
def test_get_npx_unix_like(monkeypatch):
71+
"""Should return "npx" on unix-like systems."""
72+
monkeypatch.setattr(sys, "platform", "linux")
73+
assert _get_npx_command() == "npx"
74+
75+
76+
def test_get_npx_windows(monkeypatch):
77+
"""Should return one of the npx candidates on Windows."""
78+
candidates = ["npx.cmd", "npx.exe", "npx"]
79+
80+
def fake_run(cmd, **kw):
81+
if cmd[0] in candidates:
82+
return subprocess.CompletedProcess(cmd, 0)
83+
else:
84+
raise subprocess.CalledProcessError(1, cmd[0])
85+
86+
monkeypatch.setattr(sys, "platform", "win32")
87+
monkeypatch.setattr(subprocess, "run", fake_run)
88+
assert _get_npx_command() in candidates
89+
90+
91+
def test_get_npx_returns_none_when_npx_missing(monkeypatch):
92+
"""Should give None if every candidate fails."""
93+
monkeypatch.setattr(sys, "platform", "win32", raising=False)
94+
95+
def always_fail(*args, **kwargs):
96+
raise subprocess.CalledProcessError(1, args[0])
97+
98+
monkeypatch.setattr(subprocess, "run", always_fail)
99+
assert _get_npx_command() is None

0 commit comments

Comments
 (0)