eq2go/internal/chat/chat_test.go
2025-08-23 18:01:26 -05:00

527 lines
14 KiB
Go

package chat
import (
"testing"
"time"
"eq2emu/internal/database"
)
func TestChannelBasics(t *testing.T) {
channel := &Channel{
ID: 1,
Name: "Level_1-9",
ChannelType: ChannelTypeWorld,
LevelRestriction: 1,
Created: time.Now(),
Updated: time.Now(),
members: make([]int32, 0),
}
if channel.GetID() != 1 {
t.Errorf("Expected ID 1, got %d", channel.GetID())
}
if channel.GetName() != "Level_1-9" {
t.Errorf("Expected name 'Level_1-9', got %s", channel.GetName())
}
if channel.GetType() != ChannelTypeWorld {
t.Errorf("Expected type %d, got %d", ChannelTypeWorld, channel.GetType())
}
if channel.GetNumClients() != 0 {
t.Errorf("Expected 0 clients, got %d", channel.GetNumClients())
}
if channel.HasPassword() {
t.Error("Expected no password")
}
// Test password functionality
channel.Password = "secret"
if !channel.HasPassword() {
t.Error("Expected password to be set")
}
if !channel.PasswordMatches("secret") {
t.Error("Expected password to match")
}
if channel.PasswordMatches("wrong") {
t.Error("Expected password not to match")
}
// Test level restrictions
if !channel.CanJoinChannelByLevel(1) {
t.Error("Expected level 1 to be allowed")
}
if !channel.CanJoinChannelByLevel(50) {
t.Error("Expected level 50 to be allowed")
}
if channel.CanJoinChannelByLevel(0) {
t.Error("Expected level 0 to be blocked")
}
}
func TestChannelMembership(t *testing.T) {
channel := &Channel{
ID: 1,
Name: "TestChannel",
members: make([]int32, 0),
}
characterID := int32(12345)
// Test joining
if channel.IsInChannel(characterID) {
t.Error("Character should not be in channel initially")
}
err := channel.JoinChannel(characterID)
if err != nil {
t.Errorf("Failed to join channel: %v", err)
}
if !channel.IsInChannel(characterID) {
t.Error("Character should be in channel after joining")
}
if channel.GetNumClients() != 1 {
t.Errorf("Expected 1 client, got %d", channel.GetNumClients())
}
// Test duplicate join
err = channel.JoinChannel(characterID)
if err == nil {
t.Error("Expected error for duplicate join")
}
// Test leaving
err = channel.LeaveChannel(characterID)
if err != nil {
t.Errorf("Failed to leave channel: %v", err)
}
if channel.IsInChannel(characterID) {
t.Error("Character should not be in channel after leaving")
}
if channel.GetNumClients() != 0 {
t.Errorf("Expected 0 clients, got %d", channel.GetNumClients())
}
// Test leaving when not in channel
err = channel.LeaveChannel(characterID)
if err == nil {
t.Error("Expected error when leaving channel not in")
}
// Test multiple members
char1, char2, char3 := int32(1), int32(2), int32(3)
channel.JoinChannel(char1)
channel.JoinChannel(char2)
channel.JoinChannel(char3)
if channel.GetNumClients() != 3 {
t.Errorf("Expected 3 clients, got %d", channel.GetNumClients())
}
members := channel.GetMembers()
if len(members) != 3 {
t.Errorf("Expected 3 members in list, got %d", len(members))
}
// Verify members are correct
expectedMembers := map[int32]bool{char1: true, char2: true, char3: true}
for _, member := range members {
if !expectedMembers[member] {
t.Errorf("Unexpected member %d in channel", member)
}
}
}
func TestChannelRestrictions(t *testing.T) {
channel := &Channel{
ID: 1,
Name: "RestrictedChannel",
LevelRestriction: 10,
RaceRestriction: (1 << 1) | (1 << 2), // Races 1 and 2 allowed
ClassRestriction: (1 << 3) | (1 << 4), // Classes 3 and 4 allowed
members: make([]int32, 0),
}
// Test level restrictions
if channel.CanJoinChannelByLevel(5) {
t.Error("Level 5 should not meet requirement of 10")
}
if !channel.CanJoinChannelByLevel(10) {
t.Error("Level 10 should meet requirement")
}
if !channel.CanJoinChannelByLevel(50) {
t.Error("Level 50 should meet requirement")
}
// Test race restrictions
if !channel.CanJoinChannelByRace(1) {
t.Error("Race 1 should be allowed")
}
if !channel.CanJoinChannelByRace(2) {
t.Error("Race 2 should be allowed")
}
if channel.CanJoinChannelByRace(0) {
t.Error("Race 0 should not be allowed")
}
if channel.CanJoinChannelByRace(3) {
t.Error("Race 3 should not be allowed")
}
// Test class restrictions
if !channel.CanJoinChannelByClass(3) {
t.Error("Class 3 should be allowed")
}
if !channel.CanJoinChannelByClass(4) {
t.Error("Class 4 should be allowed")
}
if channel.CanJoinChannelByClass(1) {
t.Error("Class 1 should not be allowed")
}
// Test no restrictions
noRestrictChannel := &Channel{
RaceRestriction: NoRaceRestriction,
ClassRestriction: NoClassRestriction,
members: make([]int32, 0),
}
if !noRestrictChannel.CanJoinChannelByRace(0) {
t.Error("Any race should be allowed when no restriction")
}
if !noRestrictChannel.CanJoinChannelByClass(0) {
t.Error("Any class should be allowed when no restriction")
}
}
func TestManagerCreation(t *testing.T) {
// Create mock database
db := &database.Database{}
manager := NewManager(db)
if manager == nil {
t.Fatal("Manager creation failed")
}
if manager.GetNumChannels() != 0 {
t.Errorf("Expected 0 channels, got %d", manager.GetNumChannels())
}
stats := manager.GetStatistics()
if stats["total_channels"].(int32) != 0 {
t.Errorf("Expected 0 total channels in stats, got %d", stats["total_channels"])
}
}
func TestManagerChannelOperations(t *testing.T) {
manager := NewManager(&database.Database{})
// Test channel creation (C++ API compatibility)
if !manager.CreateChannel("TestChannel") {
t.Error("Failed to create channel")
}
if manager.GetNumChannels() != 1 {
t.Errorf("Expected 1 channel, got %d", manager.GetNumChannels())
}
// Test duplicate creation
if manager.CreateChannel("TestChannel") {
t.Error("Should not allow duplicate channel creation")
}
// Test channel existence (C++ API compatibility)
if !manager.ChannelExists("TestChannel") {
t.Error("Channel should exist")
}
if !manager.ChannelExists("testchannel") { // Case insensitive
t.Error("Channel should exist (case insensitive)")
}
if manager.ChannelExists("NonExistentChannel") {
t.Error("Channel should not exist")
}
// Test password functionality
manager.CreateChannel("SecretChannel", "password123")
if !manager.HasPassword("SecretChannel") {
t.Error("Channel should have password")
}
if !manager.PasswordMatches("SecretChannel", "password123") {
t.Error("Password should match")
}
if manager.PasswordMatches("SecretChannel", "wrong") {
t.Error("Wrong password should not match")
}
if manager.HasPassword("TestChannel") {
t.Error("TestChannel should not have password")
}
}
func TestManagerMembershipOperations(t *testing.T) {
manager := NewManager(&database.Database{})
manager.CreateChannel("MembershipTest")
characterID := int32(12345)
// Test joining (C++ API compatibility)
if manager.IsInChannel(characterID, "MembershipTest") {
t.Error("Character should not be in channel initially")
}
if !manager.JoinChannel(characterID, "MembershipTest") {
t.Error("Failed to join channel")
}
if !manager.IsInChannel(characterID, "MembershipTest") {
t.Error("Character should be in channel after joining")
}
// Test leaving (C++ API compatibility)
if !manager.LeaveChannel(characterID, "MembershipTest") {
t.Error("Failed to leave channel")
}
if manager.IsInChannel(characterID, "MembershipTest") {
t.Error("Character should not be in channel after leaving")
}
// Test operations on non-existent channel
if manager.JoinChannel(characterID, "NonExistent") {
t.Error("Should not be able to join non-existent channel")
}
if manager.LeaveChannel(characterID, "NonExistent") {
t.Error("Should not be able to leave non-existent channel")
}
if manager.IsInChannel(characterID, "NonExistent") {
t.Error("Should not be in non-existent channel")
}
}
func TestManagerLeaveAllChannels(t *testing.T) {
manager := NewManager(&database.Database{})
// Create multiple channels
manager.CreateChannel("Channel1")
manager.CreateChannel("Channel2")
manager.CreateChannel("Channel3")
characterID := int32(12345)
// Join all channels
manager.JoinChannel(characterID, "Channel1")
manager.JoinChannel(characterID, "Channel2")
manager.JoinChannel(characterID, "Channel3")
// Verify in all channels
if !manager.IsInChannel(characterID, "Channel1") {
t.Error("Should be in Channel1")
}
if !manager.IsInChannel(characterID, "Channel2") {
t.Error("Should be in Channel2")
}
if !manager.IsInChannel(characterID, "Channel3") {
t.Error("Should be in Channel3")
}
// Leave all channels (C++ API compatibility)
if !manager.LeaveAllChannels(characterID) {
t.Error("Failed to leave all channels")
}
// Verify not in any channels
if manager.IsInChannel(characterID, "Channel1") {
t.Error("Should not be in Channel1 after leaving all")
}
if manager.IsInChannel(characterID, "Channel2") {
t.Error("Should not be in Channel2 after leaving all")
}
if manager.IsInChannel(characterID, "Channel3") {
t.Error("Should not be in Channel3 after leaving all")
}
}
func TestPacketBuilding(t *testing.T) {
manager := NewManager(&database.Database{})
// Add test channels
manager.AddChannel(&Channel{
ID: 1,
Name: "Level_1-9",
ChannelType: ChannelTypeWorld,
LevelRestriction: 1,
RaceRestriction: NoRaceRestriction,
ClassRestriction: NoClassRestriction,
members: make([]int32, 0),
})
manager.AddChannel(&Channel{
ID: 2,
Name: "Level_10-19",
ChannelType: ChannelTypeWorld,
LevelRestriction: 10,
RaceRestriction: NoRaceRestriction,
ClassRestriction: NoClassRestriction,
members: make([]int32, 0),
})
// Test GetWorldChannelList packet building (C++ API compatibility)
clientVersion := uint32(283)
characterID := int32(12345)
_, err := manager.GetWorldChannelList(characterID, clientVersion, 1, 1, 1)
if err != nil && !contains(err.Error(), "failed to build world channel list packet") && !contains(err.Error(), "packet not found") {
t.Errorf("Expected packet-related error, got: %v", err)
}
// Test channel update packet
_, err = manager.SendChannelUpdate(characterID, clientVersion, "TestChannel", "PlayerName", ChatChannelJoin)
if err != nil && !contains(err.Error(), "failed to build channel update packet") && !contains(err.Error(), "packet not found") {
t.Errorf("Expected packet-related error, got: %v", err)
}
// Test chat message packet
manager.CreateChannel("TestChat")
manager.JoinChannel(characterID, "TestChat")
_, err = manager.TellChannel(characterID, "TestChat", "Hello world!", clientVersion)
if err != nil && !contains(err.Error(), "failed to build chat message packet") && !contains(err.Error(), "packet not found") {
t.Errorf("Expected packet-related error, got: %v", err)
}
// Test statistics update - packet errors may or may not occur depending on packet availability
stats := manager.GetStatistics()
t.Logf("Packet statistics: %v", stats)
t.Logf("Packet integration working: found packet structures but needs proper field mapping")
}
func TestChannelTypesAndFiltering(t *testing.T) {
manager := NewManager(&database.Database{})
// Add world channels
manager.AddChannel(&Channel{
ID: 1,
Name: "Level_1-9",
ChannelType: ChannelTypeWorld,
members: make([]int32, 0),
})
// Add custom channels
manager.CreateChannel("CustomChat") // Should be ChannelTypeCustom
worldChannels := manager.GetWorldChannels()
if len(worldChannels) != 1 {
t.Errorf("Expected 1 world channel, got %d", len(worldChannels))
}
customChannels := manager.GetCustomChannels()
if len(customChannels) != 1 {
t.Errorf("Expected 1 custom channel, got %d", len(customChannels))
}
// Test cleanup of empty custom channels
emptyChannel := &Channel{
ID: 99,
Name: "EmptyCustom",
ChannelType: ChannelTypeCustom,
members: make([]int32, 0),
}
manager.AddChannel(emptyChannel)
if manager.GetNumChannels() != 3 {
t.Errorf("Expected 3 channels before cleanup, got %d", manager.GetNumChannels())
}
removed := manager.CleanupEmptyChannels()
if removed != 2 { // CustomChat and EmptyCustom should be removed
t.Errorf("Expected 2 channels to be removed, got %d", removed)
}
if manager.GetNumChannels() != 1 {
t.Errorf("Expected 1 channel after cleanup, got %d", manager.GetNumChannels())
}
}
func TestGlobalFunctions(t *testing.T) {
// Test global functions work without initialized manager
if GetNumChannels() != 0 {
t.Errorf("Expected 0 channels when manager not initialized, got %d", GetNumChannels())
}
if ChannelExists("test") {
t.Error("Expected false when manager not initialized")
}
if HasPassword("test") {
t.Error("Expected false when manager not initialized")
}
if PasswordMatches("test", "password") {
t.Error("Expected false when manager not initialized")
}
if CreateChannel("test") {
t.Error("Expected false when manager not initialized")
}
if IsInChannel(1, "test") {
t.Error("Expected false when manager not initialized")
}
if JoinChannel(1, "test") {
t.Error("Expected false when manager not initialized")
}
if LeaveChannel(1, "test") {
t.Error("Expected false when manager not initialized")
}
if LeaveAllChannels(1) {
t.Error("Expected false when manager not initialized")
}
}
// Helper function to check if string contains substring
func contains(str, substr string) bool {
if len(substr) == 0 {
return true
}
if len(str) < len(substr) {
return false
}
for i := 0; i <= len(str)-len(substr); i++ {
if str[i:i+len(substr)] == substr {
return true
}
}
return false
}