package pubsub import ( "net" "testing" "time" ) type mockConn struct { data []byte pos int } func (m *mockConn) Read(b []byte) (n int, err error) { if m.pos >= len(m.data) { return 0, nil } n = copy(b, m.data[m.pos:]) m.pos += n return n, nil } func (m *mockConn) Write(b []byte) (n int, err error) { m.data = append(m.data, b...) return len(b), nil } func (m *mockConn) Close() error { return nil } func (m *mockConn) LocalAddr() net.Addr { return nil } func (m *mockConn) RemoteAddr() net.Addr { return nil } func (m *mockConn) SetDeadline(time.Time) error { return nil } func (m *mockConn) SetReadDeadline(time.Time) error { return nil } func (m *mockConn) SetWriteDeadline(time.Time) error { return nil } func NewMockClient() *Client { return NewClient(&mockConn{data: []byte{}}) } func TestNewHub(t *testing.T) { hub := NewHub() if hub == nil { t.Fatal("NewHub() returned nil") } if hub.subs == nil { t.Error("hub.subs is nil") } } func TestHubSubscribe(t *testing.T) { hub := NewHub() client := NewMockClient() // Subscribe to a channel ok := hub.Subscribe(client, "news") if !ok { t.Error("Subscribe() returned false for new subscription") } // Check subscriber count count := hub.ChannelCount("news") if count != 1 { t.Errorf("ChannelCount() = %d, want 1", count) } // Duplicate subscription should return false ok = hub.Subscribe(client, "news") if ok { t.Error("Subscribe() should return false for duplicate subscription") } } func TestHubUnsubscribe(t *testing.T) { hub := NewHub() client := NewMockClient() hub.Subscribe(client, "news") ok := hub.Unsubscribe(client, "news") if !ok { t.Error("Unsubscribe() returned false") } count := hub.ChannelCount("news") if count != 0 { t.Errorf("ChannelCount() = %d, want 0", count) } } func TestHubUnsubscribeNotSubscribed(t *testing.T) { hub := NewHub() client := NewMockClient() ok := hub.Unsubscribe(client, "nonexistent") if ok { t.Error("Unsubscribe() should return false for non-subscribed channel") } } func TestHubUnsubscribeAll(t *testing.T) { hub := NewHub() client := NewMockClient() hub.Subscribe(client, "news") hub.Subscribe(client, "sports") hub.Subscribe(client, "weather") hub.UnsubscribeAll(client) if hub.ChannelCount("news") != 0 { t.Error("news channel should be empty") } if hub.ChannelCount("sports") != 0 { t.Error("sports channel should be empty") } if hub.ChannelCount("weather") != 0 { t.Error("weather channel should be empty") } } func TestHubPublish(t *testing.T) { hub := NewHub() client1 := NewMockClient() client2 := NewMockClient() hub.Subscribe(client1, "news") hub.Subscribe(client2, "news") count := hub.Publish("news", []byte("Hello")) if count != 2 { t.Errorf("Publish() = %d, want 2", count) } } func TestHubPublishEmptyChannel(t *testing.T) { hub := NewHub() count := hub.Publish("empty", []byte("Hello")) if count != 0 { t.Errorf("Publish() to empty channel = %d, want 0", count) } } func TestHubChannelCount(t *testing.T) { hub := NewHub() client := NewMockClient() if hub.ChannelCount("news") != 0 { t.Error("new channel should have 0 subscribers") } hub.Subscribe(client, "news") if hub.ChannelCount("news") != 1 { t.Errorf("ChannelCount() = %d, want 1", hub.ChannelCount("news")) } hub.Subscribe(client, "news") // duplicate if hub.ChannelCount("news") != 1 { t.Errorf("ChannelCount() after duplicate = %d, want 1", hub.ChannelCount("news")) } } func TestHubTotalChannels(t *testing.T) { hub := NewHub() client := NewMockClient() if hub.TotalChannels() != 0 { t.Errorf("TotalChannels() = %d, want 0", hub.TotalChannels()) } hub.Subscribe(client, "news") hub.Subscribe(client, "sports") if hub.TotalChannels() != 2 { t.Errorf("TotalChannels() = %d, want 2", hub.TotalChannels()) } } func TestHubChannelList(t *testing.T) { hub := NewHub() client := NewMockClient() channels := hub.ChannelList("") if len(channels) != 0 { t.Errorf("Empty hub ChannelList() length = %d, want 0", len(channels)) } hub.Subscribe(client, "news") hub.Subscribe(client, "sports") channels = hub.ChannelList("") if len(channels) != 2 { t.Errorf("ChannelList() length = %d, want 2", len(channels)) } } func TestHubMultipleClients(t *testing.T) { hub := NewHub() client1 := NewMockClient() client2 := NewMockClient() client3 := NewMockClient() hub.Subscribe(client1, "news") hub.Subscribe(client2, "news") hub.Subscribe(client3, "news") count := hub.ChannelCount("news") if count != 3 { t.Errorf("ChannelCount() = %d, want 3", count) } } func TestHubChannelCleanup(t *testing.T) { hub := NewHub() client := NewMockClient() hub.Subscribe(client, "temp") if hub.ChannelCount("temp") != 1 { t.Error("Channel should exist") } hub.Unsubscribe(client, "temp") if hub.ChannelCount("temp") != 0 { t.Error("Channel should be cleaned up") } channels := hub.ChannelList("") for _, ch := range channels { if ch == "temp" { t.Error("Channel should not be in list after cleanup") } } } func TestNewClient(t *testing.T) { conn := &mockConn{data: []byte{}} client := NewClient(conn) if client == nil { t.Fatal("NewClient() returned nil") } if client.subscribed == nil { t.Error("client.subscribed is nil") } } func TestClientAddSubscription(t *testing.T) { client := NewMockClient() client.AddSubscription("news") if client.SubsCount() != 1 { t.Errorf("SubsCount() = %d, want 1", client.SubsCount()) } client.AddSubscription("sports") if client.SubsCount() != 2 { t.Errorf("SubsCount() = %d, want 2", client.SubsCount()) } } func TestClientRemoveSubscription(t *testing.T) { client := NewMockClient() client.AddSubscription("news") client.AddSubscription("sports") client.RemoveSubscription("news") if client.SubsCount() != 1 { t.Errorf("SubsCount() = %d, want 1", client.SubsCount()) } } func TestClientGetChannels(t *testing.T) { client := NewMockClient() client.AddSubscription("news") client.AddSubscription("sports") channels := client.GetChannels() if len(channels) != 2 { t.Errorf("GetChannels() length = %d, want 2", len(channels)) } found := make(map[string]bool) for _, ch := range channels { found[ch] = true } if !found["news"] || !found["sports"] { t.Error("GetChannels() missing expected channels") } } func TestClientGetChannelsReturnsCopy(t *testing.T) { client := NewMockClient() client.AddSubscription("news") channels := client.GetChannels() channels[0] = "modified" channels2 := client.GetChannels() if channels2[0] == "modified" { t.Error("GetChannels() should return a copy, not modify original") } } func TestClientSubsCount(t *testing.T) { client := NewMockClient() if client.SubsCount() != 0 { t.Errorf("Initial SubsCount() = %d, want 0", client.SubsCount()) } client.AddSubscription("news") if client.SubsCount() != 1 { t.Errorf("SubsCount() = %d, want 1", client.SubsCount()) } } func TestConcurrentSubscribe(t *testing.T) { hub := NewHub() client := NewMockClient() done := make(chan bool) go func() { hub.Subscribe(client, "channel1") done <- true }() hub.Subscribe(client, "channel2") <-done if hub.TotalChannels() != 2 { t.Errorf("TotalChannels() = %d, want 2", hub.TotalChannels()) } }