package qq import ( "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" "time" "github.com/gorilla/websocket" "git.yeij.top/AskaEth/Cyrene_v2/services/platform-bridge" ) func wsServer(t *testing.T, onConn func(*websocket.Conn, int)) *httptest.Server { t.Helper() var upgrader = websocket.Upgrader{} var count atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } n := int(count.Add(1)) onConn(conn, n) })) t.Cleanup(srv.Close) return srv } func wsURL(s *httptest.Server) string { return "ws" + strings.TrimPrefix(s.URL, "http") } func TestParseMessageMissingSender(t *testing.T) { a := New(Config{Name: "test", BotQQ: "123"}) // Should not panic on malformed events if msg := a.parseMessage(map[string]any{"post_type": "message", "message_type": "private"}); msg != nil { t.Error("expected nil for event without sender") } if msg := a.parseMessage(map[string]any{"post_type": "notice"}); msg != nil { t.Error("expected nil for non-message post_type") } } func TestReconnectAfterDrop(t *testing.T) { srv := wsServer(t, func(conn *websocket.Conn, n int) { if n == 1 { conn.Close() // drop immediately to trigger reconnect return } time.Sleep(2 * time.Second) // hold the second connection conn.Close() }) a := New(Config{Name: "test", WSURL: wsURL(srv)}) if err := a.Start(); err != nil { t.Fatalf("Start: %v", err) } defer a.Stop() // Wait for the second connection (first is dropped immediately) deadline := time.Now().Add(5 * time.Second) for time.Now().Before(deadline) { if a.IsConnected() { return // reconnected successfully } time.Sleep(50 * time.Millisecond) } t.Fatal("adapter did not reconnect after connection drop") } func TestSendRateLimit(t *testing.T) { received := make(chan struct{}, 4) srv := wsServer(t, func(conn *websocket.Conn, n int) { go func() { for { if _, _, err := conn.ReadMessage(); err != nil { return } received <- struct{}{} } }() time.Sleep(3 * time.Second) conn.Close() }) a := New(Config{Name: "test", WSURL: wsURL(srv)}) a.Start() defer a.Stop() deadline := time.Now().Add(3 * time.Second) for !a.IsConnected() && time.Now().Before(deadline) { time.Sleep(20 * time.Millisecond) } if !a.IsConnected() { t.Fatal("not connected") } <-received // auth message start := time.Now() if err := a.Send("10001", "第一条", bridge.SendOptions{MinGap: 300 * time.Millisecond}); err != nil { t.Fatalf("send 1: %v", err) } if err := a.Send("10001", "第二条", bridge.SendOptions{MinGap: 300 * time.Millisecond}); err != nil { t.Fatalf("send 2: %v", err) } if elapsed := time.Since(start); elapsed < 300*time.Millisecond { t.Errorf("rate limit not enforced: two sends took %v", elapsed) } } func TestSendWhenDisconnected(t *testing.T) { a := New(Config{Name: "test", WSURL: "ws://127.0.0.1:1/unreachable"}) if err := a.Send("10001", "hi", bridge.SendOptions{}); err == nil { t.Error("expected error when not connected") } }