Files
MailUI4Agents/server/internal/sse/e2e_test.go

109 lines
3.1 KiB
Go
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 sse
import (
"net/http/httptest"
"strings"
"testing"
)
// 一个可 Flush 的 ResponseWriter供集成测试用。
type flushWriter struct {
*httptest.ResponseRecorder
flushed chan bool
}
func (f *flushWriter) Flush() {
select {
case f.flushed <- true:
default:
}
}
// 端到端验证Manager 完整走一遍「事件入缓冲区 → 新连接带 Last-Event-ID 重连 → 补投」。
// 这是生产里最关键的可靠性路径 —— 断线期间收的邮件,重连后必须能看到。
func TestManagerReplayOnReconnect(t *testing.T) {
m := &Manager{
clients: make(map[string]*Client),
eventBuffer: make(map[string]*eventRing),
}
// 1) 用户 alice 发来一封信(无人连接也入缓冲区)
m.SendToUser("alice", "new_mail", map[string]string{"mail_id": "m1"})
m.SendToUser("alice", "new_mail", map[string]string{"mail_id": "m2"})
m.SendToUser("alice", "new_mail", map[string]string{"mail_id": "m3"})
ring := m.eventBuffer["u:alice"]
if ring == nil {
t.Fatal("alice 的缓冲区应该已创建")
}
// 2) 带 Last-Event-ID=1 重连,应补投 m2、m3跳过 m1
req := httptest.NewRequest("GET", "/events/stream", nil)
req.Header.Set("Last-Event-ID", "1")
w := &flushWriter{httptest.NewRecorder(), make(chan bool, 10)}
client := m.AddClient(w, req, "", "alice")
if client == nil {
t.Fatal("AddClient 返回 nilflushWriter 应支持 Flush")
}
defer m.RemoveClient(client.ID)
body := w.Body.String()
if !strings.Contains(body, `"mail_id":"m2"`) {
t.Error("重连后应补投 m2实际 body:", body)
}
if !strings.Contains(body, `"mail_id":"m3"`) {
t.Error("重连后应补投 m3实际 body:", body)
}
if strings.Contains(body, `"mail_id":"m1"`) {
t.Error("已确认的 m1 不应重放Last-Event-ID=1实际 body:", body)
}
// 3) 连接期间新来一封信,实时推送
m.SendToUser("alice", "new_mail", map[string]string{"mail_id": "m4"})
body = w.Body.String()
if !strings.Contains(body, `"mail_id":"m4"`) {
t.Error("在线连接应实时收到 m4实际 body:", body)
}
}
// 序列号全局递增,两条不同事件不同 ID。
func TestEventIDMonotonic(t *testing.T) {
m := &Manager{
clients: make(map[string]*Client),
eventBuffer: make(map[string]*eventRing),
}
a := m.nextEventID()
b := m.nextEventID()
if a == b {
t.Fatalf("两个连续事件 ID 相同: %q", a)
}
if a > b {
t.Fatalf("事件 ID 应递增: %q > %q", a, b)
}
}
// 确保事件 ID 写进了 SSE 帧EventSource 靠 id: 行记住位置)
func TestSendWritesIDField(t *testing.T) {
fw := &flushWriter{httptest.NewRecorder(), make(chan bool, 5)}
c := &Client{
ID: "c1",
UserName: "alice",
Res: fw,
Flusher: fw,
done: make(chan struct{}),
}
c.SendWithID("42", "new_mail", map[string]string{"x": "y"})
body := fw.Body.String()
if !strings.Contains(body, "id: 42\n") {
t.Error("帧里应有 id: 42 行,实际:", body)
}
if !strings.Contains(body, "event: new_mail") {
t.Error("帧里应有 event: new_mail实际:", body)
}
if !strings.Contains(body, `data: {"x":"y"}`) {
t.Error("帧里应有 data实际:", body)
}
}