package com.gxwebsoft.ai.config; import org.junit.jupiter.api.Test; import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.reflect.Proxy; import java.nio.charset.StandardCharsets; import java.util.concurrent.Callable; import static org.junit.jupiter.api.Assertions.assertEquals; /** * WebSocket 推送是**高频路径**,不得往标准输出打印。 * *
两条 AI 流式链路(创意助手、活动策划大纲)每个 SSE 事件都会调一次 * {@link WebSocketServer#sendMessage(String, String)}:实测一次大纲生成 3062 个事件, * 即单次请求就会调用 3000+ 次。最初这里有两行 {@code System.out.println} * ({@code userId} 与 {@code session1}),于是一次生成就在控制台刷出上万行 * 「userId:833」,既刷屏又绕过日志级别(见 .scratch/ai-assistant/issues/12)。 * *
两个分支(目标已连接 / 未连接)各锁一条:改回 {@code println} 立刻失败。
*/
class WebSocketServerSilentPushTest {
@Test
void 目标未连接时推送不得打印() throws Exception {
String output = captureStdout(() -> {
// 未连接的 userId:走"查不到会话"的分支,也正是刷屏时最常见的那条
new WebSocketServer().sendMessage("silent-push-absent", "{\"answer\":\"测试\"}");
return null;
});
assertEquals("", output, "推送是高频路径,不能刷标准输出");
}
@Test
void 目标已连接时推送也不得打印() throws Exception {
WebSocketServer server = new WebSocketServer();
// onOpen 会打一条 INFO(连接日志,低频且应当保留),故放在捕获之外
server.onOpen(fakeSession(), "silent-push-open");
String output = captureStdout(() -> {
server.sendMessage("silent-push-open", "{\"answer\":\"测试\"}");
return null;
});
assertEquals("", output, "推送是高频路径,不能刷标准输出");
}
/**
* 捕获一段代码往标准输出写的内容。推送是 void 且抛受检异常,故用 {@link Callable}。
*/
private static String captureStdout(Callable