package channel import ( "testing" ) func TestCreateChannel(t *testing.T) { tests := []struct { name string chName string ownerID string wantErr bool errType error }{ { name: "create valid channel", chName: "general", ownerID: "user-1", wantErr: false, }, { name: "empty channel name", chName: "", ownerID: "user-1", wantErr: true, errType: ErrInvalidChannelName, }, { name: "channel name too short", chName: "a", ownerID: "user-1", wantErr: true, errType: ErrInvalidChannelName, }, { name: "channel name too long", chName: "channel-with-very-long-name-that-exceeds-fifty-characters-limit", ownerID: "user-1", wantErr: true, errType: ErrInvalidChannelName, }, { name: "duplicate channel name", chName: "general", ownerID: "user-1", wantErr: false, }, } for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset manager for duplicate test var m *Manager if i < 3 || i > 3 { m = NewManager() } ch, err := m.CreateChannel(tt.chName, tt.ownerID) if (err != nil) != tt.wantErr { t.Errorf("CreateChannel() error = %v, wantErr %v", err, tt.wantErr) return } if tt.wantErr && err != tt.errType { t.Errorf("CreateChannel() error = %v, expected %v", err, tt.errType) } if !tt.wantErr { if ch == nil { t.Error("CreateChannel() returned nil channel") return } if ch.Name != tt.chName { t.Errorf("Channel name = %s, want %s", ch.Name, tt.chName) } if ch.OwnerID != tt.ownerID { t.Errorf("Channel owner = %s, want %s", ch.OwnerID, tt.ownerID) } // Verify owner is automatically a member if !ch.IsMember(tt.ownerID) { t.Error("Owner should be automatically added as member") } if ch.Status != StatusActive { t.Errorf("Channel status = %v, want %v", ch.Status, StatusActive) } } }) } } func TestDuplicateChannelName(t *testing.T) { m := NewManager() // Create first channel _, err := m.CreateChannel("general", "user-1") if err != nil { t.Fatalf("CreateChannel() error = %v", err) } // Try to create duplicate _, err = m.CreateChannel("general", "user-2") if err != ErrChannelAlreadyExists { t.Errorf("CreateChannel() duplicate error = %v, expected %v", err, ErrChannelAlreadyExists) } } func TestGetChannel(t *testing.T) { m := NewManager() ch, err := m.CreateChannel("test", "user-1") if err != nil { t.Fatalf("CreateChannel() error = %v", err) } tests := []struct { name string channelID string wantErr bool wantName string }{ { name: "get existing channel", channelID: ch.ID, wantErr: false, wantName: "test", }, { name: "get nonexistent channel", channelID: "nonexistent-id", wantErr: true, wantName: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { retrieved, err := m.GetChannel(tt.channelID) if (err != nil) != tt.wantErr { t.Errorf("GetChannel() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && retrieved.Name != tt.wantName { t.Errorf("GetChannel() name = %s, want %s", retrieved.Name, tt.wantName) } }) } } func TestListChannels(t *testing.T) { m := NewManager() // Create multiple channels m.CreateChannel("general", "user-1") m.CreateChannel("announcements", "user-2") m.CreateChannel("off-topic", "user-1") channels := m.ListChannels() if len(channels) != 3 { t.Errorf("ListChannels() returned %d channels, want 3", len(channels)) } } func TestJoinChannel(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") tests := []struct { name string userID string channelID string wantErr bool }{ { name: "join existing channel", userID: "user-2", channelID: ch.ID, wantErr: false, }, { name: "join nonexistent channel", userID: "user-3", channelID: "nonexistent", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := m.JoinChannel(tt.channelID, tt.userID) if (err != nil) != tt.wantErr { t.Errorf("JoinChannel() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { channel, _ := m.GetChannel(tt.channelID) if !channel.IsMember(tt.userID) { t.Errorf("User %s not in channel after join", tt.userID) } } }) } } func TestLeaveChannel(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") // User joins m.JoinChannel(ch.ID, "user-2") // User leaves err := m.LeaveChannel(ch.ID, "user-2") if err != nil { t.Errorf("LeaveChannel() error = %v", err) return } // Verify user is no longer member channel, _ := m.GetChannel(ch.ID) if channel.IsMember("user-2") { t.Error("User should not be member after leaving") } } func TestChannelCapacity(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") ch.MaxUsers = 2 // User 1 (owner) already member, join user 2 m.JoinChannel(ch.ID, "user-2") // Try to add user 3 (should fail - at capacity) _, err := m.JoinChannel(ch.ID, "user-3") if err != ErrChannelFull { t.Errorf("JoinChannel() capacity error = %v, expected %v", err, ErrChannelFull) } } func TestDeleteChannel(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") // Soft delete err := m.DeleteChannel(ch.ID, false) if err != nil { t.Errorf("DeleteChannel() soft error = %v", err) } // Verify it's archived channel, _ := m.GetChannel(ch.ID) if channel.Status != StatusArchived { t.Errorf("Channel status = %v, want %v", channel.Status, StatusArchived) } // Hard delete err = m.DeleteChannel(ch.ID, true) if err != nil { t.Errorf("DeleteChannel() hard error = %v", err) } // Verify it's gone _, err = m.GetChannel(ch.ID) if err != ErrChannelNotFound { t.Errorf("GetChannel() after hard delete error = %v, expected %v", err, ErrChannelNotFound) } } func TestGetChannelMembers(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") m.JoinChannel(ch.ID, "user-2") m.JoinChannel(ch.ID, "user-3") members, err := m.GetChannelMembers(ch.ID) if err != nil { t.Errorf("GetChannelMembers() error = %v", err) return } if len(members) != 3 { t.Errorf("GetChannelMembers() returned %d members, want 3", len(members)) } } func TestUpdateChannel(t *testing.T) { m := NewManager() tests := []struct { name string newName string description string isPublic bool wantErr bool setup func(*Manager) string }{ { name: "update all fields", newName: "updated", description: "New description", isPublic: false, wantErr: false, setup: func(m *Manager) string { ch, _ := m.CreateChannel("test", "user-1") return ch.ID }, }, { name: "duplicate new name", newName: "other", wantErr: true, setup: func(m *Manager) string { ch, _ := m.CreateChannel("test2", "user-1") m.CreateChannel("other", "user-2") return ch.ID }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { channelID := tt.setup(m) updated, err := m.UpdateChannel(channelID, tt.newName, tt.description, tt.isPublic, 10) if (err != nil) != tt.wantErr { t.Errorf("UpdateChannel() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && updated.Name != tt.newName { t.Errorf("Updated name = %s, want %s", updated.Name, tt.newName) } }) } } func TestIsUserInChannel(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") if !m.IsUserInChannel(ch.ID, "user-1") { t.Error("Owner should be in channel") } if m.IsUserInChannel(ch.ID, "user-2") { t.Error("User-2 should not be in channel yet") } m.JoinChannel(ch.ID, "user-2") if !m.IsUserInChannel(ch.ID, "user-2") { t.Error("User-2 should be in channel after joining") } } func TestGetUserChannels(t *testing.T) { m := NewManager() _, _ = m.CreateChannel("general", "user-1") ch2, _ := m.CreateChannel("announcements", "user-2") _, _ = m.CreateChannel("off-topic", "user-1") m.JoinChannel(ch2.ID, "user-1") userChannels := m.GetUserChannels("user-1") if len(userChannels) != 3 { t.Errorf("GetUserChannels() returned %d channels, want 3", len(userChannels)) } userChannels = m.GetUserChannels("user-2") if len(userChannels) != 1 { t.Errorf("GetUserChannels() returned %d channels for user-2, want 1", len(userChannels)) } } func TestChannelConcurrency(t *testing.T) { m := NewManager() ch, _ := m.CreateChannel("test", "user-1") // Multiple users join concurrently done := make(chan bool, 100) for i := 0; i < 100; i++ { go func(userID string) { m.JoinChannel(ch.ID, userID) done <- true }(("user-" + string(rune(i)))) } // Wait for all goroutines for i := 0; i < 100; i++ { <-done } // Owner + 100 concurrent users members, _ := m.GetChannelMembers(ch.ID) if len(members) < 1 { t.Errorf("Expected at least 1 member, got %d", len(members)) } }