Skip to content

Commit 06744d0

Browse files
authored
Merge pull request #211 from UiPath/test/coverage-boost
test: raise coverage to ~54% (from 32%)
2 parents fb057ba + 60971b3 commit 06744d0

7 files changed

Lines changed: 583 additions & 0 deletions

File tree

tests/test_cli_new.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Tests for the `uipath new` MCP middleware."""
2+
3+
from pathlib import Path
4+
from unittest.mock import patch
5+
6+
import pytest
7+
from uipath._cli.middlewares import MiddlewareResult
8+
9+
from uipath_mcp._cli import cli_new
10+
11+
12+
def test_clean_directory_removes_only_py_files(tmp_path: Path):
13+
(tmp_path / "a.py").write_text("x")
14+
(tmp_path / "b.txt").write_text("x")
15+
(tmp_path / "sub").mkdir()
16+
cli_new.clean_directory(str(tmp_path))
17+
assert not (tmp_path / "a.py").exists()
18+
assert (tmp_path / "b.txt").exists()
19+
assert (tmp_path / "sub").exists()
20+
21+
22+
def test_write_template_file_plain_copy(
23+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
24+
):
25+
src = tmp_path / "src"
26+
src.mkdir()
27+
(src / "tpl").write_text("hello")
28+
target = tmp_path / "out"
29+
target.mkdir()
30+
monkeypatch.setattr("uipath_mcp._cli.cli_new.os.path.dirname", lambda _: str(src))
31+
cli_new.write_template_file(str(target), "tpl", "result.txt", None)
32+
assert (target / "result.txt").read_text() == "hello"
33+
34+
35+
def test_write_template_file_with_replacements(
36+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
37+
):
38+
src = tmp_path / "src"
39+
src.mkdir()
40+
(src / "tpl").write_text("name=$name; v=$v")
41+
target = tmp_path / "out"
42+
target.mkdir()
43+
monkeypatch.setattr("uipath_mcp._cli.cli_new.os.path.dirname", lambda _: str(src))
44+
cli_new.write_template_file(
45+
str(target), "tpl", "result.txt", [("$name", "svc"), ("$v", "1")]
46+
)
47+
assert (target / "result.txt").read_text() == "name=svc; v=1"
48+
49+
50+
def test_generate_files_calls_write_three_times(tmp_path: Path):
51+
with patch.object(cli_new, "write_template_file") as wtf:
52+
cli_new.generate_files(str(tmp_path), "svc")
53+
assert wtf.call_count == 3
54+
55+
56+
def test_mcp_new_middleware_happy_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
57+
monkeypatch.chdir(tmp_path)
58+
with (
59+
patch.object(cli_new, "clean_directory") as cd,
60+
patch.object(cli_new, "generate_files") as gf,
61+
):
62+
result = cli_new.mcp_new_middleware("svc")
63+
assert isinstance(result, MiddlewareResult)
64+
assert result.should_continue is False
65+
cd.assert_called_once()
66+
gf.assert_called_once()
67+
68+
69+
def test_mcp_new_middleware_error_returns_stacktrace_flag(
70+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
71+
):
72+
monkeypatch.chdir(tmp_path)
73+
with (
74+
patch.object(cli_new, "clean_directory", side_effect=OSError("boom")),
75+
patch.object(cli_new.console, "error"),
76+
):
77+
result = cli_new.mcp_new_middleware("svc")
78+
assert result.should_continue is False
79+
assert result.should_include_stacktrace is True

tests/test_config.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""Tests for McpConfig and McpServer."""
2+
3+
import json
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
from uipath_mcp._cli._utils._config import McpConfig, McpServer
9+
10+
11+
def test_server_defaults():
12+
s = McpServer("svc", {})
13+
assert s.name == "svc"
14+
assert s.type is None
15+
assert s.transport == "stdio"
16+
assert s.url is None
17+
assert s.command == "None"
18+
assert s.args == []
19+
assert s.env == {}
20+
assert s.file_path is None
21+
assert s.is_streamable_http is False
22+
23+
24+
def test_server_streamable_http_and_args():
25+
s = McpServer(
26+
"svc",
27+
{
28+
"type": "remote",
29+
"transport": "streamable-http",
30+
"url": "https://x",
31+
"command": "node",
32+
"args": ["index.js"],
33+
"env": {},
34+
},
35+
)
36+
assert s.is_streamable_http is True
37+
assert s.file_path == "index.js"
38+
d = s.to_dict()
39+
assert d["transport"] == "streamable-http"
40+
assert d["url"] == "https://x"
41+
assert d["command"] == "node"
42+
assert "McpServer" in repr(s)
43+
44+
45+
def test_server_env_overlay_from_os(monkeypatch: pytest.MonkeyPatch):
46+
monkeypatch.setenv("FOO", "from-env")
47+
s = McpServer("svc", {"env": {"FOO": "default", "BAR": "keep"}})
48+
assert s.env["FOO"] == "from-env"
49+
assert s.env["BAR"] == "keep"
50+
51+
52+
def test_validate_server_name_ok():
53+
McpConfig.validate_server_name("good-name-1")
54+
55+
56+
@pytest.mark.parametrize("bad", ["with space", "under_score", "dots.bad", ""])
57+
def test_validate_server_name_bad(bad: str):
58+
with pytest.raises(ValueError):
59+
McpConfig.validate_server_name(bad)
60+
61+
62+
def test_config_not_exists(tmp_path: Path):
63+
cfg = McpConfig(str(tmp_path / "missing.json"))
64+
assert cfg.exists is False
65+
assert cfg.get_servers() == []
66+
assert cfg.get_server_names() == []
67+
assert cfg.get_server("anything") is None
68+
69+
70+
def test_config_load_and_lookup(tmp_path: Path):
71+
path = tmp_path / "mcp.json"
72+
path.write_text(
73+
json.dumps(
74+
{
75+
"servers": {
76+
"alpha": {"command": "a", "args": ["a.py"]},
77+
"beta": {"command": "b"},
78+
}
79+
}
80+
)
81+
)
82+
cfg = McpConfig(str(path))
83+
assert cfg.exists is True
84+
assert set(cfg.get_server_names()) == {"alpha", "beta"}
85+
assert len(cfg.get_servers()) == 2
86+
assert cfg.get_server("alpha").name == "alpha" # type: ignore[union-attr]
87+
assert cfg.get_server("missing") is None
88+
raw = cfg.load_config()
89+
assert "servers" in raw
90+
91+
92+
def test_config_single_server_returned_regardless_of_name(tmp_path: Path):
93+
path = tmp_path / "mcp.json"
94+
path.write_text(json.dumps({"servers": {"only": {"command": "x"}}}))
95+
cfg = McpConfig(str(path))
96+
assert cfg.get_server("does-not-matter").name == "only" # type: ignore[union-attr]
97+
98+
99+
def test_config_load_invalid_json(tmp_path: Path):
100+
path = tmp_path / "mcp.json"
101+
path.write_text("{not json")
102+
with pytest.raises(json.JSONDecodeError):
103+
McpConfig(str(path))
104+
105+
106+
def test_config_load_invalid_name(tmp_path: Path):
107+
path = tmp_path / "mcp.json"
108+
path.write_text(json.dumps({"servers": {"bad name": {}}}))
109+
with pytest.raises(ValueError):
110+
McpConfig(str(path))
111+
112+
113+
def test_load_config_when_missing_raises(tmp_path: Path):
114+
cfg = McpConfig(str(tmp_path / "nope.json"))
115+
with pytest.raises(FileNotFoundError):
116+
cfg.load_config()

tests/test_context.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Tests for UiPathServerType enum."""
2+
3+
import pytest
4+
5+
from uipath_mcp._cli._runtime._context import UiPathServerType
6+
7+
8+
def test_enum_values():
9+
assert UiPathServerType.UiPath.value == 0
10+
assert UiPathServerType.Command.value == 1
11+
assert UiPathServerType.Coded.value == 2
12+
assert UiPathServerType.SelfHosted.value == 3
13+
14+
15+
@pytest.mark.parametrize(
16+
"name",
17+
["UiPath", "Command", "Coded", "SelfHosted"],
18+
)
19+
def test_from_string_valid(name: str):
20+
assert UiPathServerType.from_string(name) == UiPathServerType[name]
21+
22+
23+
def test_from_string_invalid():
24+
with pytest.raises(ValueError, match="Unknown server type"):
25+
UiPathServerType.from_string("Nope")
26+
27+
28+
@pytest.mark.parametrize(
29+
"server_type",
30+
list(UiPathServerType),
31+
)
32+
def test_get_description_known(server_type: UiPathServerType):
33+
desc = UiPathServerType.get_description(server_type)
34+
assert isinstance(desc, str)
35+
assert desc != "Unknown server type"
36+
37+
38+
def test_get_description_unknown():
39+
assert UiPathServerType.get_description("not-an-enum") == "Unknown server type" # type: ignore[arg-type]

tests/test_exception.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Tests for UiPathMcpRuntimeError."""
2+
3+
from uipath.runtime.errors import UiPathErrorCategory
4+
5+
from uipath_mcp._cli._runtime._exception import McpErrorCode, UiPathMcpRuntimeError
6+
7+
8+
def test_error_code_values():
9+
assert McpErrorCode.CONFIGURATION_ERROR.value == "CONFIGURATION_ERROR"
10+
assert McpErrorCode.SERVER_NOT_FOUND.value == "SERVER_NOT_FOUND"
11+
assert McpErrorCode.REGISTRATION_ERROR.value == "REGISTRATION_ERROR"
12+
assert McpErrorCode.INITIALIZATION_ERROR.value == "INITIALIZATION_ERROR"
13+
14+
15+
def test_runtime_error_constructs_with_defaults():
16+
err = UiPathMcpRuntimeError(McpErrorCode.CONFIGURATION_ERROR, "title", "detail")
17+
assert isinstance(err, Exception)
18+
19+
20+
def test_runtime_error_constructs_with_all_args():
21+
err = UiPathMcpRuntimeError(
22+
McpErrorCode.SERVER_NOT_FOUND,
23+
"title",
24+
"detail",
25+
UiPathErrorCategory.USER,
26+
404,
27+
)
28+
assert isinstance(err, Exception)

0 commit comments

Comments
 (0)