From 404f55c94e72b2623c9e770dd95e50e7baa0c322 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Sun, 19 Apr 2026 19:56:48 +0800 Subject: [PATCH] test(agent-core): add ReActAgentComponent end-to-end test with FakeEchoModel Co-Authored-By: Claude Opus 4.7 --- .../agent/component/FakeEchoModel.java | 34 +++++++ .../component/ReActAgentComponentTest.java | 95 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/FakeEchoModel.java create mode 100644 liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/ReActAgentComponentTest.java diff --git a/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/FakeEchoModel.java b/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/FakeEchoModel.java new file mode 100644 index 000000000..82635abd6 --- /dev/null +++ b/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/FakeEchoModel.java @@ -0,0 +1,34 @@ +package com.yomahub.liteflow.agent.component; + +import io.agentscope.core.message.ContentBlock; +import io.agentscope.core.message.Msg; +import io.agentscope.core.message.TextBlock; +import io.agentscope.core.model.ChatResponse; +import io.agentscope.core.model.GenerateOptions; +import io.agentscope.core.model.Model; +import io.agentscope.core.model.ToolSchema; +import reactor.core.publisher.Flux; + +import java.util.List; + +/** + * Minimal {@link Model} implementation for tests. Returns a fixed + * "[echo]" text response regardless of input. + */ +public class FakeEchoModel implements Model { + + @Override + public Flux stream(List messages, List toolSchemas, GenerateOptions options) { + ChatResponse resp = ChatResponse.builder() + .id("fake-id") + .content(List.of(TextBlock.builder().text("[echo]").build())) + .finishReason("stop") + .build(); + return Flux.just(resp); + } + + @Override + public String getModelName() { + return "fake-echo-model"; + } +} diff --git a/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/ReActAgentComponentTest.java b/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/ReActAgentComponentTest.java new file mode 100644 index 000000000..d7e9f1f9c --- /dev/null +++ b/liteflow-react-agent/liteflow-react-agent-core/src/test/java/com/yomahub/liteflow/agent/component/ReActAgentComponentTest.java @@ -0,0 +1,95 @@ +package com.yomahub.liteflow.agent.component; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.flow.element.Node; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.property.agent.AgentConfig; +import com.yomahub.liteflow.property.agent.ShellMode; +import com.yomahub.liteflow.slot.DataBus; +import com.yomahub.liteflow.slot.Slot; +import io.agentscope.core.model.Model; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class ReActAgentComponentTest { + + @TempDir + Path tmp; + + @BeforeEach + void init() { + AgentConfig agent = new AgentConfig(); + agent.getWorkspace().setRoot(tmp.toString()); + agent.getShell().setMode(ShellMode.DISABLED); + LiteflowConfig cfg = new LiteflowConfig(); + cfg.setAgent(agent); + LiteflowConfigGetter.setLiteflowConfig(cfg); + DataBus.init(); + } + + @AfterEach + void cleanup() { + ReActAgentComponent.AgentSessionManagerHolder.resetForTesting(); + LiteflowConfigGetter.clean(); + } + + /** + * Concrete subclass under test. + */ + static class TestAgent extends ReActAgentComponent { + @Override + protected Model buildModel(ReActAgentContext ctx) { return new FakeEchoModel(); } + @Override + protected String systemPrompt(ReActAgentContext ctx) { return "you are helpful"; } + @Override + protected String userPrompt(ReActAgentContext ctx) { + return (String) ctx.getSlot().getChainReqData(ctx.getSlot().getChainId()); + } + } + + /** + * End-to-end test: process() builds an agent, calls it, and writes the reply + * into the slot's response data. Also verifies that the session workspace + * directory is created. + */ + @Test + void process_creates_workspace_and_writes_response() throws Exception { + // 1. Create a slot via DataBus + int slotIndex = DataBus.offerSlotByBean(Collections.emptyList()); + Slot slot = DataBus.getSlot(slotIndex); + slot.putRequestId("req-1"); + String chainId = "testChain"; + slot.setChainId(chainId); + slot.setChainReqData(chainId, "hello"); + + // 2. Wire the NodeComponent into a Node with the slot index + TestAgent agent = new TestAgent(); + agent.setNodeId("testAgent"); + Node node = new Node(); + node.setInstance(agent); + node.setSlotIndex(slotIndex); + node.setCurrChainId(chainId); + agent.setRefNode(node); + + // 3. Execute + agent.process(); + + // 4. Verify workspace was created + assertTrue(Files.isDirectory(tmp.resolve("req-1")), + "session workspace directory should exist under the temp root"); + + // 5. Verify response data + assertEquals("[echo]", slot.getResponseData(), + "response data should be the fake echo text"); + + // 6. Cleanup + DataBus.releaseSlot(slotIndex); + } +}