package entity import ( "sync" "testing" "time" ) func TestNewInfoStruct(t *testing.T) { info := NewInfoStruct() if info == nil { t.Fatal("NewInfoStruct returned nil") } // Test default values if info.GetName() != "" { t.Errorf("Expected empty name, got %s", info.GetName()) } if info.GetLevel() != 0 { t.Errorf("Expected level 0, got %d", info.GetLevel()) } if info.GetMaxConcentration() != 5 { t.Errorf("Expected max concentration 5, got %d", info.GetMaxConcentration()) } if info.GetCurConcentration() != 0 { t.Errorf("Expected current concentration 0, got %d", info.GetCurConcentration()) } } func TestInfoStructBasicProperties(t *testing.T) { info := NewInfoStruct() // Test name info.SetName("Test Character") if info.GetName() != "Test Character" { t.Errorf("Expected name 'Test Character', got %s", info.GetName()) } // Test level info.SetLevel(25) if info.GetLevel() != 25 { t.Errorf("Expected level 25, got %d", info.GetLevel()) } // Test effective level info.SetEffectiveLevel(30) if info.GetEffectiveLevel() != 30 { t.Errorf("Expected effective level 30, got %d", info.GetEffectiveLevel()) } // Test class info.SetClass1(5) if info.GetClass1() != 5 { t.Errorf("Expected class 5, got %d", info.GetClass1()) } // Test race info.SetRace(3) if info.GetRace() != 3 { t.Errorf("Expected race 3, got %d", info.GetRace()) } // Test gender info.SetGender(1) if info.GetGender() != 1 { t.Errorf("Expected gender 1, got %d", info.GetGender()) } } func TestInfoStructStats(t *testing.T) { info := NewInfoStruct() // Test strength info.SetStr(15.5) if info.GetStr() != 15.5 { t.Errorf("Expected strength 15.5, got %f", info.GetStr()) } // Test stamina info.SetSta(20.0) if info.GetSta() != 20.0 { t.Errorf("Expected stamina 20.0, got %f", info.GetSta()) } // Test agility info.SetAgi(12.75) if info.GetAgi() != 12.75 { t.Errorf("Expected agility 12.75, got %f", info.GetAgi()) } // Test wisdom info.SetWis(18.25) if info.GetWis() != 18.25 { t.Errorf("Expected wisdom 18.25, got %f", info.GetWis()) } // Test intelligence info.SetIntel(22.5) if info.GetIntel() != 22.5 { t.Errorf("Expected intelligence 22.5, got %f", info.GetIntel()) } } func TestInfoStructConcentration(t *testing.T) { info := NewInfoStruct() // Test setting max concentration info.SetMaxConcentration(15) if info.GetMaxConcentration() != 15 { t.Errorf("Expected max concentration 15, got %d", info.GetMaxConcentration()) } // Test adding concentration success := info.AddConcentration(5) if !success { t.Error("Expected AddConcentration to succeed") } if info.GetCurConcentration() != 5 { t.Errorf("Expected current concentration 5, got %d", info.GetCurConcentration()) } // Test adding concentration that would exceed maximum success = info.AddConcentration(12) if success { t.Error("Expected AddConcentration to fail when exceeding maximum") } if info.GetCurConcentration() != 5 { t.Errorf("Expected current concentration to remain 5, got %d", info.GetCurConcentration()) } // Test adding concentration that exactly reaches maximum success = info.AddConcentration(10) if !success { t.Error("Expected AddConcentration to succeed when exactly reaching maximum") } if info.GetCurConcentration() != 15 { t.Errorf("Expected current concentration 15, got %d", info.GetCurConcentration()) } // Test removing concentration info.RemoveConcentration(7) if info.GetCurConcentration() != 8 { t.Errorf("Expected current concentration 8, got %d", info.GetCurConcentration()) } // Test removing more concentration than available info.RemoveConcentration(20) if info.GetCurConcentration() != 0 { t.Errorf("Expected current concentration to be clamped to 0, got %d", info.GetCurConcentration()) } } func TestInfoStructCoins(t *testing.T) { info := NewInfoStruct() // Test initial coins if info.GetCoins() != 0 { t.Errorf("Expected initial coins 0, got %d", info.GetCoins()) } // Test adding copper info.AddCoins(150) // 1 silver and 50 copper totalCopper := info.GetCoins() if totalCopper != 150 { t.Errorf("Expected total coins 150, got %d", totalCopper) } // Test adding large amount that converts to higher denominations info.AddCoins(1234567) // Should convert to plat, gold, silver, copper expectedTotal := 150 + 1234567 totalCopper = info.GetCoins() if totalCopper != int32(expectedTotal) { t.Errorf("Expected total coins %d, got %d", expectedTotal, totalCopper) } // Test removing coins success := info.RemoveCoins(100) if !success { t.Error("Expected RemoveCoins to succeed") } expectedTotal -= 100 totalCopper = info.GetCoins() if totalCopper != int32(expectedTotal) { t.Errorf("Expected total coins %d after removal, got %d", expectedTotal, totalCopper) } // Test removing more coins than available success = info.RemoveCoins(9999999) if success { t.Error("Expected RemoveCoins to fail when removing more than available") } // Total should remain unchanged totalCopper = info.GetCoins() if totalCopper != int32(expectedTotal) { t.Errorf("Expected total coins to remain %d, got %d", expectedTotal, totalCopper) } } func TestInfoStructResistances(t *testing.T) { info := NewInfoStruct() // Test all resistance types resistanceTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"} expectedValues := []int16{10, 15, 20, 25, 30, 35, 40} for i, resistType := range resistanceTypes { expectedValue := expectedValues[i] // Set resistance info.SetResistance(resistType, expectedValue) // Get resistance actualValue := info.GetResistance(resistType) if actualValue != expectedValue { t.Errorf("Expected %s resistance %d, got %d", resistType, expectedValue, actualValue) } } // Test invalid resistance type unknownResist := info.GetResistance("unknown") if unknownResist != 0 { t.Errorf("Expected unknown resistance type to return 0, got %d", unknownResist) } // Setting invalid resistance type should not panic info.SetResistance("unknown", 50) // Should be ignored } func TestInfoStructResetEffects(t *testing.T) { info := NewInfoStruct() // Set some base values first (these would normally be set during character creation) info.str = 10.0 info.strBase = 10.0 info.sta = 12.0 info.staBase = 12.0 info.heat = 15 info.heatBase = 15 // Modify current values to simulate bonuses info.SetStr(20.0) info.SetSta(25.0) info.SetResistance("heat", 30) // Reset effects info.ResetEffects() // Check that values were reset to base if info.GetStr() != 10.0 { t.Errorf("Expected strength to reset to 10.0, got %f", info.GetStr()) } if info.GetSta() != 12.0 { t.Errorf("Expected stamina to reset to 12.0, got %f", info.GetSta()) } if info.GetResistance("heat") != 15 { t.Errorf("Expected heat resistance to reset to 15, got %d", info.GetResistance("heat")) } } func TestInfoStructCalculatePrimaryStat(t *testing.T) { info := NewInfoStruct() // Set all stats to different values info.SetStr(10.0) info.SetSta(15.0) info.SetAgi(8.0) info.SetWis(20.0) // This should be the highest info.SetIntel(12.0) primaryStat := info.CalculatePrimaryStat() if primaryStat != 20.0 { t.Errorf("Expected primary stat 20.0 (wisdom), got %f", primaryStat) } // Test with intelligence being highest info.SetIntel(25.0) primaryStat = info.CalculatePrimaryStat() if primaryStat != 25.0 { t.Errorf("Expected primary stat 25.0 (intelligence), got %f", primaryStat) } // Test with all stats equal info.SetStr(30.0) info.SetSta(30.0) info.SetAgi(30.0) info.SetWis(30.0) info.SetIntel(30.0) primaryStat = info.CalculatePrimaryStat() if primaryStat != 30.0 { t.Errorf("Expected primary stat 30.0 (all equal), got %f", primaryStat) } } func TestInfoStructClone(t *testing.T) { info := NewInfoStruct() // Set some values info.SetName("Original Character") info.SetLevel(15) info.SetStr(20.0) info.SetResistance("magic", 25) info.AddConcentration(3) // Clone the info struct clone := info.Clone() if clone == nil { t.Fatal("Clone returned nil") } // Verify clone has same values if clone.GetName() != "Original Character" { t.Errorf("Expected cloned name 'Original Character', got %s", clone.GetName()) } if clone.GetLevel() != 15 { t.Errorf("Expected cloned level 15, got %d", clone.GetLevel()) } if clone.GetStr() != 20.0 { t.Errorf("Expected cloned strength 20.0, got %f", clone.GetStr()) } if clone.GetResistance("magic") != 25 { t.Errorf("Expected cloned magic resistance 25, got %d", clone.GetResistance("magic")) } if clone.GetCurConcentration() != 3 { t.Errorf("Expected cloned concentration 3, got %d", clone.GetCurConcentration()) } // Verify that modifying the clone doesn't affect the original clone.SetName("Cloned Character") clone.SetLevel(20) if info.GetName() != "Original Character" { t.Error("Original name was modified when clone was changed") } if info.GetLevel() != 15 { t.Error("Original level was modified when clone was changed") } } func TestInfoStructGetUptime(t *testing.T) { info := NewInfoStruct() // Test uptime (currently returns 0 as it's not implemented) uptime := info.GetUptime() if uptime != time.Duration(0) { t.Errorf("Expected uptime to be 0 (not implemented), got %v", uptime) } } func TestInfoStructConcurrency(t *testing.T) { info := NewInfoStruct() info.SetMaxConcentration(100) var wg sync.WaitGroup numGoroutines := 20 // Test concurrent access to basic properties wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func(index int) { defer wg.Done() // Each goroutine sets unique values info.SetName("Character" + string(rune('A'+index))) _ = info.GetName() info.SetLevel(int16(10 + index)) _ = info.GetLevel() info.SetStr(float32(10.0 + float32(index))) _ = info.GetStr() }(i) } wg.Wait() // Test concurrent concentration operations wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func() { defer wg.Done() // Try to add concentration if info.AddConcentration(1) { // If successful, remove it after a short delay time.Sleep(time.Microsecond) info.RemoveConcentration(1) } }() } wg.Wait() // After all operations, concentration should be back to 0 // (This might not always be true due to race conditions, but shouldn't crash) _ = info.GetCurConcentration() // Test concurrent coin operations wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func(amount int32) { defer wg.Done() info.AddCoins(amount) _ = info.GetCoins() // Try to remove some coins info.RemoveCoins(amount / 2) }(int32(100 + i)) } wg.Wait() // Test concurrent resistance operations wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func(value int16) { defer wg.Done() info.SetResistance("heat", value) _ = info.GetResistance("heat") info.SetResistance("cold", value+1) _ = info.GetResistance("cold") }(int16(i)) } wg.Wait() } func TestInfoStructLargeValues(t *testing.T) { info := NewInfoStruct() // Test with large coin amounts largeCoinAmount := int32(2000000000) // 2 billion copper info.AddCoins(largeCoinAmount) totalCoins := info.GetCoins() if totalCoins != largeCoinAmount { t.Errorf("Expected large coin amount %d, got %d", largeCoinAmount, totalCoins) } // Test removing large amounts success := info.RemoveCoins(largeCoinAmount) if !success { t.Error("Expected to be able to remove large coin amount") } if info.GetCoins() != 0 { t.Errorf("Expected coins to be 0 after removing all, got %d", info.GetCoins()) } // Test with maximum values info.SetMaxConcentration(32767) // Max int16 info.SetLevel(32767) if info.GetMaxConcentration() != 32767 { t.Errorf("Expected max concentration 32767, got %d", info.GetMaxConcentration()) } if info.GetLevel() != 32767 { t.Errorf("Expected level 32767, got %d", info.GetLevel()) } } func TestInfoStructEdgeCases(t *testing.T) { info := NewInfoStruct() // Test negative values info.SetStr(-5.0) if info.GetStr() != -5.0 { t.Errorf("Expected negative strength -5.0, got %f", info.GetStr()) } // Test zero values info.SetMaxConcentration(0) success := info.AddConcentration(1) if success { t.Error("Expected AddConcentration to fail with max concentration 0") } // Test very small float values info.SetAgi(0.001) if info.GetAgi() != 0.001 { t.Errorf("Expected small agility 0.001, got %f", info.GetAgi()) } // Test empty string name info.SetName("") if info.GetName() != "" { t.Errorf("Expected empty name, got '%s'", info.GetName()) } // Test very long name longName := string(make([]byte, 1000)) for i := range longName { longName = longName[:i] + "A" + longName[i+1:] } info.SetName(longName) if info.GetName() != longName { t.Error("Expected to handle very long names") } }