Files
2026-07-13 12:49:17 +08:00

212 lines
8.4 KiB
Python

"""Tests for manage_prefabs tool."""
import asyncio
import inspect
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from services.tools.manage_prefabs import manage_prefabs
from services.registry import get_registered_tools
# ── Fixture ──────────────────────────────────────────────────────────
@pytest.fixture
def mock_unity(monkeypatch):
captured: dict[str, object] = {}
async def fake_send(send_fn, unity_instance, tool_name, params):
captured["unity_instance"] = unity_instance
captured["tool_name"] = tool_name
captured["params"] = params
return {"success": True, "message": "ok"}
monkeypatch.setattr(
"services.tools.manage_prefabs.get_unity_instance_from_context",
AsyncMock(return_value="unity-instance-1"),
)
monkeypatch.setattr(
"services.tools.manage_prefabs.send_with_unity_instance",
fake_send,
)
monkeypatch.setattr(
"services.tools.manage_prefabs.preflight",
AsyncMock(return_value=None),
)
return captured
# ── component_properties ─────────────────────────────────────────────
class TestManagePrefabsComponentProperties:
"""Tests for the component_properties parameter on manage_prefabs."""
def test_component_properties_parameter_exists(self):
"""The manage_prefabs tool should have a component_properties parameter."""
sig = inspect.signature(manage_prefabs)
assert "component_properties" in sig.parameters
def test_component_properties_parameter_is_optional(self):
"""component_properties should default to None."""
sig = inspect.signature(manage_prefabs)
param = sig.parameters["component_properties"]
assert param.default is None
def test_tool_description_mentions_component_properties(self):
"""The tool description should mention component_properties."""
prefab_tool = next(
(t for t in get_registered_tools() if t["name"] == "manage_prefabs"), None
)
assert prefab_tool is not None
desc = prefab_tool.get("description") or prefab_tool.get("kwargs", {}).get("description", "")
assert "component_properties" in desc
def test_required_params_include_modify_contents(self):
"""modify_contents should be a valid action requiring prefab_path."""
from services.tools.manage_prefabs import REQUIRED_PARAMS
assert "modify_contents" in REQUIRED_PARAMS
assert "prefab_path" in REQUIRED_PARAMS["modify_contents"]
# ── delete_child ─────────────────────────────────────────────────────
class TestManagePrefabsDeleteChild:
"""Tests for the delete_child parameter on manage_prefabs."""
def test_delete_child_parameter_exists(self):
"""The manage_prefabs tool should have a delete_child parameter."""
sig = inspect.signature(manage_prefabs)
assert "delete_child" in sig.parameters
def test_delete_child_parameter_is_optional(self):
"""delete_child should default to None."""
sig = inspect.signature(manage_prefabs)
param = sig.parameters["delete_child"]
assert param.default is None
def test_tool_description_mentions_delete_child(self):
"""The tool description should mention delete_child."""
prefab_tool = next(
(t for t in get_registered_tools() if t["name"] == "manage_prefabs"), None
)
assert prefab_tool is not None
desc = prefab_tool.get("description") or prefab_tool.get("kwargs", {}).get("description", "")
assert "delete_child" in desc
def test_delete_child_string_forwards_to_unity(self, mock_unity):
"""A single string delete_child should be forwarded as-is."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="modify_contents",
prefab_path="Assets/Prefabs/Test.prefab",
delete_child="Child1",
)
)
assert result["success"] is True
assert mock_unity["tool_name"] == "manage_prefabs"
assert mock_unity["params"]["deleteChild"] == "Child1"
def test_delete_child_list_forwards_to_unity(self, mock_unity):
"""A list of delete_child paths should be forwarded as-is."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="modify_contents",
prefab_path="Assets/Prefabs/Test.prefab",
delete_child=["Child1", "Child2/Grandchild"],
)
)
assert result["success"] is True
assert mock_unity["params"]["deleteChild"] == ["Child1", "Child2/Grandchild"]
def test_delete_child_none_omitted_from_params(self, mock_unity):
"""When delete_child is None, deleteChild should not appear in params."""
asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="modify_contents",
prefab_path="Assets/Prefabs/Test.prefab",
)
)
assert "deleteChild" not in mock_unity["params"]
# ── Prefab Stage Actions ────────────────────────────────────────────
class TestManagePrefabsStageActions:
"""Tests for open/save/close prefab stage actions on manage_prefabs."""
def test_description_mentions_open_prefab_stage(self):
"""The tool description should mention open_prefab_stage."""
prefab_tool = next(
(t for t in get_registered_tools() if t["name"] == "manage_prefabs"), None
)
assert prefab_tool is not None
desc = prefab_tool.get("description") or prefab_tool.get("kwargs", {}).get("description", "")
assert "open_prefab_stage" in desc
def test_description_mentions_save_prefab_stage(self):
"""The tool description should mention save_prefab_stage."""
prefab_tool = next(
(t for t in get_registered_tools() if t["name"] == "manage_prefabs"), None
)
assert prefab_tool is not None
desc = prefab_tool.get("description") or prefab_tool.get("kwargs", {}).get("description", "")
assert "save_prefab_stage" in desc
def test_open_prefab_stage_forwards_prefab_path(self, mock_unity):
"""open_prefab_stage should forward prefab_path as prefabPath."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="open_prefab_stage",
prefab_path="Assets/Prefabs/Test.prefab",
)
)
assert result["success"] is True
assert mock_unity["params"]["action"] == "open_prefab_stage"
assert mock_unity["params"]["prefabPath"] == "Assets/Prefabs/Test.prefab"
assert mock_unity["tool_name"] == "manage_prefabs"
def test_open_prefab_stage_requires_prefab_path(self, mock_unity):
"""open_prefab_stage should fail without prefab_path."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="open_prefab_stage",
)
)
assert result["success"] is False
assert "prefab_path" in result["message"]
def test_save_prefab_stage_forwards_to_unity(self, mock_unity):
"""save_prefab_stage should forward to Unity."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="save_prefab_stage",
)
)
assert result["success"] is True
assert mock_unity["params"]["action"] == "save_prefab_stage"
assert mock_unity["tool_name"] == "manage_prefabs"
def test_close_prefab_stage_forwards_to_unity(self, mock_unity):
"""close_prefab_stage should forward to Unity."""
result = asyncio.run(
manage_prefabs(
SimpleNamespace(),
action="close_prefab_stage",
)
)
assert result["success"] is True
assert mock_unity["params"]["action"] == "close_prefab_stage"
assert mock_unity["tool_name"] == "manage_prefabs"