mateclaw/mateclaw-server/src/test/java/vip/mate/agent/context/ChatOriginTest.java

92 lines
3.6 KiB
Java

package vip.mate.agent.context;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.model.ToolContext;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* RFC-063r §2.1: ChatOrigin value-object invariants.
*/
class ChatOriginTest {
@Test
void from_nullToolContext_returnsEmpty() {
assertSame(ChatOrigin.EMPTY, ChatOrigin.from(null));
}
@Test
void from_toolContextWithoutOrigin_returnsEmpty() {
ToolContext ctx = new ToolContext(Map.of("unrelated.key", "x"));
assertSame(ChatOrigin.EMPTY, ChatOrigin.from(ctx));
}
@Test
void roundTripThroughToolContext_preservesAllFields() {
ChannelTarget target = new ChannelTarget("user-42", "thread-abc", "bot-001");
ChatOrigin original = new ChatOrigin(7L, "wechat:42", "u123", 5L,
"/data/ws/5", 9L, target, false, null, null, null);
ToolContext ctx = original.toToolContext();
ChatOrigin restored = ChatOrigin.from(ctx);
assertEquals(original, restored);
}
@Test
void wither_doesNotMutateOriginal() {
ChatOrigin base = ChatOrigin.cron("cron_1", 5L, "/data/ws/5", 9L,
new ChannelTarget("group-a", null, null));
ChatOrigin enriched = base.withAgent(42L);
assertNull(base.agentId(), "withAgent must not mutate the original");
assertEquals(42L, enriched.agentId());
assertEquals(base.channelId(), enriched.channelId(), "channelId must be preserved");
assertEquals(base.channelTarget(), enriched.channelTarget(),
"channelTarget must be preserved");
}
@Test
void cronFactory_setsRequesterToSystem() {
ChatOrigin origin = ChatOrigin.cron("cron_7", 1L, null, 3L, null);
assertEquals("system", origin.requesterId());
assertNull(origin.agentId(), "agentId is enriched later by BaseAgent");
}
@Test
void cronOriginFlag_setByFactoryAndPreservedByWithers() {
ChatOrigin cron = ChatOrigin.cron("cron_7", 1L, null, 3L, null);
assertTrue(cron.cronOrigin(), "cron() factory must flag the origin as a cron run");
assertTrue(cron.withAgent(9L).cronOrigin(), "withAgent must preserve cronOrigin");
assertTrue(cron.withConversationId("tasks_1").cronOrigin(),
"withConversationId must preserve cronOrigin");
assertTrue(cron.withWorkspace(2L, "/ws").cronOrigin(),
"withWorkspace must preserve cronOrigin");
assertFalse(ChatOrigin.web("conv_1", "u1", 1L, null).cronOrigin(),
"web() origin must not be flagged as a cron run");
assertFalse(ChatOrigin.EMPTY.cronOrigin(), "EMPTY must not be flagged as a cron run");
}
@Test
void jsonSerialization_isStableAndForwardCompatible() throws Exception {
ObjectMapper om = new ObjectMapper();
ChatOrigin origin = new ChatOrigin(7L, "wechat:42", "u123", 5L,
"/data/ws/5", 9L, new ChannelTarget("user-42", "thread-abc", "bot-001"), false, null, null, null);
String json = om.writeValueAsString(origin);
ChatOrigin restored = om.readValue(json, ChatOrigin.class);
assertEquals(origin, restored);
// RFC-063r §2.1 forward compatibility: future-added unknown fields
// must not break deserialization (covers approval rows surviving upgrades).
String jsonWithExtraField = json.replaceFirst("\\}$", ",\"futureField\":\"x\"}");
ChatOrigin tolerated = om.readValue(jsonWithExtraField, ChatOrigin.class);
assertEquals(origin, tolerated);
}
}