package ws import ( "testing" "time" ) func TestDocRoomCursorPresenceAndEchoProtection(t *testing.T) { hub := NewHub() room := hub.GetOrCreateDocRoom("doc_test_presence") c1 := &DocWSClient{ hub: hub, room: room, userID: "user_1", clientID: "cli_1", canWrite: true, send: make(chan []byte, 10), } c2 := &DocWSClient{ hub: hub, room: room, userID: "user_2", clientID: "cli_2", canWrite: false, // read-only viewer send: make(chan []byte, 10), } room.mu.Lock() room.wsClients[c1] = true room.wsClients[c2] = true room.mu.Unlock() // 1. Send cursor_moved from c1 selStart := 5 selEnd := 5 room.HandleEvent(DocSyncEvent{ Type: "cursor_moved", DocID: "doc_test_presence", ClientID: "cli_1", UserID: "user_1", UserName: "Alice", UserColor: "#3b82f6", BlockID: "blk_123", SelectionStart: &selStart, SelectionEnd: &selEnd, }, c1) // c1 should NOT have received its own echo select { case <-c1.send: t.Fatal("c1 received echo of its own cursor_moved event!") default: } // c2 SHOULD receive the event select { case msg := <-c2.send: if len(msg) == 0 { t.Fatal("empty message received by c2") } case <-time.After(500 * time.Millisecond): t.Fatal("timed out waiting for cursor_moved event on c2") } // Room should have registered c1's presence room.mu.Lock() cp, exists := room.activeCursors["cli_1"] room.mu.Unlock() if !exists || cp == nil { t.Fatal("expected active cursor for cli_1 in room") } if cp.UserName != "Alice" || cp.BlockID != "blk_123" { t.Fatalf("unexpected cursor data: %+v", cp) } // Ephemeral cursor events must NOT mark the room dirty for disk flush room.mu.Lock() dirty := room.dirty room.mu.Unlock() if dirty { t.Fatal("cursor_moved should not mark room dirty") } }