Files
HomeAgent/internal/plugins/webui/handler_sse_test.go
JianFeeeee 5163ce51a7 feat: SSE Last-Event-ID 断线重放 + GUI 表单防刷新
- webui handler: 新增 sseEventRing 环状缓冲区(200条),断线重连按 Last-Event-ID 重放遗漏事件
- GUI app.js: doRenderAll 检测连接表单打开时改走 refreshDataOnly,修复 15s 定时器擦掉用户输入的 bug
- 附带 dashboard.html/index.html 前端调整 + handler_sse_test.go 单测
2026-08-24 16:22:28 +08:00

97 lines
2.2 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 webui
import (
"encoding/json"
"reflect"
"testing"
)
func TestSSEEventRing_Append_Capacity(t *testing.T) {
r := newSSEEventRing(3)
data := json.RawMessage(`{"a":1}`)
for i := 0; i < 5; i++ {
r.Append("id-"+string(rune('0'+i)), "agent_output", data)
}
// cap=3只保留 id-2,id-3,id-4
if len(r.buf) != 3 {
t.Fatalf("expected 3, got %d", len(r.buf))
}
if r.buf[0].id != "id-2" {
t.Fatalf("expected id-2, got %s", r.buf[0].id)
}
}
func TestSSEEventRing_After_Mid(t *testing.T) {
r := newSSEEventRing(10)
data := json.RawMessage(`{"x":"y"}`)
r.Append("id-1", "reasoning", data)
r.Append("id-2", "agent_output", data)
r.Append("id-3", "tool_call", data)
after := r.After("id-2")
if len(after) != 1 || after[0].id != "id-3" {
t.Fatalf("expected [id-3], got %v", after)
}
after = r.After("id-3")
if len(after) != 0 {
t.Fatalf("expected 0, got %d", len(after))
}
}
func TestSSEEventRing_After_NotFound_ReturnsAll(t *testing.T) {
r := newSSEEventRing(3)
data := json.RawMessage(`{}`)
r.Append("id-1", "agent_output", data)
r.Append("id-2", "agent_output", data)
after := r.After("id-0")
if len(after) != 2 {
t.Fatalf("expected 2 (fallback to all), got %d", len(after))
}
}
func TestSSEEventRing_After_Last_Tip(t *testing.T) {
r := newSSEEventRing(5)
data := json.RawMessage(`{"a":1}`)
for i := 0; i < 5; i++ {
r.Append("id-"+string(rune('0'+i)), "agent_output", data)
}
after := r.After("id-4")
if len(after) != 0 {
t.Fatalf("expected 0 after tip, got %d", len(after))
}
}
func TestSSEEventRing_Concurrent(t *testing.T) {
r := newSSEEventRing(100)
done := make(chan struct{})
for i := 0; i < 8; i++ {
go func(n int) {
defer func() { done <- struct{}{} }()
data := json.RawMessage(`{}`)
for j := 0; j < 100; j++ {
r.Append("w"+string(rune('0'+n))+"/"+string(rune('0'+j)), "agent_output", data)
}
}(i)
}
for i := 0; i < 8; i++ {
go func() {
defer func() { done <- struct{}{} }()
for j := 0; j < 100; j++ {
_ = r.After("w0/0")
}
}()
}
for i := 0; i < 16; i++ {
<-done
}
}
func TestSSEEventRecord_Type(t *testing.T) {
var got interface{} = sseEventRecord{}
if reflect.TypeOf(got).Kind() != reflect.Struct {
t.Fatal("sseEventRecord should be a struct")
}
}