673 lines
22 KiB
Python
673 lines
22 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from services.tools.manage_probuilder import (
|
|
manage_probuilder,
|
|
ALL_ACTIONS,
|
|
SHAPE_ACTIONS,
|
|
MESH_ACTIONS,
|
|
VERTEX_ACTIONS,
|
|
SELECTION_ACTIONS,
|
|
UV_MATERIAL_ACTIONS,
|
|
QUERY_ACTIONS,
|
|
SMOOTHING_ACTIONS,
|
|
UTILITY_ACTIONS,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@pytest.fixture
|
|
def mock_unity(monkeypatch):
|
|
"""Patch Unity transport layer and return captured call dict."""
|
|
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_probuilder.get_unity_instance_from_context",
|
|
AsyncMock(return_value="unity-instance-1"),
|
|
)
|
|
monkeypatch.setattr(
|
|
"services.tools.manage_probuilder.send_with_unity_instance",
|
|
fake_send,
|
|
)
|
|
return captured
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Action list completeness
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_all_actions_is_union_of_sub_lists():
|
|
expected = set(
|
|
["ping"] + SHAPE_ACTIONS + MESH_ACTIONS + VERTEX_ACTIONS + SELECTION_ACTIONS
|
|
+ UV_MATERIAL_ACTIONS + QUERY_ACTIONS + SMOOTHING_ACTIONS + UTILITY_ACTIONS
|
|
)
|
|
assert set(ALL_ACTIONS) == expected
|
|
|
|
|
|
def test_no_duplicate_actions():
|
|
assert len(ALL_ACTIONS) == len(set(ALL_ACTIONS))
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Invalid / missing action
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_unknown_action_returns_error(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(SimpleNamespace(), action="nonexistent_action")
|
|
)
|
|
assert result["success"] is False
|
|
assert "Unknown action" in result["message"]
|
|
assert "tool_name" not in mock_unity # Should NOT call Unity
|
|
|
|
|
|
def test_empty_action_returns_error(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(SimpleNamespace(), action="")
|
|
)
|
|
assert result["success"] is False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shape creation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_create_shape_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="create_shape",
|
|
properties={"shapeType": "Cube", "size": [2, 2, 2]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["tool_name"] == "manage_probuilder"
|
|
assert mock_unity["params"]["action"] == "create_shape"
|
|
assert mock_unity["params"]["properties"]["shapeType"] == "Cube"
|
|
|
|
|
|
def test_create_shape_with_target(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="create_shape",
|
|
target="MyParent",
|
|
properties={"shapeType": "Torus"},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["target"] == "MyParent"
|
|
|
|
|
|
def test_create_poly_shape_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="create_poly_shape",
|
|
properties={
|
|
"points": [[0, 0, 0], [5, 0, 0], [5, 0, 5], [0, 0, 5]],
|
|
"extrudeHeight": 3.0,
|
|
},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "create_poly_shape"
|
|
assert mock_unity["params"]["properties"]["extrudeHeight"] == 3.0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Mesh editing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_extrude_faces_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="extrude_faces",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0, 1], "distance": 1.5},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "extrude_faces"
|
|
assert mock_unity["params"]["target"] == "MyCube"
|
|
assert mock_unity["params"]["properties"]["faceIndices"] == [0, 1]
|
|
assert mock_unity["params"]["properties"]["distance"] == 1.5
|
|
|
|
|
|
def test_bevel_edges_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="bevel_edges",
|
|
target="MyCube",
|
|
properties={"edgeIndices": [0, 2], "amount": 0.2},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "bevel_edges"
|
|
assert mock_unity["params"]["properties"]["amount"] == 0.2
|
|
|
|
|
|
def test_delete_faces_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="delete_faces",
|
|
target="MyCube",
|
|
properties={"faceIndices": [3]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "delete_faces"
|
|
|
|
|
|
def test_subdivide_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="subdivide",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "subdivide"
|
|
|
|
|
|
def test_combine_meshes_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="combine_meshes",
|
|
properties={"targets": ["Cube1", "Cube2"]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "combine_meshes"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Vertex operations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_move_vertices_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="move_vertices",
|
|
target="MyCube",
|
|
properties={"vertexIndices": [0, 1, 2], "offset": [0, 1, 0]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "move_vertices"
|
|
assert mock_unity["params"]["properties"]["offset"] == [0, 1, 0]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# UV & materials
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_set_face_material_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="set_face_material",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0], "materialPath": "Assets/Materials/Red.mat"},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "set_face_material"
|
|
assert mock_unity["params"]["properties"]["materialPath"] == "Assets/Materials/Red.mat"
|
|
|
|
|
|
def test_set_face_uvs_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="set_face_uvs",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0, 1], "scale": [2, 2], "rotation": 45},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "set_face_uvs"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Query
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_get_mesh_info_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="get_mesh_info",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "get_mesh_info"
|
|
assert mock_unity["params"]["target"] == "MyCube"
|
|
|
|
|
|
def test_convert_to_probuilder_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="convert_to_probuilder",
|
|
target="StandardMesh",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "convert_to_probuilder"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Search method passthrough
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_search_method_passed_through(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="get_mesh_info",
|
|
target="-12345",
|
|
search_method="by_id",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["searchMethod"] == "by_id"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Ping
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_ping_sends_to_unity(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(SimpleNamespace(), action="ping")
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "ping"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# All actions are lowercase-normalized
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_action_case_insensitive(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="Create_Shape",
|
|
properties={"shapeType": "Cube"},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "create_shape"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Non-dict result from Unity
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_non_dict_result_wrapped(monkeypatch):
|
|
async def fake_send(send_fn, unity_instance, tool_name, params):
|
|
return "unexpected string result"
|
|
|
|
monkeypatch.setattr(
|
|
"services.tools.manage_probuilder.get_unity_instance_from_context",
|
|
AsyncMock(return_value="unity-instance-1"),
|
|
)
|
|
monkeypatch.setattr(
|
|
"services.tools.manage_probuilder.send_with_unity_instance",
|
|
fake_send,
|
|
)
|
|
|
|
result = asyncio.run(
|
|
manage_probuilder(SimpleNamespace(), action="ping")
|
|
)
|
|
assert result["success"] is False
|
|
assert "unexpected string result" in result["message"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# New action categories
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_smoothing_actions_in_all():
|
|
for action in SMOOTHING_ACTIONS:
|
|
assert action in ALL_ACTIONS, f"{action} should be in ALL_ACTIONS"
|
|
|
|
|
|
def test_utility_actions_in_all():
|
|
for action in UTILITY_ACTIONS:
|
|
assert action in ALL_ACTIONS, f"{action} should be in ALL_ACTIONS"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Smoothing actions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_auto_smooth_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="auto_smooth",
|
|
target="MyCube",
|
|
properties={"angleThreshold": 45},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "auto_smooth"
|
|
assert mock_unity["params"]["target"] == "MyCube"
|
|
assert mock_unity["params"]["properties"]["angleThreshold"] == 45
|
|
|
|
|
|
def test_set_smoothing_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="set_smoothing",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0, 1, 2], "smoothingGroup": 1},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "set_smoothing"
|
|
assert mock_unity["params"]["properties"]["faceIndices"] == [0, 1, 2]
|
|
assert mock_unity["params"]["properties"]["smoothingGroup"] == 1
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Mesh utility actions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_center_pivot_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="center_pivot",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "center_pivot"
|
|
assert mock_unity["params"]["target"] == "MyCube"
|
|
|
|
|
|
def test_freeze_transform_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="freeze_transform",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "freeze_transform"
|
|
|
|
|
|
def test_validate_mesh_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="validate_mesh",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "validate_mesh"
|
|
|
|
|
|
def test_repair_mesh_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="repair_mesh",
|
|
target="MyCube",
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "repair_mesh"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_mesh_info include parameter passthrough
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_get_mesh_info_include_param_passthrough(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="get_mesh_info",
|
|
target="MyCube",
|
|
properties={"include": "faces"},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "get_mesh_info"
|
|
assert mock_unity["params"]["properties"]["include"] == "faces"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# New actions: mesh editing additions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_duplicate_and_flip_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="duplicate_and_flip",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0, 1]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "duplicate_and_flip"
|
|
assert mock_unity["params"]["properties"]["faceIndices"] == [0, 1]
|
|
|
|
|
|
def test_create_polygon_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="create_polygon",
|
|
target="MyCube",
|
|
properties={"vertexIndices": [0, 1, 2, 3], "unordered": True},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "create_polygon"
|
|
assert mock_unity["params"]["properties"]["vertexIndices"] == [0, 1, 2, 3]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# New actions: vertex operations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_weld_vertices_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="weld_vertices",
|
|
target="MyCube",
|
|
properties={"vertexIndices": [0, 1, 2], "radius": 0.05},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "weld_vertices"
|
|
assert mock_unity["params"]["properties"]["radius"] == 0.05
|
|
|
|
|
|
def test_insert_vertex_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="insert_vertex",
|
|
target="MyCube",
|
|
properties={"edge": {"a": 0, "b": 1}, "point": [0.5, 0, 0]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "insert_vertex"
|
|
assert mock_unity["params"]["properties"]["edge"] == {"a": 0, "b": 1}
|
|
|
|
|
|
def test_append_vertices_to_edge_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="append_vertices_to_edge",
|
|
target="MyCube",
|
|
properties={"edgeIndices": [0, 1], "count": 3},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "append_vertices_to_edge"
|
|
assert mock_unity["params"]["properties"]["count"] == 3
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# New actions: selection
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_select_faces_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="select_faces",
|
|
target="MyCube",
|
|
properties={"direction": "up", "tolerance": 0.9},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "select_faces"
|
|
assert mock_unity["params"]["properties"]["direction"] == "up"
|
|
assert mock_unity["params"]["properties"]["tolerance"] == 0.9
|
|
|
|
|
|
def test_selection_actions_in_all():
|
|
for action in SELECTION_ACTIONS:
|
|
assert action in ALL_ACTIONS, f"{action} should be in ALL_ACTIONS"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# New actions: utility
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_set_pivot_sends_correct_params(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="set_pivot",
|
|
target="MyCube",
|
|
properties={"position": [1.5, 0, 2.3]},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "set_pivot"
|
|
assert mock_unity["params"]["properties"]["position"] == [1.5, 0, 2.3]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Edge specification by vertex pairs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_bevel_edges_with_vertex_pairs(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="bevel_edges",
|
|
target="MyCube",
|
|
properties={"edges": [{"a": 0, "b": 1}, {"a": 2, "b": 3}], "amount": 0.15},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["action"] == "bevel_edges"
|
|
assert mock_unity["params"]["properties"]["edges"] == [{"a": 0, "b": 1}, {"a": 2, "b": 3}]
|
|
|
|
|
|
def test_extrude_edges_with_vertex_pairs(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="extrude_edges",
|
|
target="MyCube",
|
|
properties={"edges": [{"a": 0, "b": 1}], "distance": 0.5},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["properties"]["edges"] == [{"a": 0, "b": 1}]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Detach faces with deleteSourceFaces
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_detach_faces_with_delete_source(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="detach_faces",
|
|
target="MyCube",
|
|
properties={"faceIndices": [0], "deleteSourceFaces": True},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["properties"]["deleteSourceFaces"] is True
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Bridge edges with allowNonManifold
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_bridge_edges_with_allow_non_manifold(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="bridge_edges",
|
|
target="MyCube",
|
|
properties={
|
|
"edgeA": {"a": 0, "b": 1},
|
|
"edgeB": {"a": 2, "b": 3},
|
|
"allowNonManifold": True,
|
|
},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["properties"]["allowNonManifold"] is True
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Merge vertices with collapseToFirst
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_merge_vertices_with_collapse_to_first(mock_unity):
|
|
result = asyncio.run(
|
|
manage_probuilder(
|
|
SimpleNamespace(),
|
|
action="merge_vertices",
|
|
target="MyCube",
|
|
properties={"vertexIndices": [0, 1], "collapseToFirst": True},
|
|
)
|
|
)
|
|
assert result["success"] is True
|
|
assert mock_unity["params"]["properties"]["collapseToFirst"] is True
|