package services import ( "sync" "testing" ) // Run with -race: close may be triggered independently by revocation, a reader, // a writer, or slow-client eviction while broadcasts still hold a session pointer. func TestWsConcurrentEnqueueCloseAndSubscribe(t *testing.T) { ws := newWsService() session := &ClientSession{ID: "concurrent", Topics: map[string]struct{}{}, Send: make(chan []byte, 64)} ws.manager.Register(session, []string{"notification:1"}) var work sync.WaitGroup for i := 0; i < 8; i++ { work.Add(1) go func() { defer work.Done() for j := 0; j < 100; j++ { session.enqueue([]byte("test")) ws.manager.Subscribe(session, []string{"notification:1"}) ws.manager.TopicList(session) } }() } ws.closeSession(session) work.Wait() if !session.Closed.Load() || ws.manager.HasTopic("notification:1") || len(ws.manager.TopicList(session)) != 0 { t.Fatal("closed session was re-registered") } if session.enqueue([]byte("test")) { t.Fatal("closed session accepted a message") } }