fix: make Android tasks use extensions automatically and populate marketplace

This commit is contained in:
chenmingxuan committed 2026-10-14 22:32:38 +08:00
1 parent 6663233ab8
commit 9c82a40cbf
32 files changed
+341 -131

No files matched your search

+11
View File
@@ -88,6 +88,17 @@ def test_mobile_run_task_invalid_model():
mobile_run_task(task_desc="test", conversation_id="conv-1", model="invalid_model")
def test_auto_plugins_do_not_silently_fall_back_to_a_different_executor(temp_trace_env):
with (
patch("artemis.plugins.store.PluginStore.selections", return_value=["desktop"]),
patch("mcp_server.tools.task_runner.subprocess.Popen") as popen,
):
result = mobile_run_task(task_desc="手机测试中操作电脑", model="flash")
assert result["status"] == "failed"
assert "统一任务服务" in result["error"]
popen.assert_not_called()
def test_mobile_run_task_reserves_and_passes_global_queue_ticket(temp_trace_env):
process = MagicMock(pid=43210)
with (
+51 -1
View File
@@ -57,6 +57,54 @@ def enable(store, plugin, **profile):
store.set_enabled(plugin.id, True)
@pytest.mark.asyncio
async def test_android_task_discovers_cross_device_plugins_and_loads_only_on_demand(store):
plugin = manifest(targets=["computer", "mixed"])
enable(store, plugin)
runtime = PluginRuntime(store=store, target="android")
try:
assert list(runtime.packages) == [plugin.id]
assert runtime.loaded == set()
assert runtime.sessions == {}
assert plugin.id in runtime.prompt()
assert (await runtime.call("plugin_load", {"plugin_id": plugin.id})).status == "success"
assert runtime.loaded == {plugin.id}
finally:
await runtime.aclose()
def test_android_auto_selection_respects_enablement_authorization_and_explicit_opt_out(store):
enable(store, manifest(targets=["mixed"]))
assert len(store.pin(None, "android")) == 1
assert store.pin([], "android") == []
store.set_enabled("test-plugin", False)
assert store.pin(None, "android") == []
with pytest.raises(ValueError, match="未启用"):
store.pin([PluginSelection(id="test-plugin")], "android")
store.set_enabled("test-plugin", True)
store.save_profile("test-plugin", PluginProfile(authorized=False))
assert store.pin(None, "android") == []
with pytest.raises(ValueError, match="授权"):
store.pin([PluginSelection(id="test-plugin")], "android")
def test_android_task_uses_authorized_default_and_allows_explicit_profile(store):
enable(store, manifest(targets=["mixed"]))
store.save_profile(
"test-plugin",
PluginProfile(name="lab", is_default=True, authorized=True, secrets={"token": "lab"}),
)
assert store.pin(None, "android")[0].profile == "lab"
assert store.pin([PluginSelection(id="test-plugin")], "android")[0].profile == "default"
def test_computer_only_plugin_is_not_automatically_offered_to_android(store):
enable(store, manifest(targets=["computer"]))
assert store.pin(None, "android") == []
with pytest.raises(ValueError, match="不支持"):
store.pin([PluginSelection(id="test-plugin")], "android")
def test_profile_secrets_are_write_only_and_blank_preserves(store):
enable(store, manifest())
assert "private-value" not in json.dumps(store.list())
@@ -921,7 +969,7 @@ async def test_pro_text_only_observation_never_sends_an_empty_image():
@pytest.mark.asyncio
async def test_plugin_image_text_stays_with_its_own_observation(store, tmp_path):
async def test_plugin_image_text_stays_with_its_own_observation(store, tmp_path, monkeypatch):
from types import SimpleNamespace
from unittest.mock import MagicMock
from PIL import Image
@@ -929,6 +977,8 @@ async def test_plugin_image_text_stays_with_its_own_observation(store, tmp_path)
from artemis.data_engine.engine import DataEngine
from artemis.plugins.models import PluginCallResult
monkeypatch.setattr(DataEngine, "_ipc_port_candidates", staticmethod(lambda: []))
monkeypatch.setattr("artemis.data_engine.engine.PAUSE_FILE", tmp_path / "pause")
ctx = MagicMock(spec=ArtemisContext)
ctx.execution_setup = SimpleNamespace(traces_path=str(tmp_path / "traces"))
ctx.device = None