package chat import ( "context" "fmt" "sync" "testing" "time" ) // EntityWithGetID helper type for testing type EntityWithGetID struct { id int32 } func (e *EntityWithGetID) GetID() int32 { return e.id } // Mock implementations for testing type MockChannelDatabase struct { channels map[string]ChatChannelData mu sync.RWMutex loadErr error saveErr error delErr error } func NewMockChannelDatabase() *MockChannelDatabase { return &MockChannelDatabase{ channels: make(map[string]ChatChannelData), } } func (m *MockChannelDatabase) LoadWorldChannels(ctx context.Context) ([]ChatChannelData, error) { if m.loadErr != nil { return nil, m.loadErr } m.mu.RLock() defer m.mu.RUnlock() var channels []ChatChannelData for _, channel := range m.channels { channels = append(channels, channel) } return channels, nil } func (m *MockChannelDatabase) SaveChannel(ctx context.Context, channel ChatChannelData) error { if m.saveErr != nil { return m.saveErr } m.mu.Lock() defer m.mu.Unlock() m.channels[channel.Name] = channel return nil } func (m *MockChannelDatabase) DeleteChannel(ctx context.Context, channelName string) error { if m.delErr != nil { return m.delErr } m.mu.Lock() defer m.mu.Unlock() if _, exists := m.channels[channelName]; !exists { return fmt.Errorf("channel not found") } delete(m.channels, channelName) return nil } func (m *MockChannelDatabase) SetLoadError(err error) { m.loadErr = err } func (m *MockChannelDatabase) SetSaveError(err error) { m.saveErr = err } func (m *MockChannelDatabase) SetDeleteError(err error) { m.delErr = err } type MockClientManager struct { sentMessages []ChannelMessage sentLists map[int32][]ChannelInfo sentUpdates []ChatUpdate sentUserLists map[int32][]ChannelMember connectedClients map[int32]bool mu sync.RWMutex } type ChatUpdate struct { CharacterID int32 ChannelName string Action int CharacterName string } func NewMockClientManager() *MockClientManager { return &MockClientManager{ sentLists: make(map[int32][]ChannelInfo), sentUserLists: make(map[int32][]ChannelMember), connectedClients: make(map[int32]bool), } } func (m *MockClientManager) SendChannelList(characterID int32, channels []ChannelInfo) error { m.mu.Lock() defer m.mu.Unlock() m.sentLists[characterID] = channels return nil } func (m *MockClientManager) SendChannelMessage(characterID int32, message ChannelMessage) error { m.mu.Lock() defer m.mu.Unlock() m.sentMessages = append(m.sentMessages, message) return nil } func (m *MockClientManager) SendChannelUpdate(characterID int32, channelName string, action int, characterName string) error { m.mu.Lock() defer m.mu.Unlock() m.sentUpdates = append(m.sentUpdates, ChatUpdate{ CharacterID: characterID, ChannelName: channelName, Action: action, CharacterName: characterName, }) return nil } func (m *MockClientManager) SendChannelUserList(characterID int32, channelName string, members []ChannelMember) error { m.mu.Lock() defer m.mu.Unlock() m.sentUserLists[characterID] = members return nil } func (m *MockClientManager) IsClientConnected(characterID int32) bool { m.mu.RLock() defer m.mu.RUnlock() return m.connectedClients[characterID] } func (m *MockClientManager) SetClientConnected(characterID int32, connected bool) { m.mu.Lock() defer m.mu.Unlock() m.connectedClients[characterID] = connected } func (m *MockClientManager) GetSentMessages() []ChannelMessage { m.mu.RLock() defer m.mu.RUnlock() return append([]ChannelMessage{}, m.sentMessages...) } type MockPlayerManager struct { players map[int32]PlayerInfo mu sync.RWMutex } func NewMockPlayerManager() *MockPlayerManager { return &MockPlayerManager{ players: make(map[int32]PlayerInfo), } } func (m *MockPlayerManager) GetPlayerInfo(characterID int32) (PlayerInfo, error) { m.mu.RLock() defer m.mu.RUnlock() if player, exists := m.players[characterID]; exists { return player, nil } return PlayerInfo{}, fmt.Errorf("player not found") } func (m *MockPlayerManager) ValidatePlayer(characterID int32, levelReq, raceReq, classReq int32) bool { player, err := m.GetPlayerInfo(characterID) if err != nil { return false } if levelReq > 0 && player.Level < levelReq { return false } if raceReq > 0 && (raceReq&(1< 0 && (classReq&(1<