"""Integration tests for manage_ui tool."""
import asyncio
import base64
import pytest
from .test_helpers import DummyContext
import services.tools.manage_ui as manage_ui_mod
def run_async(coro):
"""Simple wrapper to run a coroutine synchronously."""
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
return loop.run_until_complete(coro)
finally:
loop.close()
asyncio.set_event_loop(None)
SAMPLE_UXML = """<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:Label text="Hello World" />
</ui:UXML>"""
SAMPLE_USS = """.label {
font-size: 24px;
color: white;
}"""
class TestManageUIPathValidation:
"""Tests for path validation logic."""
def test_create_rejects_path_outside_assets(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="NotAssets/UI/Test.uxml",
contents=SAMPLE_UXML,
))
assert resp["success"] is False
assert "Assets/" in resp["message"]
def test_create_rejects_traversal(self, monkeypatch):
async def fake_send(*_args, **_kwargs):
return {"success": True}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/../etc/passwd.uxml",
contents=SAMPLE_UXML,
))
assert resp["success"] is False
# Path normalization resolves ".." so it either fails traversal or Assets/ check
assert "traversal" in resp["message"] or "Assets/" in resp["message"]
def test_create_rejects_invalid_extension(self, monkeypatch):
async def fake_send(*_args, **_kwargs):
return {"success": True}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/UI/Test.cs",
contents="some content",
))
assert resp["success"] is False
assert ".uxml or .uss" in resp["message"]
def test_create_accepts_uxml_extension(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Created"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/UI/Menu.uxml",
contents=SAMPLE_UXML,
))
assert resp["success"] is True
assert captured["params"]["path"] == "Assets/UI/Menu.uxml"
def test_create_accepts_uss_extension(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Created"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/UI/Styles.uss",
contents=SAMPLE_USS,
))
assert resp["success"] is True
assert captured["params"]["path"] == "Assets/UI/Styles.uss"
class TestManageUIContentsEncoding:
"""Tests for base64 content encoding."""
def test_create_encodes_contents_as_base64(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Created"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/UI/Test.uxml",
contents=SAMPLE_UXML,
))
params = captured["params"]
assert params["contentsEncoded"] is True
decoded = base64.b64decode(params["encodedContents"]).decode("utf-8")
assert decoded == SAMPLE_UXML
# Raw contents should NOT be in params (only encoded)
assert "contents" not in params
def test_update_encodes_contents_as_base64(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Updated"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="update",
path="Assets/UI/Test.uss",
contents=SAMPLE_USS,
))
params = captured["params"]
assert params["contentsEncoded"] is True
decoded = base64.b64decode(params["encodedContents"]).decode("utf-8")
assert decoded == SAMPLE_USS
class TestManageUIActionRouting:
"""Tests for action-based parameter routing."""
def test_read_uses_non_mutation_path(self, monkeypatch):
captured = {}
async def fake_send(_func, _instance, cmd, params, **kwargs):
captured["cmd"] = cmd
captured["params"] = params
return {"success": True, "data": {"contents": "test"}}
monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="read",
path="Assets/UI/Test.uxml",
))
assert captured["cmd"] == "manage_ui"
assert captured["params"]["action"] == "read"
def test_create_uses_mutation_path(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, cmd, _params, **kwargs):
captured["cmd"] = cmd
return {"success": True, "message": "Created"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create",
path="Assets/UI/Test.uxml",
contents=SAMPLE_UXML,
))
assert captured["cmd"] == "manage_ui"
def test_attach_ui_document_params(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Attached"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="attach_ui_document",
target="MyCanvas",
source_asset="Assets/UI/Menu.uxml",
panel_settings="Assets/UI/PanelSettings.asset",
sort_order=5,
))
params = captured["params"]
assert params["action"] == "attach_ui_document"
assert params["target"] == "MyCanvas"
assert params["sourceAsset"] == "Assets/UI/Menu.uxml"
assert params["panelSettings"] == "Assets/UI/PanelSettings.asset"
assert params["sortOrder"] == 5
def test_create_panel_settings_params(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Created"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="create_panel_settings",
path="Assets/UI/MyPanel.asset",
scale_mode="ScaleWithScreenSize",
reference_resolution={"width": 1920, "height": 1080},
))
params = captured["params"]
assert params["action"] == "create_panel_settings"
assert params["scaleMode"] == "ScaleWithScreenSize"
assert params["referenceResolution"] == {"width": 1920, "height": 1080}
def test_get_visual_tree_params(self, monkeypatch):
captured = {}
async def fake_send(_func, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "data": {"tree": {}}}
monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="get_visual_tree",
target="UIRoot",
max_depth=5,
))
params = captured["params"]
assert params["action"] == "get_visual_tree"
assert params["target"] == "UIRoot"
assert params["maxDepth"] == 5
def test_ping_uses_non_mutation_path(self, monkeypatch):
captured = {}
async def fake_send(_func, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "pong"}
monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="ping",
))
assert resp["success"] is True
assert captured["params"]["action"] == "ping"
class TestManageUINoneRemoval:
"""Tests that None values are properly excluded from params."""
def test_none_params_excluded(self, monkeypatch):
captured = {}
async def fake_send(_func, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "data": {}}
monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="read",
path="Assets/UI/Test.uxml",
# All other params are None
))
params = captured["params"]
assert "target" not in params
assert "sourceAsset" not in params
assert "panelSettings" not in params
assert "sortOrder" not in params
assert "scaleMode" not in params
assert "maxDepth" not in params
class TestManageUIReadResponse:
"""Tests for read response handling."""
def test_read_decodes_base64_response(self, monkeypatch):
encoded = base64.b64encode(SAMPLE_UXML.encode("utf-8")).decode("utf-8")
async def fake_send(*_args, **_kwargs):
return {
"success": True,
"data": {
"path": "Assets/UI/Test.uxml",
"contents": SAMPLE_UXML,
"encodedContents": encoded,
"contentsEncoded": True,
}
}
monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(),
action="read",
path="Assets/UI/Test.uxml",
))
assert resp["success"] is True
data = resp["data"]
assert data["contents"] == SAMPLE_UXML
assert "encodedContents" not in data
assert "contentsEncoded" not in data
class TestManageUIRenderUI:
"""Tests for render_ui action."""
def test_render_ui_routes_params(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Rendered",
"data": {"path": "Assets/Screenshots/test.png"}}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(), action="render_ui",
target="UIRoot", width=1280, height=720,
include_image=True, max_resolution=480,
screenshot_file_name="my-preview",
))
assert resp["success"] is True
p = captured["params"]
assert p["action"] == "render_ui"
assert p["target"] == "UIRoot"
assert p["width"] == 1280
assert p["height"] == 720
assert p["include_image"] is True
assert p["max_resolution"] == 480
assert p["file_name"] == "my-preview"
def test_render_ui_none_excluded(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "ok"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(), action="render_ui", target="X"))
p = captured["params"]
for k in ("width", "height", "include_image", "max_resolution", "file_name"):
assert k not in p
class TestManageUILinkStylesheet:
"""Tests for link_stylesheet action."""
def test_link_stylesheet_routes_params(self, monkeypatch):
captured = {}
async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
captured["params"] = params
return {"success": True, "message": "Linked"}
monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
resp = run_async(manage_ui_mod.manage_ui(
ctx=DummyContext(), action="link_stylesheet",
path="Assets/UI/Menu.uxml",
stylesheet="Assets/UI/Styles.uss",
))
assert resp["success"] is True
p = captured["params"]
assert p["action"] == "link_stylesheet"
assert p["path"] == "Assets/UI/Menu.uxml"
assert p["stylesheet"] == "Assets/UI/Styles.uss"
for k in ("width", "height", "include_image"):
assert k not in p