package languages import ( "fmt" "testing" ) // Mock implementations for testing // MockDatabase implements the Database interface for testing type MockDatabase struct { languages map[int32]*Language playerLanguages map[int32][]int32 // playerID -> []languageIDs } func NewMockDatabase() *MockDatabase { return &MockDatabase{ languages: make(map[int32]*Language), playerLanguages: make(map[int32][]int32), } } func (md *MockDatabase) LoadAllLanguages() ([]*Language, error) { var languages []*Language for _, lang := range md.languages { languages = append(languages, lang.Copy()) } return languages, nil } func (md *MockDatabase) SaveLanguage(language *Language) error { if language == nil { return fmt.Errorf("language is nil") } md.languages[language.GetID()] = language.Copy() return nil } func (md *MockDatabase) DeleteLanguage(languageID int32) error { delete(md.languages, languageID) return nil } func (md *MockDatabase) LoadPlayerLanguages(playerID int32) ([]*Language, error) { var languages []*Language if langIDs, exists := md.playerLanguages[playerID]; exists { for _, langID := range langIDs { if lang, exists := md.languages[langID]; exists { languages = append(languages, lang.Copy()) } } } return languages, nil } func (md *MockDatabase) SavePlayerLanguage(playerID int32, languageID int32) error { if _, exists := md.languages[languageID]; !exists { return fmt.Errorf("language %d does not exist", languageID) } if playerLangs, exists := md.playerLanguages[playerID]; exists { // Check if already exists for _, id := range playerLangs { if id == languageID { return nil // Already saved } } md.playerLanguages[playerID] = append(playerLangs, languageID) } else { md.playerLanguages[playerID] = []int32{languageID} } return nil } func (md *MockDatabase) DeletePlayerLanguage(playerID int32, languageID int32) error { if playerLangs, exists := md.playerLanguages[playerID]; exists { for i, id := range playerLangs { if id == languageID { md.playerLanguages[playerID] = append(playerLangs[:i], playerLangs[i+1:]...) return nil } } } return fmt.Errorf("player %d does not know language %d", playerID, languageID) } // MockLogger implements the Logger interface for testing type MockLogger struct { logs []string } func NewMockLogger() *MockLogger { return &MockLogger{ logs: make([]string, 0), } } func (ml *MockLogger) LogInfo(message string, args ...any) { ml.logs = append(ml.logs, fmt.Sprintf("INFO: "+message, args...)) } func (ml *MockLogger) LogError(message string, args ...any) { ml.logs = append(ml.logs, fmt.Sprintf("ERROR: "+message, args...)) } func (ml *MockLogger) LogDebug(message string, args ...any) { ml.logs = append(ml.logs, fmt.Sprintf("DEBUG: "+message, args...)) } func (ml *MockLogger) LogWarning(message string, args ...any) { ml.logs = append(ml.logs, fmt.Sprintf("WARNING: "+message, args...)) } func (ml *MockLogger) GetLogs() []string { return ml.logs } func (ml *MockLogger) Clear() { ml.logs = ml.logs[:0] } // MockPlayer implements the Player interface for testing type MockPlayer struct { id int32 name string } func NewMockPlayer(id int32, name string) *MockPlayer { return &MockPlayer{ id: id, name: name, } } func (mp *MockPlayer) GetID() int32 { return mp.id } func (mp *MockPlayer) GetName() string { return mp.name } func (mp *MockPlayer) IsPlayer() bool { return true } func (mp *MockPlayer) IsNPC() bool { return false } func (mp *MockPlayer) IsBot() bool { return false } func (mp *MockPlayer) GetCharacterID() int32 { return mp.id } func (mp *MockPlayer) SendMessage(message string) { // Mock implementation - could store messages for testing } // Test functions func TestNewLanguage(t *testing.T) { lang := NewLanguage() if lang == nil { t.Fatal("NewLanguage returned nil") } if lang.GetID() != 0 { t.Errorf("Expected ID 0, got %d", lang.GetID()) } if lang.GetName() != "" { t.Errorf("Expected empty name, got %s", lang.GetName()) } if lang.GetSaveNeeded() { t.Error("New language should not need saving") } } func TestLanguageSettersAndGetters(t *testing.T) { lang := NewLanguage() // Test ID lang.SetID(123) if lang.GetID() != 123 { t.Errorf("Expected ID 123, got %d", lang.GetID()) } // Test Name lang.SetName("Test Language") if lang.GetName() != "Test Language" { t.Errorf("Expected name 'Test Language', got %s", lang.GetName()) } // Test SaveNeeded lang.SetSaveNeeded(true) if !lang.GetSaveNeeded() { t.Error("Expected save needed to be true") } } func TestLanguageValidation(t *testing.T) { lang := NewLanguage() // Invalid - no name and ID 0 if lang.IsValid() { t.Error("Empty language should not be valid") } // Set valid data lang.SetID(LanguageIDElvish) lang.SetName("Elvish") if !lang.IsValid() { t.Error("Language with valid ID and name should be valid") } // Test invalid ID lang.SetID(-1) if lang.IsValid() { t.Error("Language with invalid ID should not be valid") } lang.SetID(MaxLanguageID + 1) if lang.IsValid() { t.Error("Language with ID exceeding max should not be valid") } } func TestLanguageCopy(t *testing.T) { original := NewLanguage() original.SetID(42) original.SetName("Original") original.SetSaveNeeded(true) copy := original.Copy() if copy == nil { t.Fatal("Copy returned nil") } if copy.GetID() != original.GetID() { t.Errorf("Expected ID %d, got %d", original.GetID(), copy.GetID()) } if copy.GetName() != original.GetName() { t.Errorf("Expected name %s, got %s", original.GetName(), copy.GetName()) } if copy.GetSaveNeeded() != original.GetSaveNeeded() { t.Errorf("Expected save needed %v, got %v", original.GetSaveNeeded(), copy.GetSaveNeeded()) } // Ensure it's a separate instance copy.SetName("Modified") if original.GetName() == copy.GetName() { t.Error("Copy should be independent of original") } } func TestMasterLanguagesList(t *testing.T) { masterList := NewMasterLanguagesList() // Test initial state if masterList.Size() != 0 { t.Errorf("Expected size 0, got %d", masterList.Size()) } // Add language lang := NewLanguage() lang.SetID(LanguageIDCommon) lang.SetName("Common") err := masterList.AddLanguage(lang) if err != nil { t.Fatalf("Failed to add language: %v", err) } if masterList.Size() != 1 { t.Errorf("Expected size 1, got %d", masterList.Size()) } // Test retrieval retrieved := masterList.GetLanguage(LanguageIDCommon) if retrieved == nil { t.Fatal("GetLanguage returned nil") } if retrieved.GetName() != "Common" { t.Errorf("Expected name 'Common', got %s", retrieved.GetName()) } // Test retrieval by name byName := masterList.GetLanguageByName("Common") if byName == nil { t.Fatal("GetLanguageByName returned nil") } if byName.GetID() != LanguageIDCommon { t.Errorf("Expected ID %d, got %d", LanguageIDCommon, byName.GetID()) } // Test duplicate ID dupLang := NewLanguage() dupLang.SetID(LanguageIDCommon) dupLang.SetName("Duplicate") err = masterList.AddLanguage(dupLang) if err == nil { t.Error("Should not allow duplicate language ID") } // Test duplicate name dupNameLang := NewLanguage() dupNameLang.SetID(LanguageIDElvish) dupNameLang.SetName("Common") err = masterList.AddLanguage(dupNameLang) if err == nil { t.Error("Should not allow duplicate language name") } } func TestPlayerLanguagesList(t *testing.T) { playerList := NewPlayerLanguagesList() // Test initial state if playerList.Size() != 0 { t.Errorf("Expected size 0, got %d", playerList.Size()) } // Add language lang := NewLanguage() lang.SetID(LanguageIDCommon) lang.SetName("Common") err := playerList.Add(lang) if err != nil { t.Fatalf("Failed to add language: %v", err) } if playerList.Size() != 1 { t.Errorf("Expected size 1, got %d", playerList.Size()) } // Test has language if !playerList.HasLanguage(LanguageIDCommon) { t.Error("Player should have Common language") } if playerList.HasLanguage(LanguageIDElvish) { t.Error("Player should not have Elvish language") } // Test removal if !playerList.RemoveLanguage(LanguageIDCommon) { t.Error("Should be able to remove Common language") } if playerList.Size() != 0 { t.Errorf("Expected size 0 after removal, got %d", playerList.Size()) } } func TestManager(t *testing.T) { database := NewMockDatabase() logger := NewMockLogger() // Pre-populate database with some languages commonLang := NewLanguage() commonLang.SetID(LanguageIDCommon) commonLang.SetName("Common") database.SaveLanguage(commonLang) elvishLang := NewLanguage() elvishLang.SetID(LanguageIDElvish) elvishLang.SetName("Elvish") database.SaveLanguage(elvishLang) manager := NewManager(database, logger) // Test initialization err := manager.Initialize() if err != nil { t.Fatalf("Failed to initialize manager: %v", err) } if manager.GetLanguageCount() != 2 { t.Errorf("Expected 2 languages, got %d", manager.GetLanguageCount()) } // Test language retrieval lang := manager.GetLanguage(LanguageIDCommon) if lang == nil { t.Fatal("GetLanguage returned nil for Common") } if lang.GetName() != "Common" { t.Errorf("Expected name 'Common', got %s", lang.GetName()) } // Test language retrieval by name langByName := manager.GetLanguageByName("Elvish") if langByName == nil { t.Fatal("GetLanguageByName returned nil for Elvish") } if langByName.GetID() != LanguageIDElvish { t.Errorf("Expected ID %d, got %d", LanguageIDElvish, langByName.GetID()) } // Test adding new language dwarvenLang := NewLanguage() dwarvenLang.SetID(LanguageIDDwarven) dwarvenLang.SetName("Dwarven") err = manager.AddLanguage(dwarvenLang) if err != nil { t.Fatalf("Failed to add language: %v", err) } if manager.GetLanguageCount() != 3 { t.Errorf("Expected 3 languages after adding, got %d", manager.GetLanguageCount()) } // Test statistics stats := manager.GetStatistics() if stats.TotalLanguages != 3 { t.Errorf("Expected 3 total languages in stats, got %d", stats.TotalLanguages) } } func TestPlayerLanguageAdapter(t *testing.T) { database := NewMockDatabase() logger := NewMockLogger() // Set up manager with languages manager := NewManager(database, logger) commonLang := NewLanguage() commonLang.SetID(LanguageIDCommon) commonLang.SetName("Common") manager.AddLanguage(commonLang) elvishLang := NewLanguage() elvishLang.SetID(LanguageIDElvish) elvishLang.SetName("Elvish") manager.AddLanguage(elvishLang) // Create adapter adapter := NewPlayerLanguageAdapter(manager, logger) // Test initial state - should know Common by default if !adapter.KnowsLanguage(LanguageIDCommon) { t.Error("Adapter should know Common language by default") } if adapter.GetPrimaryLanguage() != LanguageIDCommon { t.Errorf("Expected primary language to be Common (%d), got %d", LanguageIDCommon, adapter.GetPrimaryLanguage()) } // Test learning a new language err := adapter.LearnLanguage(LanguageIDElvish) if err != nil { t.Fatalf("Failed to learn Elvish: %v", err) } if !adapter.KnowsLanguage(LanguageIDElvish) { t.Error("Should know Elvish after learning") } // Test setting primary language adapter.SetPrimaryLanguage(LanguageIDElvish) if adapter.GetPrimaryLanguage() != LanguageIDElvish { t.Errorf("Expected primary language to be Elvish (%d), got %d", LanguageIDElvish, adapter.GetPrimaryLanguage()) } // Test forgetting a language err = adapter.ForgetLanguage(LanguageIDElvish) if err != nil { t.Fatalf("Failed to forget Elvish: %v", err) } if adapter.KnowsLanguage(LanguageIDElvish) { t.Error("Should not know Elvish after forgetting") } // Primary language should reset to Common after forgetting if adapter.GetPrimaryLanguage() != LanguageIDCommon { t.Errorf("Expected primary language to reset to Common (%d), got %d", LanguageIDCommon, adapter.GetPrimaryLanguage()) } // Test cannot forget Common err = adapter.ForgetLanguage(LanguageIDCommon) if err == nil { t.Error("Should not be able to forget Common language") } } func TestPlayerLanguageAdapterDatabase(t *testing.T) { database := NewMockDatabase() logger := NewMockLogger() // Set up manager with languages manager := NewManager(database, logger) commonLang := NewLanguage() commonLang.SetID(LanguageIDCommon) commonLang.SetName("Common") manager.AddLanguage(commonLang) elvishLang := NewLanguage() elvishLang.SetID(LanguageIDElvish) elvishLang.SetName("Elvish") manager.AddLanguage(elvishLang) // Pre-populate database for player playerID := int32(123) database.SavePlayerLanguage(playerID, LanguageIDCommon) database.SavePlayerLanguage(playerID, LanguageIDElvish) // Create adapter and load from database adapter := NewPlayerLanguageAdapter(manager, logger) err := adapter.LoadPlayerLanguages(database, playerID) if err != nil { t.Fatalf("Failed to load player languages: %v", err) } // Should know both languages if !adapter.KnowsLanguage(LanguageIDCommon) { t.Error("Should know Common after loading from database") } if !adapter.KnowsLanguage(LanguageIDElvish) { t.Error("Should know Elvish after loading from database") } // Learn a new language and save dwarvenLang := NewLanguage() dwarvenLang.SetID(LanguageIDDwarven) dwarvenLang.SetName("Dwarven") manager.AddLanguage(dwarvenLang) err = adapter.LearnLanguage(LanguageIDDwarven) if err != nil { t.Fatalf("Failed to learn Dwarven: %v", err) } err = adapter.SavePlayerLanguages(database, playerID) if err != nil { t.Fatalf("Failed to save player languages: %v", err) } // Create new adapter and load - should have all three languages newAdapter := NewPlayerLanguageAdapter(manager, logger) err = newAdapter.LoadPlayerLanguages(database, playerID) if err != nil { t.Fatalf("Failed to load player languages in new adapter: %v", err) } if !newAdapter.KnowsLanguage(LanguageIDDwarven) { t.Error("New adapter should know Dwarven after loading from database") } } func TestChatLanguageProcessor(t *testing.T) { database := NewMockDatabase() logger := NewMockLogger() manager := NewManager(database, logger) commonLang := NewLanguage() commonLang.SetID(LanguageIDCommon) commonLang.SetName("Common") manager.AddLanguage(commonLang) processor := NewChatLanguageProcessor(manager, logger) player := NewMockPlayer(123, "TestPlayer") message := "Hello, world!" // Test processing message in Common language processed, err := processor.ProcessMessage(player, message, LanguageIDCommon) if err != nil { t.Fatalf("Failed to process message: %v", err) } if processed != message { t.Errorf("Expected processed message '%s', got '%s'", message, processed) } // Test processing message in non-existent language _, err = processor.ProcessMessage(player, message, 9999) if err == nil { t.Error("Should fail to process message in non-existent language") } // Test filter message (currently always returns original message) filtered := processor.FilterMessage(player, message, LanguageIDCommon) if filtered != message { t.Errorf("Expected filtered message '%s', got '%s'", message, filtered) } } func TestLanguageSkramble(t *testing.T) { processor := NewChatLanguageProcessor(nil, nil) message := "Hello World" // Test full comprehension scrambled := processor.GetLanguageSkramble(message, 1.0) if scrambled != message { t.Errorf("Full comprehension should return original message, got '%s'", scrambled) } // Test no comprehension scrambled = processor.GetLanguageSkramble(message, 0.0) if scrambled == message { t.Error("No comprehension should scramble the message") } // Should preserve spaces if len(scrambled) != len(message) { t.Errorf("Scrambled message should have same length: expected %d, got %d", len(message), len(scrambled)) } } func TestManagerCommands(t *testing.T) { database := NewMockDatabase() logger := NewMockLogger() manager := NewManager(database, logger) commonLang := NewLanguage() commonLang.SetID(LanguageIDCommon) commonLang.SetName("Common") manager.AddLanguage(commonLang) // Test stats command result, err := manager.ProcessCommand("stats", []string{}) if err != nil { t.Fatalf("Stats command failed: %v", err) } if result == "" { t.Error("Stats command should return non-empty result") } // Test list command result, err = manager.ProcessCommand("list", []string{}) if err != nil { t.Fatalf("List command failed: %v", err) } if result == "" { t.Error("List command should return non-empty result") } // Test info command result, err = manager.ProcessCommand("info", []string{"0"}) if err != nil { t.Fatalf("Info command failed: %v", err) } if result == "" { t.Error("Info command should return non-empty result") } // Test unknown command _, err = manager.ProcessCommand("unknown", []string{}) if err == nil { t.Error("Unknown command should return error") } } func BenchmarkLanguageLookup(b *testing.B) { database := NewMockDatabase() logger := NewMockLogger() manager := NewManager(database, logger) // Add many languages for i := 0; i < 1000; i++ { lang := NewLanguage() lang.SetID(int32(i)) lang.SetName(fmt.Sprintf("Language_%d", i)) manager.AddLanguage(lang) } b.ResetTimer() for i := 0; i < b.N; i++ { manager.GetLanguage(int32(i % 1000)) } } func BenchmarkPlayerLanguageAdapter(b *testing.B) { database := NewMockDatabase() logger := NewMockLogger() manager := NewManager(database, logger) // Add some languages for i := 0; i < 100; i++ { lang := NewLanguage() lang.SetID(int32(i)) lang.SetName(fmt.Sprintf("Language_%d", i)) manager.AddLanguage(lang) } adapter := NewPlayerLanguageAdapter(manager, logger) b.ResetTimer() for i := 0; i < b.N; i++ { languageID := int32(i % 100) if !adapter.KnowsLanguage(languageID) { adapter.LearnLanguage(languageID) } } }