eq2go/internal/entity/manager_test.go

802 lines
20 KiB
Go

package entity
import (
"testing"
)
func createTestManager() *Manager {
return NewManager()
}
func createTestEntity() *Entity {
entity := NewEntity()
// Set some basic info for testing
info := entity.GetInfoStruct()
info.SetName("Test Entity")
info.SetLevel(10)
info.SetClass1(1) // Fighter
info.SetRace(1) // Human
info.SetGender(1) // Male
info.SetStr(50.0)
info.SetSta(45.0)
info.SetAgi(40.0)
info.SetWis(35.0)
info.SetIntel(30.0)
// Set HP and Power for testing
entity.SetTotalHP(1000)
entity.SetHP(1000)
entity.SetTotalPower(500)
entity.SetPower(500)
return entity
}
// Test Manager Creation and Configuration
func TestManagerCreation(t *testing.T) {
manager := NewManager()
if manager == nil {
t.Fatal("NewManager returned nil")
}
// Check default configuration
config := manager.GetConfiguration()
if config["enable_statistics"].(bool) != true {
t.Error("Expected statistics to be enabled by default")
}
if config["enable_validation"].(bool) != true {
t.Error("Expected validation to be enabled by default")
}
if config["enable_pet_management"].(bool) != true {
t.Error("Expected pet management to be enabled by default")
}
}
// Test Entity Creation and Destruction
func TestEntityCreationAndDestruction(t *testing.T) {
manager := createTestManager()
// Test entity creation
entity := manager.CreateEntity()
if entity == nil {
t.Fatal("CreateEntity returned nil")
}
if entity.GetInfoStruct() == nil {
t.Error("Entity info struct is nil")
}
// Check initial state
if entity.IsInCombat() {
t.Error("New entity should not be in combat")
}
if entity.IsCasting() {
t.Error("New entity should not be casting")
}
// Test entity destruction
err := manager.DestroyEntity(entity)
if err != nil {
t.Errorf("DestroyEntity failed: %v", err)
}
// Test destroying nil entity
err = manager.DestroyEntity(nil)
if err == nil {
t.Error("Expected error when destroying nil entity")
}
}
// Test Entity State Management
func TestEntityStateManagement(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Test combat state
if manager.IsEntityInCombat(entity) {
t.Error("Entity should not be in combat initially")
}
manager.SetEntityInCombat(entity, true)
if !manager.IsEntityInCombat(entity) {
t.Error("Entity should be in combat after setting")
}
manager.SetEntityInCombat(entity, false)
if manager.IsEntityInCombat(entity) {
t.Error("Entity should not be in combat after unsetting")
}
// Test casting state
if manager.IsEntityCasting(entity) {
t.Error("Entity should not be casting initially")
}
manager.SetEntityCasting(entity, true)
if !manager.IsEntityCasting(entity) {
t.Error("Entity should be casting after setting")
}
manager.SetEntityCasting(entity, false)
if manager.IsEntityCasting(entity) {
t.Error("Entity should not be casting after unsetting")
}
// Test with nil entity
if manager.IsEntityInCombat(nil) {
t.Error("Nil entity should not be in combat")
}
if manager.IsEntityCasting(nil) {
t.Error("Nil entity should not be casting")
}
}
// Test InfoStruct Management
func TestInfoStructManagement(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Test getting info struct
info := manager.GetEntityInfoStruct(entity)
if info == nil {
t.Fatal("GetEntityInfoStruct returned nil")
}
if info.GetName() != "Test Entity" {
t.Errorf("Expected 'Test Entity', got '%s'", info.GetName())
}
if info.GetLevel() != 10 {
t.Errorf("Expected level 10, got %d", info.GetLevel())
}
// Test setting new info struct
newInfo := NewInfoStruct()
newInfo.SetName("Updated Entity")
newInfo.SetLevel(20)
manager.SetEntityInfoStruct(entity, newInfo)
updatedInfo := manager.GetEntityInfoStruct(entity)
if updatedInfo.GetName() != "Updated Entity" {
t.Error("Info struct was not updated")
}
// Test with nil entity
nilInfo := manager.GetEntityInfoStruct(nil)
if nilInfo != nil {
t.Error("Expected nil for nil entity info struct")
}
}
// Test Spell Effect Management
func TestSpellEffectManagement(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Test adding maintained spell
success := manager.AddMaintainedSpell(entity, "Test Buff", 1001, 60.0, 1)
if !success {
t.Error("Failed to add maintained spell")
}
// Test getting maintained spell
effect := entity.GetMaintainedSpell(1001)
if effect == nil {
t.Error("Failed to retrieve maintained spell")
}
// Test removing maintained spell
success = manager.RemoveMaintainedSpell(entity, 1001)
if !success {
t.Error("Failed to remove maintained spell")
}
// Verify removal
effect = entity.GetMaintainedSpell(1001)
if effect != nil {
t.Error("Maintained spell was not removed")
}
// Test adding temporary spell effect
success = manager.AddSpellEffect(entity, 2001, 12345, 30.0)
if !success {
t.Error("Failed to add spell effect")
}
// Test removing spell effect
success = manager.RemoveSpellEffect(entity, 2001)
if !success {
t.Error("Failed to remove spell effect")
}
// Test detrimental spell
manager.AddDetrimentalSpell(entity, 3001, 12345, 45.0, 1)
// Test removing detrimental spell
success = manager.RemoveDetrimentalSpell(entity, 3001, 12345)
if !success {
t.Error("Failed to remove detrimental spell")
}
// Test with nil entity
if manager.AddMaintainedSpell(nil, "Test", 1, 1.0, 1) {
t.Error("Should not be able to add spell to nil entity")
}
}
// Test Pet Management
func TestPetManagement(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
pet := createTestEntity()
pet.GetInfoStruct().SetName("Test Pet")
// Test setting summon pet
manager.SetEntityPet(entity, pet)
retrievedPet := manager.GetEntityPet(entity)
if retrievedPet != pet {
t.Error("Failed to set/get summon pet")
}
if pet.GetOwner() != entity.GetID() {
t.Error("Pet owner was not set correctly")
}
if pet.GetPetType() != PetTypeSummon {
t.Error("Pet type was not set correctly")
}
// Test charmed pet
charmedPet := createTestEntity()
charmedPet.GetInfoStruct().SetName("Charmed Pet")
manager.SetEntityCharmedPet(entity, charmedPet)
retrievedCharmed := manager.GetEntityCharmedPet(entity)
if retrievedCharmed != charmedPet {
t.Error("Failed to set/get charmed pet")
}
if charmedPet.GetPetType() != PetTypeCharm {
t.Error("Charmed pet type was not set correctly")
}
// Test dismissing pet
manager.DismissPet(pet)
if !pet.IsPetDismissing() {
t.Error("Pet was not marked for dismissal")
}
// Test with nil entities
if manager.GetEntityPet(nil) != nil {
t.Error("Expected nil for nil entity pet")
}
if manager.GetEntityCharmedPet(nil) != nil {
t.Error("Expected nil for nil entity charmed pet")
}
}
// Test Movement and Position
func TestMovementAndPosition(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Set initial position
entity.SetX(100.0)
entity.SetY(200.0, false)
entity.SetZ(300.0)
entity.SetHeading(150, 150) // Heading is int16, not float
// Update position tracking
manager.UpdateEntityPosition(entity)
// Check if position was recorded
lastX, lastY, lastZ, lastHeading := entity.GetLastPosition()
if lastX == -1 || lastY == -1 || lastZ == -1 || lastHeading == -1 {
t.Error("Position was not updated correctly")
}
// Test movement detection (should not have moved yet)
if manager.HasEntityMoved(entity) {
t.Error("Entity should not have moved yet")
}
// Move entity
entity.SetX(101.0)
// Now should detect movement
if !manager.HasEntityMoved(entity) {
t.Error("Entity movement was not detected")
}
// Test speed calculation
entity.SetMaxSpeed(10.0)
entity.SetBaseSpeed(8.0)
entity.SetSpeedMultiplier(1.5)
speed := manager.GetEntityEffectiveSpeed(entity)
expected := float32(8.0 * 1.5) // base * multiplier
if speed != expected {
t.Errorf("Expected speed %.2f, got %.2f", expected, speed)
}
// Test with nil entity
if manager.HasEntityMoved(nil) {
t.Error("Nil entity should not have moved")
}
if manager.GetEntityEffectiveSpeed(nil) != 0.0 {
t.Error("Nil entity speed should be 0.0")
}
}
// Test Stat Calculations
func TestStatCalculations(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Test primary stat calculation
primaryStat := manager.GetEntityPrimaryStat(entity)
if primaryStat != 50 { // STR is highest at 50.0
t.Errorf("Expected primary stat 50, got %d", primaryStat)
}
// Test resistance calculations
info := entity.GetInfoStruct()
info.SetResistance("heat", 25)
info.SetResistance("cold", 30)
heatResist := manager.GetEntityResistance(entity, "heat")
if heatResist != 25 {
t.Errorf("Expected heat resistance 25, got %d", heatResist)
}
coldResist := manager.GetEntityResistance(entity, "cold")
if coldResist != 30 {
t.Errorf("Expected cold resistance 30, got %d", coldResist)
}
unknownResist := manager.GetEntityResistance(entity, "unknown")
if unknownResist != 0 {
t.Errorf("Expected unknown resistance 0, got %d", unknownResist)
}
// Test with nil entity
if manager.GetEntityPrimaryStat(nil) != 0 {
t.Error("Nil entity primary stat should be 0")
}
if manager.GetEntityResistance(nil, "heat") != 0 {
t.Error("Nil entity resistance should be 0")
}
}
// Test Bonus Calculations
func TestBonusCalculations(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Calculate bonuses (this should recalculate all stats)
manager.CalculateEntityBonuses(entity)
// Stats should be recalculated (may be different due to effects)
newStr := entity.GetStr()
// Since we don't have spell effects, stats should remain the same or reset to base
if newStr < 0 {
t.Error("Strength should not be negative after bonus calculation")
}
}
// Test Packet Building (these would normally require packet definitions)
func TestPacketBuilding(t *testing.T) {
manager := createTestManager()
entityID := int32(12345)
clientVersion := uint32(57048)
// Test entity verbs request - should fail without packet definitions
_, err := manager.SendEntityVerbsRequest(entityID, clientVersion)
if err == nil {
t.Error("Expected error for missing packet definition, but got none")
}
// Test entity verbs response - should fail without packet definitions
verbs := []map[string]interface{}{
{"verb_id": 1, "verb_name": "examine"},
{"verb_id": 2, "verb_name": "attack"},
}
_, err = manager.SendEntityVerbsResponse(entityID, clientVersion, verbs)
if err == nil {
t.Error("Expected error for missing packet definition, but got none")
}
// Test entity verb action - should fail without packet definitions
_, err = manager.SendEntityVerbsVerb(entityID, 67890, clientVersion, 1, "examine")
if err == nil {
t.Error("Expected error for missing packet definition, but got none")
}
}
// Test Effect Processing
func TestEffectProcessing(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Add a spell effect
manager.AddSpellEffect(entity, 1001, 12345, 30.0)
// Process effects (should handle cleanup, etc.)
manager.ProcessEntityEffects(entity)
// Cleanup effects
manager.CleanupEntityEffects(entity)
// Test with nil entity (should not crash)
manager.ProcessEntityEffects(nil)
manager.CleanupEntityEffects(nil)
}
// Test Statistics and Monitoring
func TestStatisticsAndMonitoring(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Perform some operations to generate statistics
manager.CreateEntity()
manager.SetEntityInCombat(entity, true)
manager.AddSpellEffect(entity, 1001, 12345, 30.0)
manager.RemoveSpellEffect(entity, 1001)
manager.CalculateEntityBonuses(entity)
stats := manager.GetStatistics()
if stats == nil {
t.Fatal("Statistics returned nil")
}
// Check some expected statistics exist
if _, exists := stats["entities_created"]; !exists {
t.Error("Expected entities_created statistic")
}
if _, exists := stats["spell_effects_added"]; !exists {
t.Error("Expected spell_effects_added statistic")
}
if _, exists := stats["spell_effects_removed"]; !exists {
t.Error("Expected spell_effects_removed statistic")
}
if _, exists := stats["bonus_calculations"]; !exists {
t.Error("Expected bonus_calculations statistic")
}
if _, exists := stats["combat_actions"]; !exists {
t.Error("Expected combat_actions statistic")
}
// Check that some statistics have non-zero values
if stats["entities_created"].(int64) < 1 {
t.Error("Expected at least 1 entity created")
}
if stats["spell_effects_added"].(int64) < 1 {
t.Error("Expected at least 1 spell effect added")
}
// Test reset statistics
manager.ResetStatistics()
newStats := manager.GetStatistics()
if newStats["entities_created"].(int64) != 0 {
t.Error("Expected entities created to be reset to 0")
}
}
// Test Configuration Management
func TestConfigurationManagement(t *testing.T) {
manager := createTestManager()
// Test default configuration
config := manager.GetConfiguration()
if !config["enable_statistics"].(bool) {
t.Error("Expected statistics to be enabled by default")
}
// Test configuration update
newConfig := map[string]interface{}{
"enable_statistics": false,
"enable_combat_logging": true,
"max_entities_per_zone": 5000,
}
manager.SetConfiguration(newConfig)
updatedConfig := manager.GetConfiguration()
if updatedConfig["enable_statistics"].(bool) {
t.Error("Expected statistics to be disabled after update")
}
if !updatedConfig["enable_combat_logging"].(bool) {
t.Error("Expected combat logging to be enabled after update")
}
if updatedConfig["max_entities_per_zone"].(int) != 5000 {
t.Error("Expected max_entities_per_zone to be updated to 5000")
}
}
// Test Validation and Health Checks
func TestValidationAndHealthChecks(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Test health check
if !manager.IsHealthy() {
t.Error("Manager should be healthy")
}
// Test entity validation
issues := manager.ValidateEntity(entity)
if len(issues) > 0 {
t.Errorf("Expected no validation issues, got %d: %v", len(issues), issues)
}
// Test validation with invalid entity
invalidEntity := createTestEntity()
invalidEntity.GetInfoStruct().SetLevel(-1)
invalidEntity.GetInfoStruct().SetName("")
invalidEntity.SetTotalHP(-100)
validationIssues := manager.ValidateEntity(invalidEntity)
if len(validationIssues) == 0 {
t.Error("Expected validation issues for invalid entity")
}
// Check specific issues (should have at least 3 issues)
if len(validationIssues) < 3 {
t.Errorf("Expected at least 3 validation issues, got %d: %v", len(validationIssues), validationIssues)
}
// Test validation with nil entity
nilIssues := manager.ValidateEntity(nil)
if len(nilIssues) != 1 || nilIssues[0] != "entity is nil" {
t.Error("Expected 'entity is nil' issue for nil entity")
}
}
// Test Thread Safety (basic test)
func TestThreadSafety(t *testing.T) {
manager := createTestManager()
entity := createTestEntity()
// Perform concurrent operations
done := make(chan bool, 10)
// Start multiple goroutines performing different operations
for i := 0; i < 10; i++ {
go func(id int) {
defer func() { done <- true }()
// Mix of read and write operations
manager.GetEntityInfoStruct(entity)
manager.SetEntityInCombat(entity, true)
manager.SetEntityInCombat(entity, false)
manager.GetStatistics()
manager.CalculateEntityBonuses(entity)
manager.ProcessEntityEffects(entity)
}(i)
}
// Wait for all goroutines to complete
for i := 0; i < 10; i++ {
<-done
}
// Manager should still be functional
if !manager.IsHealthy() {
t.Error("Manager appears corrupted after concurrent access")
}
}
// Test Entity Stat Methods
func TestEntityStatMethods(t *testing.T) {
entity := createTestEntity()
// Test individual stat getters
if entity.GetStr() != 50 {
t.Errorf("Expected STR 50, got %d", entity.GetStr())
}
if entity.GetSta() != 45 {
t.Errorf("Expected STA 45, got %d", entity.GetSta())
}
if entity.GetAgi() != 40 {
t.Errorf("Expected AGI 40, got %d", entity.GetAgi())
}
if entity.GetWis() != 35 {
t.Errorf("Expected WIS 35, got %d", entity.GetWis())
}
if entity.GetIntel() != 30 {
t.Errorf("Expected INT 30, got %d", entity.GetIntel())
}
// Test primary stat calculation
primary := entity.GetPrimaryStat()
if primary != 50 { // STR is highest
t.Errorf("Expected primary stat 50, got %d", primary)
}
}
// Test Entity Health and Power
func TestEntityHealthAndPower(t *testing.T) {
entity := createTestEntity()
// Test initial values
if entity.GetTotalHP() != 1000 {
t.Errorf("Expected total HP 1000, got %d", entity.GetTotalHP())
}
if entity.GetHP() != 1000 {
t.Errorf("Expected current HP 1000, got %d", entity.GetHP())
}
if entity.GetTotalPower() != 500 {
t.Errorf("Expected total power 500, got %d", entity.GetTotalPower())
}
if entity.GetPower() != 500 {
t.Errorf("Expected current power 500, got %d", entity.GetPower())
}
// Test alive state
if !entity.IsAlive() {
t.Error("Entity should be alive with positive HP")
}
if entity.IsDead() {
t.Error("Entity should not be dead with positive HP")
}
// Test setting HP to 0
entity.SetHP(0)
entity.SetAlive(false) // Need to explicitly set alive state
if entity.IsAlive() {
t.Error("Entity should not be alive with 0 HP")
}
if !entity.IsDead() {
t.Error("Entity should be dead with 0 HP")
}
}
// Test InfoStruct Methods
func TestInfoStructMethods(t *testing.T) {
info := NewInfoStruct()
// Test basic properties
info.SetName("Test Character")
if info.GetName() != "Test Character" {
t.Error("Name was not set correctly")
}
info.SetLevel(25)
if info.GetLevel() != 25 {
t.Error("Level was not set correctly")
}
info.SetClass1(5)
if info.GetClass1() != 5 {
t.Error("Class was not set correctly")
}
info.SetRace(2)
if info.GetRace() != 2 {
t.Error("Race was not set correctly")
}
// Test concentration system
info.SetMaxConcentration(10)
if info.GetMaxConcentration() != 10 {
t.Error("Max concentration was not set correctly")
}
success := info.AddConcentration(5)
if !success {
t.Error("Failed to add concentration")
}
if info.GetCurConcentration() != 5 {
t.Error("Current concentration was not updated correctly")
}
// Try to add too much concentration
success = info.AddConcentration(10)
if success {
t.Error("Should not be able to add more concentration than available")
}
// Remove concentration
info.RemoveConcentration(3)
if info.GetCurConcentration() != 2 {
t.Error("Concentration was not removed correctly")
}
// Test coin system
info.AddCoins(1234567) // 1.23 plat, 45 gold, 67 copper
totalCoins := info.GetCoins()
if totalCoins != 1234567 {
t.Errorf("Expected %d total coins, got %d", 1234567, totalCoins)
}
// Test removing coins
success = info.RemoveCoins(100000)
if !success {
t.Error("Failed to remove coins")
}
newTotal := info.GetCoins()
if newTotal != 1134567 {
t.Errorf("Expected %d coins after removal, got %d", 1134567, newTotal)
}
// Try to remove more coins than available
success = info.RemoveCoins(2000000)
if success {
t.Error("Should not be able to remove more coins than available")
}
}
// Test InfoStruct Clone
func TestInfoStructClone(t *testing.T) {
original := NewInfoStruct()
original.SetName("Original")
original.SetLevel(50)
original.SetStr(100.0)
original.AddCoins(5000)
clone := original.Clone()
if clone == nil {
t.Fatal("Clone returned nil")
}
// Verify clone has same data
if clone.GetName() != original.GetName() {
t.Error("Clone has different name")
}
if clone.GetLevel() != original.GetLevel() {
t.Error("Clone has different level")
}
if clone.GetStr() != original.GetStr() {
t.Error("Clone has different strength")
}
if clone.GetCoins() != original.GetCoins() {
t.Error("Clone has different coins")
}
// Verify clone is independent
clone.SetName("Modified Clone")
if original.GetName() == "Modified Clone" {
t.Error("Modifying clone affected original")
}
}
// Benchmark tests
func BenchmarkCreateEntity(b *testing.B) {
manager := createTestManager()
b.ResetTimer()
for i := 0; i < b.N; i++ {
manager.CreateEntity()
}
}
func BenchmarkGetEntityInfoStruct(b *testing.B) {
manager := createTestManager()
entity := createTestEntity()
b.ResetTimer()
for i := 0; i < b.N; i++ {
manager.GetEntityInfoStruct(entity)
}
}
func BenchmarkCalculateEntityBonuses(b *testing.B) {
manager := createTestManager()
entity := createTestEntity()
b.ResetTimer()
for i := 0; i < b.N; i++ {
manager.CalculateEntityBonuses(entity)
}
}
func BenchmarkGetStatistics(b *testing.B) {
manager := createTestManager()
b.ResetTimer()
for i := 0; i < b.N; i++ {
manager.GetStatistics()
}
}