From fc3dc39b824e8e7d10a18d37c9973eedac1a42e2 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:49:45 +0800 Subject: [PATCH] fix(test): update tests for event bus + room-based Hub signatures - integration_test: pass events.Bus to NewRouter - handler_test: pass events.Bus to handler.New - hub_test: add mock MembershipChecker, JWT token generation, replace hub.clients with totalClients() helper for room-based Hub Co-Authored-By: Claude Opus 4.6 (1M context) --- server/cmd/server/integration_test.go | 4 +- server/internal/handler/handler_test.go | 4 +- server/internal/realtime/hub_test.go | 59 ++++++++++++++++++------- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/server/cmd/server/integration_test.go b/server/cmd/server/integration_test.go index 1b6e07fc..486dcee7 100644 --- a/server/cmd/server/integration_test.go +++ b/server/cmd/server/integration_test.go @@ -17,6 +17,7 @@ import ( "github.com/gorilla/websocket" "github.com/jackc/pgx/v5/pgxpool" + "github.com/multica-ai/multica/server/internal/events" "github.com/multica-ai/multica/server/internal/realtime" ) @@ -60,7 +61,8 @@ func TestMain(m *testing.M) { hub := realtime.NewHub() go hub.Run() - router := NewRouter(pool, hub) + bus := events.NewBus() + router := NewRouter(pool, hub, bus) testServer = httptest.NewServer(router) // Login to get a real JWT token diff --git a/server/internal/handler/handler_test.go b/server/internal/handler/handler_test.go index cb1f0bb6..1c7be740 100644 --- a/server/internal/handler/handler_test.go +++ b/server/internal/handler/handler_test.go @@ -13,6 +13,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/jackc/pgx/v5/pgxpool" + "github.com/multica-ai/multica/server/internal/events" "github.com/multica-ai/multica/server/internal/realtime" db "github.com/multica-ai/multica/server/pkg/db/generated" ) @@ -44,7 +45,8 @@ func TestMain(m *testing.M) { queries := db.New(pool) hub := realtime.NewHub() go hub.Run() - testHandler = New(queries, pool, hub) + bus := events.NewBus() + testHandler = New(queries, pool, hub, bus) testPool = pool testUserID, testWorkspaceID, err = setupHandlerTestFixture(ctx, pool) diff --git a/server/internal/realtime/hub_test.go b/server/internal/realtime/hub_test.go index 3a7f8dc5..2a8df786 100644 --- a/server/internal/realtime/hub_test.go +++ b/server/internal/realtime/hub_test.go @@ -1,23 +1,49 @@ package realtime import ( + "context" "net/http" "net/http/httptest" "strings" "testing" "time" + "github.com/golang-jwt/jwt/v5" "github.com/gorilla/websocket" + "github.com/multica-ai/multica/server/internal/auth" ) +const testWorkspaceID = "test-workspace" +const testUserID = "test-user" + +// mockMembershipChecker always returns true. +type mockMembershipChecker struct{} + +func (m *mockMembershipChecker) IsMember(_ context.Context, _, _ string) bool { + return true +} + +func makeTestToken(t *testing.T) string { + t.Helper() + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": testUserID, + }) + signed, err := token.SignedString(auth.JWTSecret()) + if err != nil { + t.Fatalf("failed to sign test JWT: %v", err) + } + return signed +} + func newTestHub(t *testing.T) (*Hub, *httptest.Server) { t.Helper() hub := NewHub() go hub.Run() + mc := &mockMembershipChecker{} mux := http.NewServeMux() mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { - HandleWebSocket(hub, w, r) + HandleWebSocket(hub, mc, w, r) }) server := httptest.NewServer(mux) return hub, server @@ -25,7 +51,8 @@ func newTestHub(t *testing.T) (*Hub, *httptest.Server) { func connectWS(t *testing.T, server *httptest.Server) *websocket.Conn { t.Helper() - wsURL := "ws" + strings.TrimPrefix(server.URL, "http") + "/ws" + token := makeTestToken(t) + wsURL := "ws" + strings.TrimPrefix(server.URL, "http") + "/ws?token=" + token + "&workspace_id=" + testWorkspaceID conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) if err != nil { t.Fatalf("failed to connect WebSocket: %v", err) @@ -33,6 +60,17 @@ func connectWS(t *testing.T, server *httptest.Server) *websocket.Conn { return conn } +// totalClients counts all clients across all rooms. +func totalClients(hub *Hub) int { + hub.mu.RLock() + defer hub.mu.RUnlock() + count := 0 + for _, clients := range hub.rooms { + count += len(clients) + } + return count +} + func TestHub_ClientRegistration(t *testing.T) { hub, server := newTestHub(t) defer server.Close() @@ -42,10 +80,7 @@ func TestHub_ClientRegistration(t *testing.T) { time.Sleep(50 * time.Millisecond) - hub.mu.RLock() - count := len(hub.clients) - hub.mu.RUnlock() - + count := totalClients(hub) if count != 1 { t.Fatalf("expected 1 client, got %d", count) } @@ -92,9 +127,7 @@ func TestHub_ClientDisconnect(t *testing.T) { time.Sleep(50 * time.Millisecond) - hub.mu.RLock() - countBefore := len(hub.clients) - hub.mu.RUnlock() + countBefore := totalClients(hub) if countBefore != 1 { t.Fatalf("expected 1 client before disconnect, got %d", countBefore) } @@ -102,9 +135,7 @@ func TestHub_ClientDisconnect(t *testing.T) { conn.Close() time.Sleep(100 * time.Millisecond) - hub.mu.RLock() - countAfter := len(hub.clients) - hub.mu.RUnlock() + countAfter := totalClients(hub) if countAfter != 0 { t.Fatalf("expected 0 clients after disconnect, got %d", countAfter) } @@ -123,9 +154,7 @@ func TestHub_BroadcastToMultipleClients(t *testing.T) { time.Sleep(50 * time.Millisecond) - hub.mu.RLock() - count := len(hub.clients) - hub.mu.RUnlock() + count := totalClients(hub) if count != numClients { t.Fatalf("expected %d clients, got %d", numClients, count) }