package main import ( "testing" "time" ) func TestHub_BroadcastReachesSubscriber(t *testing.T) { h := NewHub() ch := h.Subscribe() defer h.Unsubscribe(ch) want := ServerEvent{Board: "issues", CardID: "0119", Action: "updated"} h.Broadcast(want) select { case got := <-ch: if got != want { t.Fatalf("got %+v, want %+v", got, want) } case <-time.After(time.Second): t.Fatal("timeout waiting for broadcast") } } func TestHub_UnsubscribeStopsDelivery(t *testing.T) { h := NewHub() ch := h.Subscribe() if got := h.Count(); got != 1 { t.Fatalf("Count() = %d, want 1", got) } h.Unsubscribe(ch) if got := h.Count(); got != 0 { t.Fatalf("Count() after Unsubscribe = %d, want 0", got) } // channel should be closed if _, ok := <-ch; ok { t.Fatalf("expected closed channel after Unsubscribe") } // double-unsubscribe is a no-op h.Unsubscribe(ch) // broadcast should not panic and should reach nobody h.Broadcast(ServerEvent{Board: "issues", CardID: "x", Action: "updated"}) } func TestHub_MultipleSubscribersAllReceive(t *testing.T) { h := NewHub() const n = 5 chans := make([]chan ServerEvent, n) for i := range chans { chans[i] = h.Subscribe() } defer func() { for _, ch := range chans { h.Unsubscribe(ch) } }() want := ServerEvent{Board: "flows", CardID: "abc", Action: "created", EventType: "card_added"} h.Broadcast(want) for i, ch := range chans { select { case got := <-ch: if got != want { t.Fatalf("sub %d: got %+v, want %+v", i, got, want) } case <-time.After(time.Second): t.Fatalf("sub %d: timeout", i) } } }