package com.battery.business.web; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import com.battery.business.service.ITestLiveService; import com.ruoyi.common.exception.ServiceException; /** * SSE 推送的失败收尾:**只结束连接,不交给 MVC 的异常分发**。 * *
背景(线上实测):测试被删除后,订阅它的实时页还在推,`current` 抛「测试不存在」; * 原实现走 `completeWithError`,Spring 于是把异常送去全局异常处理器,而响应头已经是 * `text/event-stream`,写 `AjaxResult` JSON 必然失败——连锁抛出 HttpMessageNotWritableException * 与安全链拒绝,每 3 秒刷一屏 ERROR。 */ class TestLiveStreamTest { /** 只记录被怎么结束,不真的写出:SseEmitter 未 initialize 时 complete 是安全空操作。 */ private static final class RecordingEmitter extends SseEmitter { private boolean completed; private boolean failedWithError; private RecordingEmitter() { super(0L); } @Override public synchronized void complete() { completed = true; super.complete(); } @Override public synchronized void completeWithError(Throwable failure) { failedWithError = true; super.completeWithError(failure); } } @Test void endsTheSubscriptionInsteadOfHandingTheFailureToMvcWhenTheTestIsGone() { RecordingEmitter emitter = new RecordingEmitter(); TestLiveStream stream = stream(emitter, testId -> { throw new ServiceException("测试不存在"); }); SseEmitter returned = stream.stream(4242L); assertSame(emitter, returned); assertTrue(emitter.completed); // 关键:不走 completeWithError —— 那会把异常送进 MVC 的异常分发。 assertFalse(emitter.failedWithError); } @Test void keepsPushingWhileTheTestIsThere() { RecordingEmitter emitter = new RecordingEmitter(); TestLiveStream stream = stream(emitter, testId -> liveState(testId)); stream.stream(7L); assertFalse(emitter.completed); assertFalse(emitter.failedWithError); } private static TestLiveStream stream(SseEmitter emitter, ITestLiveService live) { return new TestLiveStream(live) { @Override protected SseEmitter createEmitter() { return emitter; } }; } private static ITestLiveService.LiveState liveState(Long testId) { return new ITestLiveService.LiveState(testId, "RUNNING", "RECEIVING", 1L, java.util.List.of(), java.util.List.of(), null); } }