140 lines
5.2 KiB
Python
140 lines
5.2 KiB
Python
"""Tests for the import_model_file asset-gen tool and CLI command (local model import).
|
|
|
|
Pass-through tool: NO API keys, NO file bytes. Unity transport fully mocked.
|
|
"""
|
|
|
|
import asyncio
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock, AsyncMock
|
|
from click.testing import CliRunner
|
|
|
|
from cli.commands.asset_gen import asset_gen
|
|
from cli.utils.config import CLIConfig
|
|
from services.registry import get_registered_tools
|
|
|
|
from services.tools import import_model_file as mod
|
|
from services.tools.import_model_file import import_model_file
|
|
|
|
|
|
COMMAND = "import_model_file"
|
|
ALLOWED_KEYS = {"sourcePath", "name", "outputFolder", "targetSize", "animationType"}
|
|
|
|
|
|
def _call_tool(**kwargs):
|
|
ctx = MagicMock()
|
|
with patch.object(mod, "get_unity_instance_from_context",
|
|
new=AsyncMock(return_value="unity-1")):
|
|
with patch.object(mod, "send_with_unity_instance",
|
|
new=AsyncMock(return_value={"success": True, "data": {}})) as mock_send:
|
|
result = asyncio.run(import_model_file(ctx, **kwargs))
|
|
return result, mock_send.call_args.args
|
|
|
|
|
|
def _sent_command(sent_args):
|
|
return sent_args[2]
|
|
|
|
|
|
def _sent_params(sent_args):
|
|
return sent_args[3]
|
|
|
|
|
|
@pytest.fixture
|
|
def runner():
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_config():
|
|
return CLIConfig(host="127.0.0.1", port=8080, timeout=30, format="text", unity_instance=None)
|
|
|
|
|
|
@pytest.fixture
|
|
def cli_runner(runner, mock_config):
|
|
def _invoke(args):
|
|
with patch("cli.commands.asset_gen.get_config", return_value=mock_config):
|
|
with patch("cli.commands.asset_gen.run_command",
|
|
return_value={"success": True, "message": "OK", "data": {}}) as mock_run:
|
|
result = runner.invoke(asset_gen, args)
|
|
return result, mock_run
|
|
return _invoke
|
|
|
|
|
|
class TestImportModelFileRegistration:
|
|
def test_tool_registered_under_asset_gen_group(self):
|
|
tools = get_registered_tools()
|
|
tool = next((t for t in tools if t["name"] == "import_model_file"), None)
|
|
assert tool is not None
|
|
assert tool["group"] == "asset_gen"
|
|
|
|
|
|
class TestImportModelFileRouting:
|
|
def test_routes_to_command_with_param_mapping(self):
|
|
_, sent = _call_tool(
|
|
source_path="/tmp/cube.fbx", name="Cube",
|
|
output_folder="Assets/Generated/Imported", target_size=2.0,
|
|
)
|
|
assert _sent_command(sent) == COMMAND
|
|
params = _sent_params(sent)
|
|
assert params["sourcePath"] == "/tmp/cube.fbx"
|
|
assert params["name"] == "Cube"
|
|
assert params["outputFolder"] == "Assets/Generated/Imported"
|
|
assert params["targetSize"] == 2.0
|
|
assert "source_path" not in params and "output_folder" not in params and "target_size" not in params
|
|
|
|
def test_none_values_stripped(self):
|
|
_, sent = _call_tool(source_path="/tmp/x.obj")
|
|
assert _sent_params(sent) == {"sourcePath": "/tmp/x.obj"}
|
|
|
|
def test_animation_type_mapped(self):
|
|
_, sent = _call_tool(source_path="/tmp/rig.fbx", animation_type="generic")
|
|
params = _sent_params(sent)
|
|
assert params["animationType"] == "generic"
|
|
assert "animation_type" not in params
|
|
assert set(params.keys()).issubset(ALLOWED_KEYS)
|
|
|
|
def test_no_secret_keys_in_payload(self):
|
|
_, sent = _call_tool(
|
|
source_path="/tmp/a.glb", name="N",
|
|
output_folder="Assets/Generated/Imported", target_size=1.0,
|
|
)
|
|
params = _sent_params(sent)
|
|
assert set(params.keys()).issubset(ALLOWED_KEYS)
|
|
joined = " ".join(params.keys()).lower()
|
|
for forbidden in ("key", "secret", "token", "apikey", "password"):
|
|
assert forbidden not in joined
|
|
|
|
def test_non_dict_response_guarded(self):
|
|
ctx = MagicMock()
|
|
with patch.object(mod, "get_unity_instance_from_context",
|
|
new=AsyncMock(return_value="u")):
|
|
with patch.object(mod, "send_with_unity_instance",
|
|
new=AsyncMock(return_value=None)):
|
|
result = asyncio.run(import_model_file(ctx, source_path="/tmp/x.obj"))
|
|
assert result["success"] is False
|
|
|
|
|
|
class TestImportModelFileCLI:
|
|
def test_import_model_file_cli(self, cli_runner):
|
|
result, mock_run = cli_runner([
|
|
"import-model-file", "--source-path", "/tmp/cube.fbx",
|
|
"--name", "Cube", "--output-folder", "Assets/Props", "--target-size", "1.5",
|
|
])
|
|
assert result.exit_code == 0
|
|
command = mock_run.call_args.args[0]
|
|
params = mock_run.call_args.args[1]
|
|
assert command == COMMAND
|
|
assert params["sourcePath"] == "/tmp/cube.fbx"
|
|
assert params["name"] == "Cube"
|
|
assert params["outputFolder"] == "Assets/Props"
|
|
assert params["targetSize"] == 1.5
|
|
assert set(params.keys()).issubset(ALLOWED_KEYS)
|
|
|
|
def test_import_model_file_cli_animation_type(self, cli_runner):
|
|
result, mock_run = cli_runner([
|
|
"import-model-file", "--source-path", "/tmp/rig.fbx", "--animation-type", "humanoid",
|
|
])
|
|
assert result.exit_code == 0
|
|
params = mock_run.call_args.args[1]
|
|
assert params["animationType"] == "humanoid"
|
|
assert set(params.keys()).issubset(ALLOWED_KEYS)
|