Files
tuanwei-java/src/test/java/com/gxwebsoft/ai/config/WebSocketServerSilentPushTest.java
T
weicw-dshsa 21e49c6a1a fix(ai): WebSocket 推送静默化,并补重试与静默推送用例(issue 11 / 12)
- WebSocketServer.sendMessage 里两行 System.out.println 删除。该方法被**每个 SSE
  事件**调用一次(大纲单次生成实测 3062 个事件,即一次请求刷 6000+ 行),而且绕过
  日志框架——既不受 logging.level 控制(生产把 com.gxwebsoft 压到 ERROR 也拦不住),
  也不进日志文件,真故障时需要的几行会被顶掉。日志改到低频的 onOpen / onClose;
  同时把 onOpen 里静默吞掉的握手回应异常改成 WARN(那是「连上了却收不到内容」的
  第一现场)。
- 新增用例:AiCreativeRetryTest、AiCreativeRetryIntegrationTest(issue 11,
  后者用本地 HTTP server 扮演 Dify,断言瞬时故障确实重跑一次、凭证失效只请求一次)、
  WebSocketServerSilentPushTest(issue 12,捕获 System.out 断言推送路径零输出)。
2026-10-13 20:00:01 +08:00

83 lines
3.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 推送是**高频路径**,不得往标准输出打印。
*
* <p>两条 AI 流式链路(创意助手、活动策划大纲)每个 SSE 事件都会调一次
* {@link WebSocketServer#sendMessage(String, String)}:实测一次大纲生成 3062 个事件,
* 即单次请求就会调用 3000+ 次。最初这里有两行 {@code System.out.println}
* ({@code userId} 与 {@code session1}),于是一次生成就在控制台刷出上万行
* 「userId:833」,既刷屏又绕过日志级别(见 .scratch/ai-assistant/issues/12)。
*
* <p>两个分支(目标已连接 / 未连接)各锁一条:改回 {@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<Void> action) throws Exception {
PrintStream original = System.out;
ByteArrayOutputStream captured = new ByteArrayOutputStream();
try {
System.setOut(new PrintStream(captured, true, StandardCharsets.UTF_8.name()));
action.call();
} finally {
System.setOut(original);
}
return captured.toString(StandardCharsets.UTF_8.name());
}
/**
* 一个什么都不做的 {@link Session}:只为让 {@code sendMessage} 走进"已连接"分支。
*/
private static Session fakeSession() {
return (Session) Proxy.newProxyInstance(Session.class.getClassLoader(),
new Class<?>[]{Session.class}, (proxy, method, args) -> {
if ("getBasicRemote".equals(method.getName())) {
return Proxy.newProxyInstance(RemoteEndpoint.Basic.class.getClassLoader(),
new Class<?>[]{RemoteEndpoint.Basic.class},
(endpoint, call, callArgs) -> null);
}
return null;
});
}
}