Files
wehub-resource-sync db1d565b64
Integration Tests / melodic (push) Has been cancelled
Integration Tests / noetic (push) Has been cancelled
Integration Tests / humble (push) Has been cancelled
Integration Tests / jazzy (push) Has been cancelled
Ruff Lint & Format / ruff (push) Has been cancelled
Sync main to develop / Check if sync is needed (push) Has been cancelled
Sync main to develop / Sync main to develop (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:36:23 +08:00

139 lines
5.9 KiB
Python

"""Unit tests for ros_mcp/utils/config_utils.py."""
from pathlib import Path
import pytest
import ros_mcp.utils.config_utils as config_module
from ros_mcp.utils.config_utils import (
get_verified_robot_spec_util,
get_verified_robots_list_util,
load_robot_config,
)
# ---------------------------------------------------------------------------
# load_robot_config — accepts specs_dir param, fully controllable
# ---------------------------------------------------------------------------
class TestLoadRobotConfig:
def test_valid_yaml(self, valid_robot_yaml):
specs_dir, robot_name = valid_robot_yaml
config = load_robot_config(robot_name, specs_dir)
assert config["name"] == "test_robot"
assert config["type"] == "simulated"
assert "test robot" in config["prompts"].lower()
def test_missing_file_raises(self, tmp_path):
with pytest.raises(FileNotFoundError, match="Robot config file not found"):
load_robot_config("nonexistent", str(tmp_path))
def test_empty_yaml_returns_empty_dict(self, empty_robot_yaml):
specs_dir, robot_name = empty_robot_yaml
config = load_robot_config(robot_name, specs_dir)
assert config == {}
# ---------------------------------------------------------------------------
# get_verified_robot_spec_util — uses Path(__file__) internally
# ---------------------------------------------------------------------------
def _patch_specs_dir(monkeypatch, tmp_path):
"""Redirect config_utils.__file__ so specs_dir resolves to tmp_path/robot_specifications.
Note: this approach is tightly coupled to config_utils's internal path
resolution (Path(__file__).parent.parent.parent / "robot_specifications").
If config_utils is refactored to accept specs_dir via injection or
configuration, these monkeypatches will need to be revisited.
"""
# config_utils resolves: Path(__file__).parent.parent.parent / "robot_specifications"
# So __file__ must be at: tmp_path / "a" / "b" / "config_utils.py"
fake_file = tmp_path / "a" / "b" / "config_utils.py"
fake_file.parent.mkdir(parents=True, exist_ok=True)
monkeypatch.setattr(config_module, "__file__", str(fake_file))
class TestGetVerifiedRobotSpecUtil:
def test_valid_robot_with_fixture(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
specs_dir = tmp_path / "robot_specifications"
specs_dir.mkdir()
(specs_dir / "my_robot.yaml").write_text("type: simulated\nprompts: A test robot.\n")
result = get_verified_robot_spec_util("my_robot")
assert "my_robot" in result
assert result["my_robot"]["type"] == "simulated"
def test_spaces_in_name_replaced(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
specs_dir = tmp_path / "robot_specifications"
specs_dir.mkdir()
(specs_dir / "my_robot.yaml").write_text("type: real\nprompts: A real robot.\n")
result = get_verified_robot_spec_util("my robot")
assert "my_robot" in result
def test_missing_robot_raises(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
(tmp_path / "robot_specifications").mkdir()
with pytest.raises(FileNotFoundError):
get_verified_robot_spec_util("nonexistent")
def test_empty_config_raises_value_error(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
specs_dir = tmp_path / "robot_specifications"
specs_dir.mkdir()
(specs_dir / "empty.yaml").write_text("")
with pytest.raises(ValueError, match="No configuration found"):
get_verified_robot_spec_util("empty")
def test_missing_required_field_raises(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
specs_dir = tmp_path / "robot_specifications"
specs_dir.mkdir()
(specs_dir / "incomplete.yaml").write_text("type: real\n")
with pytest.raises(ValueError, match="missing required field"):
get_verified_robot_spec_util("incomplete")
def test_with_real_robot_specs(self):
"""Integration-style: test against actual robot_specifications/ in the repo."""
specs_path = Path(__file__).parent.parent.parent / "robot_specifications"
real_robots = [f.stem for f in specs_path.glob("*.yaml") if f.stem != "YOUR_ROBOT_NAME"]
if not real_robots:
pytest.skip("No real robot spec files found")
result = get_verified_robot_spec_util(real_robots[0])
assert real_robots[0] in result
assert "type" in result[real_robots[0]]
assert "prompts" in result[real_robots[0]]
# ---------------------------------------------------------------------------
# get_verified_robots_list_util — uses Path(__file__) internally
# ---------------------------------------------------------------------------
class TestGetVerifiedRobotsListUtil:
def test_returns_sorted_list(self):
"""Test against the actual robot_specifications/ directory."""
result = get_verified_robots_list_util()
if "error" in result:
pytest.skip(f"Skipping: {result['error']}")
assert "robot_specifications" in result
assert "count" in result
assert result["count"] > 0
names = result["robot_specifications"]
assert names == sorted(names)
def test_missing_directory(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
# Don't create robot_specifications/ → should return error
result = get_verified_robots_list_util()
assert "error" in result
assert "not found" in result["error"]
def test_empty_directory(self, monkeypatch, tmp_path):
_patch_specs_dir(monkeypatch, tmp_path)
(tmp_path / "robot_specifications").mkdir()
result = get_verified_robots_list_util()
assert "error" in result
assert "No robot specification files found" in result["error"]