Skip to content

Commit c0bc666

Browse files
authored
Tests(cli): Add coverage for helper functions (#635)
1 parent a99711d commit c0bc666

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

tests/cli/__init__.py

Whitespace-only changes.

tests/cli/test_utils.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
test_path = Path("/pkg")
49+
cmd = _build_uv_command(
50+
"foo.py",
51+
with_editable=test_path,
52+
with_packages=["package1", "package2"],
53+
)
54+
assert cmd == [
55+
"uv",
56+
"run",
57+
"--with",
58+
"mcp",
59+
"--with-editable",
60+
str(test_path), # Use str() to match what the function does
61+
"--with",
62+
"package1",
63+
"--with",
64+
"package2",
65+
"mcp",
66+
"run",
67+
"foo.py",
68+
]
69+
70+
71+
def test_get_npx_unix_like(monkeypatch):
72+
"""Should return "npx" on unix-like systems."""
73+
monkeypatch.setattr(sys, "platform", "linux")
74+
assert _get_npx_command() == "npx"
75+
76+
77+
def test_get_npx_windows(monkeypatch):
78+
"""Should return one of the npx candidates on Windows."""
79+
candidates = ["npx.cmd", "npx.exe", "npx"]
80+
81+
def fake_run(cmd, **kw):
82+
if cmd[0] in candidates:
83+
return subprocess.CompletedProcess(cmd, 0)
84+
else:
85+
raise subprocess.CalledProcessError(1, cmd[0])
86+
87+
monkeypatch.setattr(sys, "platform", "win32")
88+
monkeypatch.setattr(subprocess, "run", fake_run)
89+
assert _get_npx_command() in candidates
90+
91+
92+
def test_get_npx_returns_none_when_npx_missing(monkeypatch):
93+
"""Should give None if every candidate fails."""
94+
monkeypatch.setattr(sys, "platform", "win32", raising=False)
95+
96+
def always_fail(*args, **kwargs):
97+
raise subprocess.CalledProcessError(1, args[0])
98+
99+
monkeypatch.setattr(subprocess, "run", always_fail)
100+
assert _get_npx_command() is None

0 commit comments

Comments
 (0)