Skip to content

Commit ea2ec85

Browse files
committed
test+docs: pytest coverage + CLI usage for component reference wiring
- Add 4 pytest cases (Server/tests/integration/test_manage_components.py) covering get_referenceable, set_reference (asset_path + clear), and batch_wire. Mocks async_send_command_with_retry; verifies the action and parameter shape sent to Unity. - Document the three new actions in docs/guides/CLI_USAGE.md under Component Operations. - Extend manage_components docstring with examples for the new actions. Note: Undo and prefab-override correctness require a real Unity Editor and are not asserted in Python unit tests.
1 parent c866993 commit ea2ec85

3 files changed

Lines changed: 183 additions & 1 deletion

File tree

Server/src/services/tools/manage_components.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ async def manage_components(
116116
- Remove BoxCollider: action="remove", target=-12345, component_type="BoxCollider"
117117
- Set single property: action="set_property", target="Enemy", component_type="Rigidbody", property="mass", value=5.0
118118
- Set multiple properties: action="set_property", target="Enemy", component_type="Rigidbody", properties={"mass": 5.0, "useGravity": false}
119+
- List referenceable targets: action="get_referenceable", target="Player", component_type="AudioSource", property="clip"
120+
- Set reference (asset): action="set_reference", target="Player", component_type="AudioSource", property="clip", reference_asset_path="Assets/Audio/BGM.mp3"
121+
- Wire multiple references: action="batch_wire", target="GameManager", component_type="GameManager", references=[{"property_name": "player", "reference_path": "Player"}]
119122
"""
120123
unity_instance = await get_unity_instance_from_context(ctx)
121124

Server/tests/integration/test_manage_components.py

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""
22
Tests for the manage_components tool.
33
4-
This tool handles component lifecycle operations (add, remove, set_property).
4+
This tool handles component lifecycle operations (add, remove, set_property)
5+
and object reference wiring (get_referenceable, set_reference, batch_wire).
56
"""
67
import pytest
78

@@ -240,3 +241,162 @@ async def fake_send(cmd, params, **kwargs):
240241
assert captured["params"]["target"] == 12345
241242
assert captured["params"]["searchMethod"] == "by_id"
242243

244+
245+
@pytest.mark.asyncio
246+
async def test_manage_components_get_referenceable(monkeypatch):
247+
"""get_referenceable forwards property + discovery flags + limit to Unity."""
248+
captured = {}
249+
250+
async def fake_send(cmd, params, **kwargs):
251+
captured["cmd"] = cmd
252+
captured["params"] = params
253+
return {
254+
"success": True,
255+
"data": {
256+
"scene_objects": [{"name": "Player", "instanceID": 1001}],
257+
"assets": [{"name": "BGM", "assetPath": "Assets/Audio/BGM.mp3"}],
258+
},
259+
}
260+
261+
monkeypatch.setattr(
262+
manage_comp_mod,
263+
"async_send_command_with_retry",
264+
fake_send,
265+
)
266+
267+
resp = await manage_comp_mod.manage_components(
268+
ctx=DummyContext(),
269+
action="get_referenceable",
270+
target="Player",
271+
component_type="AudioSource",
272+
property="clip",
273+
include_scene=True,
274+
include_assets=True,
275+
limit=50,
276+
)
277+
278+
assert resp.get("success") is True
279+
assert captured["cmd"] == "manage_components"
280+
assert captured["params"]["action"] == "get_referenceable"
281+
assert captured["params"]["componentType"] == "AudioSource"
282+
assert captured["params"]["property"] == "clip"
283+
assert captured["params"]["include_scene"] is True
284+
assert captured["params"]["include_assets"] is True
285+
assert captured["params"]["limit"] == 50
286+
287+
288+
@pytest.mark.asyncio
289+
async def test_manage_components_set_reference_asset_path(monkeypatch):
290+
"""set_reference forwards reference_asset_path to Unity."""
291+
captured = {}
292+
293+
async def fake_send(cmd, params, **kwargs):
294+
captured["params"] = params
295+
return {
296+
"success": True,
297+
"data": {
298+
"previous_value": None,
299+
"new_value": {"name": "BGM", "assetPath": "Assets/Audio/BGM.mp3"},
300+
},
301+
}
302+
303+
monkeypatch.setattr(
304+
manage_comp_mod,
305+
"async_send_command_with_retry",
306+
fake_send,
307+
)
308+
309+
resp = await manage_comp_mod.manage_components(
310+
ctx=DummyContext(),
311+
action="set_reference",
312+
target="Player",
313+
component_type="AudioSource",
314+
property="clip",
315+
reference_asset_path="Assets/Audio/BGM.mp3",
316+
)
317+
318+
assert resp.get("success") is True
319+
assert captured["params"]["action"] == "set_reference"
320+
assert captured["params"]["property"] == "clip"
321+
assert captured["params"]["reference_asset_path"] == "Assets/Audio/BGM.mp3"
322+
assert "reference_path" not in captured["params"]
323+
assert "reference_instance_id" not in captured["params"]
324+
325+
326+
@pytest.mark.asyncio
327+
async def test_manage_components_set_reference_clear(monkeypatch):
328+
"""set_reference with clear=True forwards clear flag and omits selectors."""
329+
captured = {}
330+
331+
async def fake_send(cmd, params, **kwargs):
332+
captured["params"] = params
333+
return {
334+
"success": True,
335+
"data": {"previous_value": {"name": "BGM"}, "new_value": None},
336+
}
337+
338+
monkeypatch.setattr(
339+
manage_comp_mod,
340+
"async_send_command_with_retry",
341+
fake_send,
342+
)
343+
344+
resp = await manage_comp_mod.manage_components(
345+
ctx=DummyContext(),
346+
action="set_reference",
347+
target="Player",
348+
component_type="AudioSource",
349+
property="clip",
350+
clear=True,
351+
)
352+
353+
assert resp.get("success") is True
354+
assert captured["params"]["action"] == "set_reference"
355+
assert captured["params"]["clear"] is True
356+
assert "reference_path" not in captured["params"]
357+
assert "reference_asset_path" not in captured["params"]
358+
assert "reference_instance_id" not in captured["params"]
359+
360+
361+
@pytest.mark.asyncio
362+
async def test_manage_components_batch_wire(monkeypatch):
363+
"""batch_wire forwards references list and atomic flag."""
364+
captured = {}
365+
366+
async def fake_send(cmd, params, **kwargs):
367+
captured["params"] = params
368+
return {
369+
"success": True,
370+
"data": {
371+
"results": [
372+
{"property": "player", "success": True},
373+
{"property": "bgMusic", "success": True},
374+
]
375+
},
376+
}
377+
378+
monkeypatch.setattr(
379+
manage_comp_mod,
380+
"async_send_command_with_retry",
381+
fake_send,
382+
)
383+
384+
references = [
385+
{"property_name": "player", "reference_path": "Player"},
386+
{"property_name": "bgMusic", "reference_asset_path": "Assets/Audio/BGM.mp3"},
387+
]
388+
389+
resp = await manage_comp_mod.manage_components(
390+
ctx=DummyContext(),
391+
action="batch_wire",
392+
target="GameManager",
393+
component_type="GameManager",
394+
references=references,
395+
atomic=True,
396+
)
397+
398+
assert resp.get("success") is True
399+
assert captured["params"]["action"] == "batch_wire"
400+
assert captured["params"]["componentType"] == "GameManager"
401+
assert captured["params"]["references"] == references
402+
assert captured["params"]["atomic"] is True

docs/guides/CLI_USAGE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ unity-mcp component remove "Player" Rigidbody
108108

109109
# Set property
110110
unity-mcp component set "Player" Rigidbody mass 10
111+
112+
# List valid reference targets for an object reference field
113+
unity-mcp component get_referenceable "Player" AudioSource clip
114+
115+
# Assign an object reference (project asset by path)
116+
unity-mcp component set_reference "Player" AudioSource clip \
117+
--reference_asset_path "Assets/Audio/BGM.mp3"
118+
119+
# Assign an object reference (scene object by hierarchy path)
120+
unity-mcp component set_reference "MainCamera" CinemachineCamera Follow \
121+
--reference_path "Player"
122+
123+
# Clear an object reference
124+
unity-mcp component set_reference "Player" AudioSource clip --clear
125+
126+
# Wire multiple references on a component atomically
127+
unity-mcp component batch_wire "GameManager" GameManager --references \
128+
'[{"property_name":"player","reference_path":"Player"},
129+
{"property_name":"bgMusic","reference_asset_path":"Assets/Audio/BGM.mp3"}]'
111130
```
112131

113132
### Script Operations

0 commit comments

Comments
 (0)