eq2go/internal/languages/languages_test.go

704 lines
18 KiB
Go

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)
}
}
}