From 420d51af15d1aa3ad19a888912bcc0cc11d3f1b9 Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Fri, 16 Jan 2026 23:28:46 +0800 Subject: [PATCH] fix(LocalRunnerClient): harden doRun branches and add tests --- .../core/action/runner/LocalRunnerClient.java | 13 ++- .../action/runner/LocalRunnerClientTest.java | 101 +++++++++++++++++- 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java index 142f90a0..21e20f1c 100644 --- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java +++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java @@ -222,6 +222,10 @@ public class LocalRunnerClient extends RunnerClient { return response; } String[] commands = SystemExecHelper.buildCommands(ext, metaAction.getParams(), file.getAbsolutePath()); + if (commands == null || commands.length == 0) { + response.setOk(false); + response.setData("不支持的文件类型: " + file.getName()); + } SystemExecHelper.Result execResult = SystemExecHelper.exec(commands); response.setOk(execResult.isOk()); response.setData(execResult.getTotal()); @@ -236,7 +240,12 @@ public class LocalRunnerClient extends RunnerClient { .arguments(metaAction.getParams()) .build(); McpSchema.CallToolResult callToolResult = mcpClient.callTool(callToolRequest); - response.setOk(callToolResult.isError()); + val callToolResultError = callToolResult.isError(); + if (callToolResultError == null) { + response.setOk(false); + } else { + response.setOk(!callToolResultError); + } response.setData(callToolResult.structuredContent().toString()); return response; } @@ -1234,7 +1243,7 @@ public class LocalRunnerClient extends RunnerClient { val httpKeys = Set.of("uri", "endpoint", "headers"); val httpKey = Set.of("url"); val keys = mcp.keySet(); - val timeout = mcp.getInt("timeout", 10); + val timeout = mcp.getInt("timeout", 30); if (keys.equals(stdioKeys)) { val command = mcp.getStr("command"); diff --git a/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java b/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java index f502f296..c60d8d24 100644 --- a/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java +++ b/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java @@ -5,16 +5,15 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import work.slhaf.partner.core.action.entity.MetaAction; import work.slhaf.partner.core.action.entity.MetaActionInfo; +import work.slhaf.partner.core.action.entity.MetaActionType; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -177,6 +176,15 @@ public class LocalRunnerClientTest { + " \"env\": {}\n" + " }"; } + + static MetaAction buildMetaAction(MetaActionType type, String location, String name, Map params) { + MetaAction metaAction = new MetaAction(); + metaAction.setType(type); + metaAction.setLocation(location); + metaAction.setName(name); + metaAction.setParams(params); + return metaAction; + } } @Nested @@ -762,4 +770,89 @@ public class LocalRunnerClientTest { } } + @Nested + class DoRunTest { + + @Test + void testDoRunWithOriginUnknownExt(@TempDir Path tempDir) throws IOException { + ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); + + try { + Path script = tempDir.resolve("run"); + Files.writeString(script, "echo ok\n"); + MetaAction metaAction = buildMetaAction(MetaActionType.ORIGIN, script.toString(), "run", Map.of()); + RunnerClient.RunnerResponse response = client.doRun(metaAction); + Assertions.assertNotNull(response); + Assertions.assertFalse(response.isOk()); + Assertions.assertEquals("未知文件类型", response.getData()); + } finally { + executor.shutdownNow(); + } + } + + @Test + void testDoRunWithOriginScriptSuccess(@TempDir Path tempDir) throws IOException { + ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); + + try { + Path script = tempDir.resolve("run.sh"); + Files.writeString(script, "echo ok\n"); + MetaAction metaAction = buildMetaAction(MetaActionType.ORIGIN, script.toString(), "run", Map.of()); + RunnerClient.RunnerResponse response = client.doRun(metaAction); + Assertions.assertNotNull(response); + Assertions.assertTrue(response.isOk()); + Assertions.assertTrue(response.getData().contains("ok")); + } finally { + executor.shutdownNow(); + } + } + + @Test + void testDoRunWithMcpMissingClient(@TempDir Path tempDir) { + ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); + + try { + MetaAction metaAction = buildMetaAction(MetaActionType.MCP, "missing-client", "missing-tool", Map.of()); + RunnerClient.RunnerResponse response = client.doRun(metaAction); + Assertions.assertNotNull(response); + Assertions.assertFalse(response.isOk()); + } finally { + executor.shutdownNow(); + } + } + + @Test + void testDoRunWithMcpLoadedFromCommonConfig(@TempDir Path tempDir) throws IOException, InterruptedException { + ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + + Path mcpDir = tempDir.resolve("action").resolve("mcp"); + Files.createDirectories(mcpDir); + Path configFile = mcpDir.resolve("servers.json"); + String config = buildCommonMcpConfig( + buildStdioServerEntry("playwright", "@playwright/mcp@latest") + ); + writeCommonMcpConfig(configFile, config); + + LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); + + try { + waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")), 20000); + Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("playwright::"))); + + MetaAction metaAction = buildMetaAction(MetaActionType.MCP, "playwright", "browser_navigate", Map.of("url", "https://deepwiki.com/microsoft/vscode")); + client.run(metaAction); + Assertions.assertNotEquals(MetaAction.ResultStatus.WAITING, metaAction.getResult().getStatus()); + } finally { + executor.shutdownNow(); + } + } + } + }