package items import ( "fmt" "testing" ) func TestNewItem(t *testing.T) { item := NewItem() if item == nil { t.Fatal("NewItem returned nil") } if item.Details.UniqueID <= 0 { t.Error("New item should have a valid unique ID") } if item.Details.Count != 1 { t.Errorf("Expected count 1, got %d", item.Details.Count) } if item.GenericInfo.Condition != DefaultItemCondition { t.Errorf("Expected condition %d, got %d", DefaultItemCondition, item.GenericInfo.Condition) } } func TestNewItemFromTemplate(t *testing.T) { // Create template template := NewItem() template.Name = "Test Sword" template.Description = "A test weapon" template.Details.ItemID = 12345 template.Details.Icon = 100 template.GenericInfo.ItemType = ItemTypeWeapon template.WeaponInfo = &WeaponInfo{ WieldType: ItemWieldTypeSingle, DamageLow1: 10, DamageHigh1: 20, Delay: 30, Rating: 1.5, } // Create from template item := NewItemFromTemplate(template) if item == nil { t.Fatal("NewItemFromTemplate returned nil") } if item.Name != template.Name { t.Errorf("Expected name %s, got %s", template.Name, item.Name) } if item.Details.ItemID != template.Details.ItemID { t.Errorf("Expected item ID %d, got %d", template.Details.ItemID, item.Details.ItemID) } if item.Details.UniqueID == template.Details.UniqueID { t.Error("New item should have different unique ID from template") } if item.WeaponInfo == nil { t.Fatal("Weapon info should be copied") } if item.WeaponInfo.DamageLow1 != template.WeaponInfo.DamageLow1 { t.Errorf("Expected damage %d, got %d", template.WeaponInfo.DamageLow1, item.WeaponInfo.DamageLow1) } } func TestItemCopy(t *testing.T) { original := NewItem() original.Name = "Original Item" original.Details.ItemID = 999 original.AddStat(&ItemStat{ StatName: "Strength", StatType: ItemStatStr, Value: 10, }) copy := original.Copy() if copy == nil { t.Fatal("Copy returned nil") } if copy.Name != original.Name { t.Errorf("Expected name %s, got %s", original.Name, copy.Name) } if copy.Details.UniqueID == original.Details.UniqueID { t.Error("Copy should have different unique ID") } if len(copy.ItemStats) != len(original.ItemStats) { t.Errorf("Expected %d stats, got %d", len(original.ItemStats), len(copy.ItemStats)) } // Test nil copy var nilItem *Item nilCopy := nilItem.Copy() if nilCopy != nil { t.Error("Copy of nil should return nil") } } func TestItemValidation(t *testing.T) { // Valid item item := NewItem() item.Name = "Valid Item" item.Details.ItemID = 100 result := item.Validate() if !result.Valid { t.Errorf("Valid item should pass validation: %v", result.Errors) } // Invalid item - no name invalidItem := NewItem() invalidItem.Details.ItemID = 100 result = invalidItem.Validate() if result.Valid { t.Error("Item without name should fail validation") } // Invalid item - negative count invalidItem2 := NewItem() invalidItem2.Name = "Invalid Item" invalidItem2.Details.ItemID = 100 invalidItem2.Details.Count = -1 result = invalidItem2.Validate() if result.Valid { t.Error("Item with negative count should fail validation") } } func TestItemStats(t *testing.T) { item := NewItem() // Add a stat stat := &ItemStat{ StatName: "Strength", StatType: ItemStatStr, Value: 15, Level: 1, } item.AddStat(stat) if len(item.ItemStats) != 1 { t.Errorf("Expected 1 stat, got %d", len(item.ItemStats)) } // Check if item has stat if !item.HasStat(0, "Strength") { t.Error("Item should have Strength stat") } if !item.HasStat(uint32(ItemStatStr), "") { t.Error("Item should have STR stat by ID") } if item.HasStat(0, "Nonexistent") { t.Error("Item should not have nonexistent stat") } // Add stat by values item.AddStatByValues(ItemStatAgi, 0, 10, 1, "Agility") if len(item.ItemStats) != 2 { t.Errorf("Expected 2 stats, got %d", len(item.ItemStats)) } } func TestItemFlags(t *testing.T) { item := NewItem() // Set flags item.GenericInfo.ItemFlags = Attuned | NoTrade item.GenericInfo.ItemFlags2 = Heirloom | Ornate // Test flag checking if !item.CheckFlag(Attuned) { t.Error("Item should be attuned") } if !item.CheckFlag(NoTrade) { t.Error("Item should be no-trade") } if item.CheckFlag(Lore) { t.Error("Item should not be lore") } if !item.CheckFlag2(Heirloom) { t.Error("Item should be heirloom") } if !item.CheckFlag2(Ornate) { t.Error("Item should be ornate") } if item.CheckFlag2(Refined) { t.Error("Item should not be refined") } } func TestItemLocking(t *testing.T) { item := NewItem() // Item should not be locked initially if item.IsItemLocked() { t.Error("New item should not be locked") } // Lock for crafting if !item.TryLockItem(LockReasonCrafting) { t.Error("Should be able to lock item for crafting") } if !item.IsItemLocked() { t.Error("Item should be locked") } if !item.IsItemLockedFor(LockReasonCrafting) { t.Error("Item should be locked for crafting") } if item.IsItemLockedFor(LockReasonHouse) { t.Error("Item should not be locked for house") } // Try to lock for another reason while already locked if item.TryLockItem(LockReasonHouse) { t.Error("Should not be able to lock for different reason") } // Unlock if !item.TryUnlockItem(LockReasonCrafting) { t.Error("Should be able to unlock item") } if item.IsItemLocked() { t.Error("Item should not be locked after unlock") } } func TestItemTypes(t *testing.T) { item := NewItem() // Test weapon item.GenericInfo.ItemType = ItemTypeWeapon if !item.IsWeapon() { t.Error("Item should be a weapon") } if item.IsArmor() { t.Error("Item should not be armor") } // Test armor item.GenericInfo.ItemType = ItemTypeArmor if !item.IsArmor() { t.Error("Item should be armor") } if item.IsWeapon() { t.Error("Item should not be a weapon") } // Test bag item.GenericInfo.ItemType = ItemTypeBag if !item.IsBag() { t.Error("Item should be a bag") } // Test food item.GenericInfo.ItemType = ItemTypeFood item.FoodInfo = &FoodInfo{Type: 1} // Food if !item.IsFood() { t.Error("Item should be food") } if !item.IsFoodFood() { t.Error("Item should be food (not drink)") } if item.IsFoodDrink() { t.Error("Item should not be drink") } // Test drink item.FoodInfo.Type = 0 // Drink if !item.IsFoodDrink() { t.Error("Item should be drink") } if item.IsFoodFood() { t.Error("Item should not be food") } } func TestMasterItemList(t *testing.T) { masterList := NewMasterItemList() if masterList == nil { t.Fatal("NewMasterItemList returned nil") } // Initial state if masterList.GetItemCount() != 0 { t.Error("New master list should be empty") } // Add item item := NewItem() item.Name = "Test Item" item.Details.ItemID = 12345 masterList.AddItem(item) if masterList.GetItemCount() != 1 { t.Errorf("Expected 1 item, got %d", masterList.GetItemCount()) } // Get item retrieved := masterList.GetItem(12345) if retrieved == nil { t.Fatal("GetItem returned nil") } if retrieved.Name != item.Name { t.Errorf("Expected name %s, got %s", item.Name, retrieved.Name) } // Get by name byName := masterList.GetItemByName("Test Item") if byName == nil { t.Fatal("GetItemByName returned nil") } if byName.Details.ItemID != item.Details.ItemID { t.Errorf("Expected item ID %d, got %d", item.Details.ItemID, byName.Details.ItemID) } // Get non-existent item nonExistent := masterList.GetItem(99999) if nonExistent != nil { t.Error("GetItem should return nil for non-existent item") } // Test stats stats := masterList.GetStats() if stats.TotalItems != 1 { t.Errorf("Expected 1 total item, got %d", stats.TotalItems) } } func TestMasterItemListStatMapping(t *testing.T) { masterList := NewMasterItemList() // Test getting stat ID by name strID := masterList.GetItemStatIDByName("strength") // ItemStatStr is 0, so we need to verify it was actually found if strID != ItemStatStr { t.Errorf("Expected strength stat ID %d, got %d", ItemStatStr, strID) } // Also test a stat that doesn't exist to ensure it returns a different value nonExistentID := masterList.GetItemStatIDByName("nonexistent_stat") if nonExistentID == ItemStatStr && ItemStatStr == 0 { // This means we can't distinguish between "strength" and non-existent stats // Let's verify strength is actually in the map strName := masterList.GetItemStatNameByID(ItemStatStr) if strName != "strength" { t.Error("Strength stat mapping not found") } } // Test getting stat name by ID strName := masterList.GetItemStatNameByID(ItemStatStr) if strName == "" { t.Error("Should find stat name for STR") } // Add custom stat mapping masterList.AddMappedItemStat(9999, "custom stat") customID := masterList.GetItemStatIDByName("custom stat") if customID != 9999 { t.Errorf("Expected custom stat ID 9999, got %d", customID) } customName := masterList.GetItemStatNameByID(9999) if customName != "custom stat" { t.Errorf("Expected 'custom stat', got '%s'", customName) } } func TestPlayerItemList(t *testing.T) { t.Log("Starting TestPlayerItemList...") playerList := NewPlayerItemList() t.Log("NewPlayerItemList() completed") if playerList == nil { t.Fatal("NewPlayerItemList returned nil") } t.Log("PlayerItemList created successfully") // Initial state t.Log("Checking initial state...") if playerList.GetNumberOfItems() != 0 { t.Error("New player list should be empty") } t.Log("Initial state check completed") // Create test item t.Log("Creating test item...") item := NewItem() item.Name = "Player Item" item.Details.ItemID = 1001 item.Details.BagID = 0 item.Details.SlotID = 0 t.Log("Test item created") // Add item t.Log("Adding item to player list...") if !playerList.AddItem(item) { t.Error("Should be able to add item") } t.Log("Item added successfully") if playerList.GetNumberOfItems() != 1 { t.Errorf("Expected 1 item, got %d", playerList.GetNumberOfItems()) } // Get item retrieved := playerList.GetItem(0, 0, BaseEquipment) if retrieved == nil { t.Fatal("GetItem returned nil") } if retrieved.Name != item.Name { t.Errorf("Expected name %s, got %s", item.Name, retrieved.Name) } // Test HasItem if !playerList.HasItem(1001, false) { t.Error("Player should have item 1001") } if playerList.HasItem(9999, false) { t.Error("Player should not have item 9999") } // Remove item playerList.RemoveItem(item, true, true) if playerList.GetNumberOfItems() != 0 { t.Errorf("Expected 0 items after removal, got %d", playerList.GetNumberOfItems()) } } func TestPlayerItemListOverflow(t *testing.T) { playerList := NewPlayerItemList() // Add item to overflow item := NewItem() item.Name = "Overflow Item" item.Details.ItemID = 2001 if !playerList.AddOverflowItem(item) { t.Error("Should be able to add overflow item") } // Check overflow overflowItem := playerList.GetOverflowItem() if overflowItem == nil { t.Fatal("GetOverflowItem returned nil") } if overflowItem.Name != item.Name { t.Errorf("Expected name %s, got %s", item.Name, overflowItem.Name) } // Get all overflow items overflowItems := playerList.GetOverflowItemList() if len(overflowItems) != 1 { t.Errorf("Expected 1 overflow item, got %d", len(overflowItems)) } // Remove overflow item playerList.RemoveOverflowItem(item) overflowItems = playerList.GetOverflowItemList() if len(overflowItems) != 0 { t.Errorf("Expected 0 overflow items, got %d", len(overflowItems)) } } func TestEquipmentItemList(t *testing.T) { equipment := NewEquipmentItemList() if equipment == nil { t.Fatal("NewEquipmentItemList returned nil") } // Initial state if equipment.GetNumberOfItems() != 0 { t.Error("New equipment list should be empty") } // Create test weapon weapon := NewItem() weapon.Name = "Test Sword" weapon.Details.ItemID = 3001 weapon.GenericInfo.ItemType = ItemTypeWeapon weapon.AddSlot(EQ2PrimarySlot) // Equip weapon if !equipment.AddItem(EQ2PrimarySlot, weapon) { t.Error("Should be able to equip weapon") } if equipment.GetNumberOfItems() != 1 { t.Errorf("Expected 1 equipped item, got %d", equipment.GetNumberOfItems()) } // Get equipped weapon equippedWeapon := equipment.GetItem(EQ2PrimarySlot) if equippedWeapon == nil { t.Fatal("GetItem returned nil") } if equippedWeapon.Name != weapon.Name { t.Errorf("Expected name %s, got %s", weapon.Name, equippedWeapon.Name) } // Test equipment queries if !equipment.HasItem(3001) { t.Error("Equipment should have item 3001") } if !equipment.HasWeaponEquipped() { t.Error("Equipment should have weapon equipped") } weapons := equipment.GetWeapons() if len(weapons) != 1 { t.Errorf("Expected 1 weapon, got %d", len(weapons)) } // Remove weapon equipment.RemoveItem(EQ2PrimarySlot, false) if equipment.GetNumberOfItems() != 0 { t.Errorf("Expected 0 items after removal, got %d", equipment.GetNumberOfItems()) } } func TestEquipmentValidation(t *testing.T) { equipment := NewEquipmentItemList() // Create invalid item (no name) invalidItem := NewItem() invalidItem.Details.ItemID = 4001 invalidItem.AddSlot(EQ2HeadSlot) equipment.SetItem(EQ2HeadSlot, invalidItem, false) result := equipment.ValidateEquipment() if result.Valid { t.Error("Equipment with invalid item should fail validation") } // Create item that can't be equipped in the slot wrongSlotItem := NewItem() wrongSlotItem.Name = "Wrong Slot Item" wrongSlotItem.Details.ItemID = 4002 wrongSlotItem.AddSlot(EQ2ChestSlot) // Can only go in chest equipment.SetItem(EQ2HeadSlot, wrongSlotItem, false) result = equipment.ValidateEquipment() if result.Valid { t.Error("Equipment with wrong slot item should fail validation") } } func TestItemSystemAdapter(t *testing.T) { // Create dependencies masterList := NewMasterItemList() spellManager := NewMockSpellManager() // Add a test spell spellManager.AddMockSpell(1001, "Test Spell", 100, 1, "A test spell") adapter := NewItemSystemAdapter( masterList, spellManager, nil, // playerManager nil, // packetManager nil, // ruleManager nil, // databaseService nil, // questManager nil, // brokerManager nil, // craftingManager nil, // housingManager nil, // lootManager ) if adapter == nil { t.Fatal("NewItemSystemAdapter returned nil") } // Test stats stats := adapter.GetSystemStats() if stats == nil { t.Error("GetSystemStats should not return nil") } totalTemplates, ok := stats["total_item_templates"].(int32) if !ok || totalTemplates != 0 { t.Errorf("Expected 0 total templates, got %v", stats["total_item_templates"]) } } func TestItemBrokerChecks(t *testing.T) { masterList := NewMasterItemList() // Create weapon weapon := NewItem() weapon.Name = "Test Weapon" weapon.GenericInfo.ItemType = ItemTypeWeapon weapon.AddSlot(EQ2PrimarySlot) // Test broker type checks if !masterList.ShouldAddItemBrokerSlot(weapon, ItemBrokerSlotPrimary) { t.Error("Weapon should match primary slot broker type") } if masterList.ShouldAddItemBrokerSlot(weapon, ItemBrokerSlotHead) { t.Error("Weapon should not match head slot broker type") } // Create armor with stats armor := NewItem() armor.Name = "Test Armor" armor.GenericInfo.ItemType = ItemTypeArmor armor.AddStat(&ItemStat{ StatName: "Strength", StatType: ItemStatStr, Value: 10, }) if !masterList.ShouldAddItemBrokerStat(armor, ItemBrokerStatTypeStr) { t.Error("Armor should match STR stat broker type") } if masterList.ShouldAddItemBrokerStat(armor, ItemBrokerStatTypeInt) { t.Error("Armor should not match INT stat broker type") } } func TestItemSearchCriteria(t *testing.T) { masterList := NewMasterItemList() // Add test items sword := NewItem() sword.Name = "Steel Sword" sword.Details.ItemID = 5001 sword.Details.Tier = 1 sword.Details.RecommendedLevel = 10 sword.GenericInfo.ItemType = ItemTypeWeapon sword.BrokerPrice = 1000 armor := NewItem() armor.Name = "Iron Armor" armor.Details.ItemID = 5002 armor.Details.Tier = 2 armor.Details.RecommendedLevel = 15 armor.GenericInfo.ItemType = ItemTypeArmor armor.BrokerPrice = 2000 masterList.AddItem(sword) masterList.AddItem(armor) // Search by name criteria := &ItemSearchCriteria{ Name: "sword", } results := masterList.GetItems(criteria) if len(results) != 1 { t.Errorf("Expected 1 result for sword search, got %d", len(results)) } if results[0].Name != sword.Name { t.Errorf("Expected %s, got %s", sword.Name, results[0].Name) } // Search by price range criteria = &ItemSearchCriteria{ MinPrice: 1500, MaxPrice: 2500, } results = masterList.GetItems(criteria) if len(results) != 1 { t.Errorf("Expected 1 result for price search, got %d", len(results)) } if results[0].Name != armor.Name { t.Errorf("Expected %s, got %s", armor.Name, results[0].Name) } // Search by tier criteria = &ItemSearchCriteria{ MinTier: 2, MaxTier: 2, } results = masterList.GetItems(criteria) if len(results) != 1 { t.Errorf("Expected 1 result for tier search, got %d", len(results)) } // Search with no matches criteria = &ItemSearchCriteria{ Name: "nonexistent", } results = masterList.GetItems(criteria) if len(results) != 0 { t.Errorf("Expected 0 results for nonexistent search, got %d", len(results)) } } func TestNextUniqueID(t *testing.T) { id1 := NextUniqueID() id2 := NextUniqueID() if id1 == id2 { t.Error("NextUniqueID should return different IDs") } if id2 != id1+1 { t.Errorf("Expected ID2 to be ID1+1, got %d and %d", id1, id2) } } func TestItemError(t *testing.T) { err := NewItemError("test error") if err == nil { t.Fatal("NewItemError returned nil") } if err.Error() != "test error" { t.Errorf("Expected 'test error', got '%s'", err.Error()) } if !IsItemError(err) { t.Error("Should identify as item error") } // Test with non-item error if IsItemError(fmt.Errorf("not an item error")) { t.Error("Should not identify as item error") } } func TestConstants(t *testing.T) { // Test slot constants if EQ2PrimarySlot != 0 { t.Errorf("Expected EQ2PrimarySlot to be 0, got %d", EQ2PrimarySlot) } if NumSlots != 25 { t.Errorf("Expected NumSlots to be 25, got %d", NumSlots) } // Test item type constants if ItemTypeWeapon != 1 { t.Errorf("Expected ItemTypeWeapon to be 1, got %d", ItemTypeWeapon) } // Test flag constants if Attuned != 1 { t.Errorf("Expected Attuned to be 1, got %d", Attuned) } // Test stat constants if ItemStatStr != 0 { t.Errorf("Expected ItemStatStr to be 0, got %d", ItemStatStr) } } func BenchmarkItemCreation(b *testing.B) { for i := 0; i < b.N; i++ { item := NewItem() item.Name = "Benchmark Item" item.Details.ItemID = int32(i) } } func BenchmarkMasterItemListAccess(b *testing.B) { masterList := NewMasterItemList() // Add test items for i := 0; i < 1000; i++ { item := NewItem() item.Name = fmt.Sprintf("Item %d", i) item.Details.ItemID = int32(i + 1000) masterList.AddItem(item) } b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetItem(int32((i % 1000) + 1000)) } } func BenchmarkPlayerItemListAdd(b *testing.B) { playerList := NewPlayerItemList() b.ResetTimer() for i := 0; i < b.N; i++ { item := NewItem() item.Name = fmt.Sprintf("Item %d", i) item.Details.ItemID = int32(i) item.Details.BagID = int32(i % 6) item.Details.SlotID = int16(i % 20) playerList.AddItem(item) } } func BenchmarkEquipmentBonusCalculation(b *testing.B) { equipment := NewEquipmentItemList() // Add some equipped items with stats for slot := 0; slot < 10; slot++ { item := NewItem() item.Name = fmt.Sprintf("Equipment %d", slot) item.Details.ItemID = int32(slot + 6000) item.AddSlot(int8(slot)) // Add some stats item.AddStat(&ItemStat{StatType: ItemStatStr, Value: 10}) item.AddStat(&ItemStat{StatType: ItemStatAgi, Value: 5}) item.AddStat(&ItemStat{StatType: ItemStatHealth, Value: 100}) equipment.SetItem(int8(slot), item, false) } b.ResetTimer() for i := 0; i < b.N; i++ { equipment.CalculateEquipmentBonuses() } }