package sync import ( "testing" "github.com/google/uuid" ) // TestBroadcastIsPerUser pins the 2026-11-02 chokepoint: sync events are // private to the acting user. The pre-fix helpers fanned out through the // untargeted Broadcast, delivering one user's book progress — and // annotation payloads, which can carry note text — to every connected // client on the instance. The untargeted method no longer exists (the // compiler enforces an audience on every emitter); this test enforces the // routing at runtime, in the spirit of TestRlsInvariant: no event may // cross a user boundary. func TestBroadcastIsPerUser(t *testing.T) { cm := NewConnectionManager() mkConn := func(user string) *DeviceConnection { conn := &DeviceConnection{ ID: uuid.NewString(), UserID: user, DeviceType: "test", DeviceName: user + "-device", Send: make(chan BroadcastMessage, 8), } cm.AddConnection(conn) return conn } // Alice has two devices connected; Bob has one. a1 := mkConn("alice") a2 := mkConn("alice") b1 := mkConn("bob") source := SourceDevice{ID: "dev-1", Name: "alice-kobo", Type: "kobo"} cm.BroadcastProgressUpdate("alice", uuid.New(), 42.5, source) cm.BroadcastAnnotationUpdate("alice", uuid.New(), "highlight", map[string]any{"highlight_id": "h1"}, source) cm.BroadcastConflictNotification("alice", uuid.New(), "detection", "conflict-1") drain := func(conn *DeviceConnection) []BroadcastMessage { var got []BroadcastMessage for { select { case msg := <-conn.Send: got = append(got, msg) default: return got } } } // Alice's devices each received all three of her events... for _, conn := range []*DeviceConnection{a1, a2} { got := drain(conn) if len(got) != 3 { t.Fatalf("%s got %d messages, want 3", conn.DeviceName, len(got)) } for _, msg := range got { // Conflict events are server-detected and carry no source; // progress and annotation events identify the originating // device so clients can ignore their own echo. if msg.Type != "conflict" && (msg.Source == nil || msg.Source.Name != "alice-kobo") { t.Errorf("%s: unexpected event source on %q: %+v", conn.DeviceName, msg.Type, msg.Source) } } } // ...and Bob received nothing. Any message here is a cross-user leak. if leaked := drain(b1); len(leaked) != 0 { t.Fatalf("bob's client received %d message(s) from alice's sync activity — cross-user leak", len(leaked)) } }