fix(sync): scan events keep instance-wide fan-out as the sanctioned BroadcastToAll
Verifying the per-user chokepoint end to end surfaced a UI regression it would have caused: web/src/header.ts runs the sitewide scan indicator on scan_progress/scan_complete events in every browser, and routing those to the triggering identity alone froze the indicator for everyone else at its page-load state (active-summary stays the load-time fallback, but live movement died). The source's own comment declares the indicator sitewide by design, and scan payloads carry no user data — job ids, file counts, progress percentages only. Resolution honors both truths: BroadcastToAll is re-added as the ONE sanctioned instance-wide fan-out, its doc comment making the decision explicit (no user-derived data; anything carrying books, notes, or annotations belongs in BroadcastToUser), and the two scan emitters use it. TestBroadcastToAllReachesEveryone pins the exception alongside TestBroadcastIsPerUser — audiences are now named decisions the compiler and the tests both enforce.
This commit is contained in:
1 parent
a431dfa10b
commit
dd963993eb
4 files changed
+77
-13
No files matched your search
@@ -68,8 +68,44 @@ func TestBroadcastIsPerUser(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ...and Bob received nothing. Any message here is a cross-user leak.
|
||||
// ...and Bob received nothing user-derived from Alice's sync
|
||||
// activity. Scan events are the one sanctioned instance-wide fan-out
|
||||
// (BroadcastToAll — job ids and counts, no user data); everything
|
||||
// carrying a user's books or annotations stops at that user's edge.
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// TestBroadcastToAllReachesEveryone pins the sanctioned exception: scan
|
||||
// activity is instance-wide (the sitewide header indicator runs on it),
|
||||
// while everything user-derived stays per-user (TestBroadcastIsPerUser).
|
||||
func TestBroadcastToAllReachesEveryone(t *testing.T) {
|
||||
cm := NewConnectionManager()
|
||||
|
||||
var conns []*DeviceConnection
|
||||
for _, user := range []string{"alice", "bob", "carol"} {
|
||||
conn := &DeviceConnection{
|
||||
ID: uuid.NewString(),
|
||||
UserID: user,
|
||||
DeviceType: "test",
|
||||
DeviceName: user + "-device",
|
||||
Send: make(chan BroadcastMessage, 4),
|
||||
}
|
||||
cm.AddConnection(conn)
|
||||
conns = append(conns, conn)
|
||||
}
|
||||
|
||||
cm.BroadcastToAll(BroadcastMessage{Type: MessageTypeScanProgress})
|
||||
|
||||
for _, conn := range conns {
|
||||
select {
|
||||
case msg := <-conn.Send:
|
||||
if msg.Type != MessageTypeScanProgress {
|
||||
t.Errorf("%s: got %q, want scan_progress", conn.DeviceName, msg.Type)
|
||||
}
|
||||
default:
|
||||
t.Errorf("%s: received nothing from BroadcastToAll", conn.DeviceName)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user