Skip to content

Tests(cli): Add coverage for helper functions #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added tests/cli/__init__.py
Empty file.
99 changes: 99 additions & 0 deletions tests/cli/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import subprocess
import sys
from pathlib import Path

import pytest

from mcp.cli.cli import _build_uv_command, _get_npx_command, _parse_file_path


@pytest.mark.parametrize(
"spec, expected_obj",
[
("server.py", None),
("foo.py:srv_obj", "srv_obj"),
],
)
def test_parse_file_path_accepts_valid_specs(tmp_path, spec, expected_obj):
"""Should accept valid file specs."""
file = tmp_path / spec.split(":")[0]
file.write_text("x = 1")
path, obj = _parse_file_path(f"{file}:{expected_obj}" if ":" in spec else str(file))
assert path == file.resolve()
assert obj == expected_obj


def test_parse_file_path_missing(tmp_path):
"""Should system exit if a file is missing."""
with pytest.raises(SystemExit):
_parse_file_path(str(tmp_path / "missing.py"))


def test_parse_file_exit_on_dir(tmp_path):
"""Should system exit if a directory is passed"""
dir_path = tmp_path / "dir"
dir_path.mkdir()
with pytest.raises(SystemExit):
_parse_file_path(str(dir_path))


def test_build_uv_command_minimal():
"""Should emit core command when no extras specified."""
cmd = _build_uv_command("foo.py")
assert cmd == ["uv", "run", "--with", "mcp", "mcp", "run", "foo.py"]


def test_build_uv_command_adds_editable_and_packages():
"""Should include --with-editable and every --with pkg in correct order."""
cmd = _build_uv_command(
"foo.py",
with_editable=Path("/pkg"),
with_packages=["package1", "package2"],
)
assert cmd == [
"uv",
"run",
"--with",
"mcp",
"--with-editable",
"/pkg",
"--with",
"package1",
"--with",
"package2",
"mcp",
"run",
"foo.py",
]


def test_get_npx_unix_like(monkeypatch):
"""Should return "npx" on unix-like systems."""
monkeypatch.setattr(sys, "platform", "linux")
assert _get_npx_command() == "npx"


def test_get_npx_windows(monkeypatch):
"""Should return one of the npx candidates on Windows."""
candidates = ["npx.cmd", "npx.exe", "npx"]

def fake_run(cmd, **kw):
if cmd[0] in candidates:
return subprocess.CompletedProcess(cmd, 0)
else:
raise subprocess.CalledProcessError(1, cmd[0])

monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(subprocess, "run", fake_run)
assert _get_npx_command() in candidates


def test_get_npx_returns_none_when_npx_missing(monkeypatch):
"""Should give None if every candidate fails."""
monkeypatch.setattr(sys, "platform", "win32", raising=False)

def always_fail(*args, **kwargs):
raise subprocess.CalledProcessError(1, args[0])

monkeypatch.setattr(subprocess, "run", always_fail)
assert _get_npx_command() is None
Loading