mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 19:23:42 +08:00
140 lines
5.3 KiB
Java
140 lines
5.3 KiB
Java
package vip.mate.llm.chatmodel;
|
|
|
|
import org.junit.jupiter.api.AfterEach;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.util.List;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
/**
|
|
* {@link AssistantThinkingRelay} — RelayEntry carries both per-assistant thinking
|
|
* and the caller's original {@code user} field, so the consumer can restore it
|
|
* when rebuilding the outbound request.
|
|
*/
|
|
class AssistantThinkingRelayTest {
|
|
|
|
@BeforeEach
|
|
void clear() {
|
|
AssistantThinkingRelay.clearAll();
|
|
}
|
|
|
|
@AfterEach
|
|
void tearDown() {
|
|
AssistantThinkingRelay.clearAll();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("stash returns token with expected prefix")
|
|
void stash_returnsTokenWithPrefix() {
|
|
String token = AssistantThinkingRelay.stash(List.of("thinking-a"), null);
|
|
assertTrue(AssistantThinkingRelay.isToken(token));
|
|
assertTrue(token.startsWith(AssistantThinkingRelay.TOKEN_PREFIX));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("stash + take roundtrips thinkings in order and originalUser")
|
|
void stashTake_roundtrip() {
|
|
List<String> thinkings = List.of("one", "", "three");
|
|
String token = AssistantThinkingRelay.stash(thinkings, "caller-user-42");
|
|
AssistantThinkingRelay.RelayEntry entry = AssistantThinkingRelay.take(token);
|
|
|
|
assertNotNull(entry);
|
|
assertEquals(List.of("one", "", "three"), entry.thinkings());
|
|
assertEquals("caller-user-42", entry.originalUser());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("stash + take with null originalUser preserves null")
|
|
void stashTake_nullOriginalUser() {
|
|
String token = AssistantThinkingRelay.stash(List.of("x"), null);
|
|
AssistantThinkingRelay.RelayEntry entry = AssistantThinkingRelay.take(token);
|
|
assertNotNull(entry);
|
|
assertNull(entry.originalUser());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("take removes entry — subsequent take returns null")
|
|
void take_removesEntry() {
|
|
String token = AssistantThinkingRelay.stash(List.of("x"), "u");
|
|
assertNotNull(AssistantThinkingRelay.take(token));
|
|
assertNull(AssistantThinkingRelay.take(token));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("take on non-token user returns null")
|
|
void take_onNonToken_returnsNull() {
|
|
assertNull(AssistantThinkingRelay.take(null));
|
|
assertNull(AssistantThinkingRelay.take(""));
|
|
assertNull(AssistantThinkingRelay.take("some-real-user-id"));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("isToken: prefix-based detection")
|
|
void isToken_prefixDetection() {
|
|
assertFalse(AssistantThinkingRelay.isToken(null));
|
|
assertFalse(AssistantThinkingRelay.isToken(""));
|
|
assertFalse(AssistantThinkingRelay.isToken("regular-user"));
|
|
assertTrue(AssistantThinkingRelay.isToken(AssistantThinkingRelay.TOKEN_PREFIX + "anything"));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("discard after take is a no-op (idempotent)")
|
|
void discard_idempotent() {
|
|
String token = AssistantThinkingRelay.stash(List.of("x"), "u");
|
|
AssistantThinkingRelay.take(token);
|
|
// Should not throw and not affect other entries
|
|
AssistantThinkingRelay.discard(token);
|
|
assertEquals(0, AssistantThinkingRelay.size());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("discard without take removes the entry (producer failure path)")
|
|
void discard_withoutTake_removes() {
|
|
String token = AssistantThinkingRelay.stash(List.of("x"), "u");
|
|
assertEquals(1, AssistantThinkingRelay.size());
|
|
AssistantThinkingRelay.discard(token);
|
|
assertEquals(0, AssistantThinkingRelay.size());
|
|
// Subsequent take still returns null
|
|
assertNull(AssistantThinkingRelay.take(token));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("concurrent stashes produce distinct tokens")
|
|
void stash_distinctTokens() {
|
|
String a = AssistantThinkingRelay.stash(List.of("a"), "ua");
|
|
String b = AssistantThinkingRelay.stash(List.of("b"), "ub");
|
|
assertNotEquals(a, b);
|
|
|
|
AssistantThinkingRelay.RelayEntry ea = AssistantThinkingRelay.take(a);
|
|
AssistantThinkingRelay.RelayEntry eb = AssistantThinkingRelay.take(b);
|
|
assertEquals(List.of("a"), ea.thinkings());
|
|
assertEquals("ua", ea.originalUser());
|
|
assertEquals(List.of("b"), eb.thinkings());
|
|
assertEquals("ub", eb.originalUser());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("RelayEntry.thinkings is immutable (defensive copy)")
|
|
void relayEntry_thinkingsImmutable() {
|
|
java.util.ArrayList<String> mutable = new java.util.ArrayList<>(List.of("a", "b"));
|
|
String token = AssistantThinkingRelay.stash(mutable, "u");
|
|
mutable.set(0, "mutated"); // should not affect the stashed copy
|
|
|
|
AssistantThinkingRelay.RelayEntry entry = AssistantThinkingRelay.take(token);
|
|
assertEquals(List.of("a", "b"), entry.thinkings());
|
|
|
|
// thinkings returned is also unmodifiable
|
|
assertThrows(UnsupportedOperationException.class,
|
|
() -> entry.thinkings().set(0, "x"));
|
|
}
|
|
}
|