yeg
4 天以前 46634e427fdd43e80c017938d1aa35f25a3166ac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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 的异常分发**。
 *
 * <p>背景(线上实测):测试被删除后,订阅它的实时页还在推,`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);
    }
}