package transmute import ( "path/filepath" "sync" "testing" ) // Mock implementations for testing // MockItem implements the Item interface for testing type MockItem struct { id int32 uniqueID int32 name string adventureDefaultLevel int32 itemFlags int32 itemFlags2 int32 tier int32 stackCount int32 count int32 } func NewMockItem(id, uniqueID int32, name string, level int32) *MockItem { return &MockItem{ id: id, uniqueID: uniqueID, name: name, adventureDefaultLevel: level, tier: ItemTagLegendary, stackCount: 1, count: 1, } } func (m *MockItem) GetID() int32 { return m.id } func (m *MockItem) GetUniqueID() int32 { return m.uniqueID } func (m *MockItem) GetName() string { return m.name } func (m *MockItem) GetAdventureDefaultLevel() int32 { return m.adventureDefaultLevel } func (m *MockItem) GetItemFlags() int32 { return m.itemFlags } func (m *MockItem) GetItemFlags2() int32 { return m.itemFlags2 } func (m *MockItem) GetTier() int32 { return m.tier } func (m *MockItem) GetStackCount() int32 { return m.stackCount } func (m *MockItem) SetCount(count int32) { m.count = count } func (m *MockItem) CreateItemLink(version int32, detailed bool) string { return "[" + m.name + "]" } // MockPlayer implements the Player interface for testing type MockPlayer struct { items map[int32]Item skills map[string]Skill stats map[int32]int32 name string zone Zone } func NewMockPlayer(name string) *MockPlayer { return &MockPlayer{ items: make(map[int32]Item), skills: make(map[string]Skill), stats: make(map[int32]int32), name: name, } } func (m *MockPlayer) GetItemList() map[int32]Item { return m.items } func (m *MockPlayer) GetItemFromUniqueID(uniqueID int32) Item { return m.items[uniqueID] } func (m *MockPlayer) GetSkillByName(skillName string) Skill { return m.skills[skillName] } func (m *MockPlayer) GetStat(statType int32) int32 { return m.stats[statType] } func (m *MockPlayer) GetName() string { return m.name } func (m *MockPlayer) GetZone() Zone { return m.zone } func (m *MockPlayer) RemoveItem(item Item, deleteItem bool) bool { delete(m.items, item.GetUniqueID()); return true } func (m *MockPlayer) AddItem(item Item) (bool, error) { m.items[item.GetUniqueID()] = item; return true, nil } func (m *MockPlayer) IncreaseSkill(skillName string, amount int32) error { return nil } // MockClient implements the Client interface for testing type MockClient struct { version int32 transmuteID int32 packets [][]byte messages []string mutex sync.Mutex } func NewMockClient(version int32) *MockClient { return &MockClient{ version: version, packets: make([][]byte, 0), messages: make([]string, 0), } } func (m *MockClient) GetVersion() int32 { return m.version } func (m *MockClient) GetTransmuteID() int32 { return m.transmuteID } func (m *MockClient) SetTransmuteID(id int32) { m.transmuteID = id } func (m *MockClient) QueuePacket(packet []byte) { m.mutex.Lock() m.packets = append(m.packets, packet) m.mutex.Unlock() } func (m *MockClient) SimpleMessage(channel int32, message string) { m.mutex.Lock() m.messages = append(m.messages, message) m.mutex.Unlock() } func (m *MockClient) Message(channel int32, format string, args ...any) { // Simple implementation for testing m.SimpleMessage(channel, format) } func (m *MockClient) AddItem(item Item, itemDeleted *bool) error { *itemDeleted = false return nil } func (m *MockClient) GetPackets() [][]byte { m.mutex.Lock() defer m.mutex.Unlock() return append([][]byte(nil), m.packets...) } func (m *MockClient) GetMessages() []string { m.mutex.Lock() defer m.mutex.Unlock() return append([]string(nil), m.messages...) } // MockSkill implements the Skill interface for testing type MockSkill struct { current int32 max int32 } func NewMockSkill(current, max int32) *MockSkill { return &MockSkill{current: current, max: max} } func (m *MockSkill) GetCurrentValue() int32 { return m.current } func (m *MockSkill) GetMaxValue() int32 { return m.max } // MockZone implements the Zone interface for testing type MockZone struct{} func (m *MockZone) ProcessSpell(spell Spell, caster Player) error { // For testing, we'll simulate spell processing return nil } // MockSpell implements the Spell interface for testing type MockSpell struct { id int32 name string } func (m *MockSpell) GetID() int32 { return m.id } func (m *MockSpell) GetName() string { return m.name } // MockSpellMaster implements the SpellMaster interface for testing type MockSpellMaster struct { spells map[int32]Spell } func NewMockSpellMaster() *MockSpellMaster { return &MockSpellMaster{ spells: make(map[int32]Spell), } } func (m *MockSpellMaster) GetSpell(spellID int32, tier int32) Spell { return m.spells[spellID] } // MockItemMaster implements the ItemMaster interface for testing type MockItemMaster struct { items map[int32]Item } func NewMockItemMaster() *MockItemMaster { return &MockItemMaster{ items: make(map[int32]Item), } } func (m *MockItemMaster) GetItem(itemID int32) Item { return m.items[itemID] } func (m *MockItemMaster) CreateItem(itemID int32) Item { if item, exists := m.items[itemID]; exists { // Return a copy mockItem := item.(*MockItem) return &MockItem{ id: mockItem.id, uniqueID: mockItem.uniqueID, name: mockItem.name, adventureDefaultLevel: mockItem.adventureDefaultLevel, itemFlags: mockItem.itemFlags, itemFlags2: mockItem.itemFlags2, tier: mockItem.tier, stackCount: mockItem.stackCount, count: 1, } } // Create a default item if not found return NewMockItem(itemID, itemID, "Mock Item", 50) } // Test database functionality func TestDatabaseOperations(t *testing.T) { // Create temporary database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_transmute.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() // Test LoadTransmutingTiers (should populate default tiers) tiers, err := db.LoadTransmutingTiers() if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } if len(tiers) == 0 { t.Fatal("Expected default tiers to be loaded") } expectedTiers := 10 // Should have 10 default tiers (1-9, 10-19, ..., 90-100) if len(tiers) != expectedTiers { t.Errorf("Expected %d default tiers, got %d", expectedTiers, len(tiers)) } // Verify first tier firstTier := tiers[0] if firstTier.MinLevel != 1 || firstTier.MaxLevel != 9 { t.Errorf("Expected first tier to be 1-9, got %d-%d", firstTier.MinLevel, firstTier.MaxLevel) } // Test GetTransmutingTierByLevel tier, err := db.GetTransmutingTierByLevel(5) if err != nil { t.Fatalf("Failed to get tier by level: %v", err) } if tier.MinLevel != 1 || tier.MaxLevel != 9 { t.Errorf("Expected tier 1-9 for level 5, got %d-%d", tier.MinLevel, tier.MaxLevel) } // Test non-existent level _, err = db.GetTransmutingTierByLevel(200) if err == nil { t.Error("Expected error for non-existent tier level") } // Test SaveTransmutingTier newTier := &TransmutingTier{ MinLevel: 101, MaxLevel: 110, FragmentID: 2001, PowderID: 2002, InfusionID: 2003, ManaID: 2004, } err = db.SaveTransmutingTier(newTier) if err != nil { t.Fatalf("Failed to save new tier: %v", err) } // Verify it was saved savedTier, err := db.GetTransmutingTierByLevel(105) if err != nil { t.Fatalf("Failed to get saved tier: %v", err) } if savedTier.FragmentID != 2001 { t.Errorf("Expected fragment ID 2001, got %d", savedTier.FragmentID) } // Test TransmutingTierExists exists, err := db.TransmutingTierExists(101, 110) if err != nil { t.Fatalf("Failed to check tier existence: %v", err) } if !exists { t.Error("Expected tier 101-110 to exist") } exists, err = db.TransmutingTierExists(999, 1000) if err != nil { t.Fatalf("Failed to check non-existent tier: %v", err) } if exists { t.Error("Expected tier 999-1000 not to exist") } // Test UpdateTransmutingTier updatedTier := &TransmutingTier{ MinLevel: 101, MaxLevel: 110, FragmentID: 3001, PowderID: 3002, InfusionID: 3003, ManaID: 3004, } err = db.UpdateTransmutingTier(101, 110, updatedTier) if err != nil { t.Fatalf("Failed to update tier: %v", err) } // Verify update updated, err := db.GetTransmutingTierByLevel(105) if err != nil { t.Fatalf("Failed to get updated tier: %v", err) } if updated.FragmentID != 3001 { t.Errorf("Expected updated fragment ID 3001, got %d", updated.FragmentID) } // Test DeleteTransmutingTier err = db.DeleteTransmutingTier(101, 110) if err != nil { t.Fatalf("Failed to delete tier: %v", err) } // Verify deletion _, err = db.GetTransmutingTierByLevel(105) if err == nil { t.Error("Expected error after deleting tier") } } func TestDatabaseValidation(t *testing.T) { tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_validation.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() // Test saving nil tier err = db.SaveTransmutingTier(nil) if err == nil { t.Error("Expected error when saving nil tier") } // Test invalid level range invalidTier := &TransmutingTier{ MinLevel: -1, MaxLevel: 10, } err = db.SaveTransmutingTier(invalidTier) if err == nil { t.Error("Expected error for invalid level range") } // Test min > max invalidTier.MinLevel = 20 invalidTier.MaxLevel = 10 err = db.SaveTransmutingTier(invalidTier) if err == nil { t.Error("Expected error when min level > max level") } // Test invalid material IDs invalidTier.MinLevel = 10 invalidTier.MaxLevel = 20 invalidTier.FragmentID = 0 err = db.SaveTransmutingTier(invalidTier) if err == nil { t.Error("Expected error for invalid material ID") } } // Test transmutation logic func TestTransmuter(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() // Add some test materials to item master itemMaster.items[1001] = NewMockItem(1001, 1001, "Fragment", 10) itemMaster.items[1002] = NewMockItem(1002, 1002, "Powder", 10) transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Create test database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_transmuter.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() // Load tiers err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } tiers := transmuter.GetTransmutingTiers() if len(tiers) == 0 { t.Fatal("Expected transmuting tiers to be loaded") } // Test item transmutability transmutableItem := NewMockItem(500, 500, "Legendary Sword", 25) transmutableItem.tier = ItemTagLegendary if !transmuter.IsItemTransmutable(transmutableItem) { t.Error("Expected legendary item to be transmutable") } // Test non-transmutable item (wrong tier) nonTransmutableItem := NewMockItem(501, 501, "Common Sword", 25) nonTransmutableItem.tier = ItemTagTreasured if transmuter.IsItemTransmutable(nonTransmutableItem) { t.Error("Expected treasured item not to be transmutable") } // Test flagged item flaggedItem := NewMockItem(502, 502, "No-Trade Sword", 25) flaggedItem.tier = ItemTagLegendary flaggedItem.itemFlags = NoTransmute if transmuter.IsItemTransmutable(flaggedItem) { t.Error("Expected no-transmute item not to be transmutable") } // Test stacked item stackedItem := NewMockItem(503, 503, "Stacked Item", 25) stackedItem.tier = ItemTagLegendary stackedItem.stackCount = 5 if transmuter.IsItemTransmutable(stackedItem) { t.Error("Expected stacked item not to be transmutable") } } func TestCreateItemRequest(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Set up database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_request.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } // Create test player with transmutable items player := NewMockPlayer("TestPlayer") transmutableItem1 := NewMockItem(600, 600, "Legendary Sword", 25) transmutableItem1.tier = ItemTagLegendary transmutableItem2 := NewMockItem(601, 601, "Fabled Shield", 35) transmutableItem2.tier = ItemTagFabled player.items[600] = transmutableItem1 player.items[601] = transmutableItem2 // Add non-transmutable item nonTransmutableItem := NewMockItem(602, 602, "Common Helm", 25) nonTransmutableItem.tier = ItemTagTreasured player.items[602] = nonTransmutableItem client := NewMockClient(1000) // Create item request requestID, err := transmuter.CreateItemRequest(client, player) if err != nil { t.Fatalf("Failed to create item request: %v", err) } if requestID == 0 { t.Error("Expected non-zero request ID") } if client.GetTransmuteID() != requestID { t.Error("Expected client transmute ID to match request ID") } // Verify request was stored request := transmuter.GetActiveRequest(requestID) if request == nil { t.Error("Expected active request to be stored") } if request.Phase != PhaseItemSelection { t.Errorf("Expected phase %d, got %d", PhaseItemSelection, request.Phase) } } func TestHandleItemResponse(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Set up database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_response.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } // Create test player with transmuting skill player := NewMockPlayer("TestPlayer") transmutingSkill := NewMockSkill(100, 300) player.skills["Transmuting"] = transmutingSkill transmutableItem := NewMockItem(700, 700, "Legendary Weapon", 25) transmutableItem.tier = ItemTagLegendary player.items[700] = transmutableItem client := NewMockClient(1000) // Create initial request requestID, err := transmuter.CreateItemRequest(client, player) if err != nil { t.Fatalf("Failed to create initial request: %v", err) } // Handle item response err = transmuter.HandleItemResponse(client, player, requestID, 700) if err != nil { t.Fatalf("Failed to handle item response: %v", err) } // Verify request was updated request := transmuter.GetActiveRequest(requestID) if request == nil { t.Fatal("Expected request to still exist") } if request.Phase != PhaseConfirmation { t.Errorf("Expected phase %d, got %d", PhaseConfirmation, request.Phase) } if request.ItemID != 700 { t.Errorf("Expected item ID 700, got %d", request.ItemID) } // Test insufficient skill lowSkillPlayer := NewMockPlayer("LowSkillPlayer") lowSkill := NewMockSkill(1, 300) lowSkillPlayer.skills["Transmuting"] = lowSkill highLevelItem := NewMockItem(701, 701, "High Level Item", 80) highLevelItem.tier = ItemTagLegendary lowSkillPlayer.items[701] = highLevelItem requestID2, err := transmuter.CreateItemRequest(client, lowSkillPlayer) if err != nil { t.Fatalf("Failed to create second request: %v", err) } err = transmuter.HandleItemResponse(client, lowSkillPlayer, requestID2, 701) if err == nil { t.Error("Expected error for insufficient transmuting skill") } // Test non-existent item err = transmuter.HandleItemResponse(client, player, requestID, 9999) if err == nil { t.Error("Expected error for non-existent item") } // Test non-transmutable item nonTransmutableItem := NewMockItem(702, 702, "Common Item", 25) nonTransmutableItem.tier = ItemTagTreasured player.items[702] = nonTransmutableItem err = transmuter.HandleItemResponse(client, player, requestID, 702) if err == nil { t.Error("Expected error for non-transmutable item") } } func TestHandleConfirmResponse(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() // Add transmute spell to spell master transmuteSpell := &MockSpell{id: TransmuteItemSpellID, name: "Transmute Item"} spellMaster.spells[TransmuteItemSpellID] = transmuteSpell transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Set up database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_confirm.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } // Create test setup player := NewMockPlayer("TestPlayer") zone := &MockZone{} player.zone = zone transmutableItem := NewMockItem(800, 800, "Legendary Item", 25) transmutableItem.tier = ItemTagLegendary player.items[800] = transmutableItem client := NewMockClient(1000) client.SetTransmuteID(800) // Test successful confirmation err = transmuter.HandleConfirmResponse(client, player, 800) if err != nil { t.Fatalf("Failed to handle confirm response: %v", err) } // Test non-existent item err = transmuter.HandleConfirmResponse(client, player, 9999) if err == nil { t.Error("Expected error for non-existent item") } // Test player without zone playerNoZone := NewMockPlayer("NoZonePlayer") playerNoZone.items[800] = transmutableItem err = transmuter.HandleConfirmResponse(client, playerNoZone, 800) if err == nil { t.Error("Expected error for player without zone") } } // Test material calculation logic func TestCalculateTransmuteResult(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() // Add test materials to item master itemMaster.items[1001] = NewMockItem(1001, 1001, "Fragment", 10) // Tier 1 fragment itemMaster.items[1002] = NewMockItem(1002, 1002, "Powder", 10) // Tier 1 powder transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Set up database and load tiers tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_materials.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } // Test legendary item (should give powder/infusion) legendaryItem := NewMockItem(900, 900, "Legendary Item", 25) legendaryItem.tier = ItemTagLegendary // We can't easily test the random result, but we can test the structure result, err := transmuter.calculateTransmuteResult(legendaryItem) if err != nil { t.Fatalf("Failed to calculate transmute result: %v", err) } if !result.Success { t.Errorf("Expected successful result, got error: %s", result.ErrorMessage) } // Test item with no tier match highLevelItem := NewMockItem(901, 901, "High Level Item", 200) highLevelItem.tier = ItemTagLegendary result, err = transmuter.calculateTransmuteResult(highLevelItem) if err != nil { t.Fatalf("Failed to calculate result for high level item: %v", err) } if result.Success { t.Error("Expected failure for item with no tier match") } } func TestCompleteTransmutation(t *testing.T) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() // Add test materials fragment := NewMockItem(1001, 1001, "Fragment", 10) powder := NewMockItem(1002, 1002, "Powder", 10) itemMaster.items[1001] = fragment itemMaster.items[1002] = powder transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) // Set up database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_complete.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() err = transmuter.LoadTransmutingTiers(db) if err != nil { t.Fatalf("Failed to load transmuting tiers: %v", err) } // Create test setup player := NewMockPlayer("TestPlayer") transmutingSkill := NewMockSkill(100, 300) player.skills["Transmuting"] = transmutingSkill transmutableItem := NewMockItem(1000, 1000, "Test Item", 25) transmutableItem.tier = ItemTagLegendary player.items[1000] = transmutableItem client := NewMockClient(1000) client.SetTransmuteID(1000) // Complete transmutation err = transmuter.CompleteTransmutation(client, player) if err != nil { t.Fatalf("Failed to complete transmutation: %v", err) } // Verify item was removed from player if _, exists := player.items[1000]; exists { t.Error("Expected transmuted item to be removed from player") } // Verify messages were sent messages := client.GetMessages() if len(messages) == 0 { t.Error("Expected completion messages to be sent") } // Test non-existent item client.SetTransmuteID(9999) err = transmuter.CompleteTransmutation(client, player) if err == nil { t.Error("Expected error for non-existent item") } } // Test Manager functionality func TestManager(t *testing.T) { // Create test database tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_manager.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() // Test initialization err = manager.Initialize() if err != nil { t.Fatalf("Failed to initialize manager: %v", err) } tiers := manager.GetTransmutingTiers() if len(tiers) == 0 { t.Error("Expected tiers to be loaded after initialization") } // Test statistics stats := manager.GetStatistics() if stats["total_transmutes"] != int64(0) { t.Error("Expected zero transmutes initially") } // Test validation issues := manager.ValidateTransmutingSetup() if len(issues) > 0 { t.Errorf("Expected no validation issues, got: %v", issues) } // Test GetTierForItemLevel tier := manager.GetTierForItemLevel(25) if tier == nil { t.Error("Expected to find tier for level 25") } if tier.MinLevel > 25 || tier.MaxLevel < 25 { t.Errorf("Expected tier to contain level 25, got %d-%d", tier.MinLevel, tier.MaxLevel) } // Test GetTierForItemLevel for non-existent level noTier := manager.GetTierForItemLevel(200) if noTier != nil { t.Error("Expected no tier for level 200") } // Test CalculateRequiredSkill item := NewMockItem(1100, 1100, "Test Item", 25) requiredSkill := manager.CalculateRequiredSkill(item) expectedSkill := (25 - 5) * 5 // (level - 5) * 5 = 100 if requiredSkill != int32(expectedSkill) { t.Errorf("Expected required skill %d, got %d", expectedSkill, requiredSkill) } // Test low level item lowLevelItem := NewMockItem(1101, 1101, "Low Level Item", 3) requiredSkill = manager.CalculateRequiredSkill(lowLevelItem) if requiredSkill != 0 { t.Errorf("Expected required skill 0 for low level item, got %d", requiredSkill) } } func TestManagerPlayerOperations(t *testing.T) { tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_player_ops.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() err = manager.Initialize() if err != nil { t.Fatalf("Failed to initialize manager: %v", err) } // Create test player player := NewMockPlayer("TestPlayer") transmutingSkill := NewMockSkill(100, 300) player.skills["Transmuting"] = transmutingSkill // Add transmutable and non-transmutable items transmutableItem := NewMockItem(1200, 1200, "Legendary Sword", 25) transmutableItem.tier = ItemTagLegendary player.items[1200] = transmutableItem nonTransmutableItem := NewMockItem(1201, 1201, "Common Sword", 25) nonTransmutableItem.tier = ItemTagTreasured player.items[1201] = nonTransmutableItem // Test GetTransmutableItems transmutableItems := manager.GetTransmutableItems(player) if len(transmutableItems) != 1 { t.Errorf("Expected 1 transmutable item, got %d", len(transmutableItems)) } if transmutableItems[0].GetID() != 1200 { t.Errorf("Expected transmutable item ID 1200, got %d", transmutableItems[0].GetID()) } // Test CanPlayerTransmuteItem canTransmute, reason := manager.CanPlayerTransmuteItem(player, transmutableItem) if !canTransmute { t.Errorf("Expected player to be able to transmute item, reason: %s", reason) } // Test with insufficient skill highLevelItem := NewMockItem(1202, 1202, "High Level Item", 80) highLevelItem.tier = ItemTagLegendary canTransmute, reason = manager.CanPlayerTransmuteItem(player, highLevelItem) if canTransmute { t.Error("Expected player not to be able to transmute high level item") } if reason == "" { t.Error("Expected reason for inability to transmute") } // Test with non-transmutable item canTransmute, reason = manager.CanPlayerTransmuteItem(player, nonTransmutableItem) if canTransmute { t.Error("Expected player not to be able to transmute non-transmutable item") } } func TestManagerCommandProcessing(t *testing.T) { tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_commands.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() err = manager.Initialize() if err != nil { t.Fatalf("Failed to initialize manager: %v", err) } client := NewMockClient(1000) player := NewMockPlayer("TestPlayer") // Test stats command result, err := manager.ProcessCommand("stats", []string{}, client, player) if err != nil { t.Fatalf("Failed to process stats command: %v", err) } if result == "" { t.Error("Expected stats command to return result") } // Test validate command result, err = manager.ProcessCommand("validate", []string{}, client, player) if err != nil { t.Fatalf("Failed to process validate command: %v", err) } if result == "" { t.Error("Expected validate command to return result") } // Test tiers command result, err = manager.ProcessCommand("tiers", []string{}, client, player) if err != nil { t.Fatalf("Failed to process tiers command: %v", err) } if result == "" { t.Error("Expected tiers command to return result") } // Test reload command result, err = manager.ProcessCommand("reload", []string{}, client, player) if err != nil { t.Fatalf("Failed to process reload command: %v", err) } if result == "" { t.Error("Expected reload command to return result") } // Test unknown command _, err = manager.ProcessCommand("unknown", []string{}, client, player) if err == nil { t.Error("Expected error for unknown command") } } func TestManagerStatistics(t *testing.T) { tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_stats.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() // Test initial statistics stats := manager.GetStatistics() if stats["total_transmutes"] != int64(0) { t.Error("Expected zero total transmutes initially") } // Test RecordMaterialProduced manager.RecordMaterialProduced(1001, 5) manager.RecordMaterialProduced(1002, 3) manager.RecordMaterialProduced(1001, 2) // Add more of the same material count := manager.GetMaterialProductionCount(1001) if count != 7 { t.Errorf("Expected material 1001 count 7, got %d", count) } count = manager.GetMaterialProductionCount(1002) if count != 3 { t.Errorf("Expected material 1002 count 3, got %d", count) } // Test statistics after recording stats = manager.GetStatistics() materialStats := stats["material_counts"].(map[int32]int64) if materialStats[1001] != 7 { t.Errorf("Expected material 1001 in stats to be 7, got %d", materialStats[1001]) } // Test ResetStatistics manager.ResetStatistics() stats = manager.GetStatistics() if stats["total_transmutes"] != int64(0) { t.Error("Expected statistics to be reset") } count = manager.GetMaterialProductionCount(1001) if count != 0 { t.Error("Expected material count to be reset") } } // Test concurrent operations func TestConcurrency(t *testing.T) { tempDir := t.TempDir() dbPath := filepath.Join(tempDir, "test_concurrency.db") db, err := OpenDB(dbPath) if err != nil { t.Fatalf("Failed to open test database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() err = manager.Initialize() if err != nil { t.Fatalf("Failed to initialize manager: %v", err) } var wg sync.WaitGroup numGoroutines := 10 numOperations := 100 // Test concurrent material recording wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func(id int) { defer wg.Done() for j := 0; j < numOperations; j++ { materialID := int32(1000 + (id % 5)) // Use 5 different materials manager.RecordMaterialProduced(materialID, 1) } }(i) } wg.Wait() // Verify concurrent operations worked correctly stats := manager.GetStatistics() materialStats := stats["material_counts"].(map[int32]int64) totalRecorded := int64(0) for _, count := range materialStats { totalRecorded += count } expectedTotal := int64(numGoroutines * numOperations) if totalRecorded != expectedTotal { t.Errorf("Expected total recorded %d, got %d", expectedTotal, totalRecorded) } } // Test packet builder func TestPacketBuilder(t *testing.T) { builder := NewPacketBuilder() // Test BuildItemRequestPacket items := []int32{100, 200, 300} packet, err := builder.BuildItemRequestPacket(12345, items, 1000) if err != nil { t.Fatalf("Failed to build item request packet: %v", err) } if packet == nil { t.Error("Expected packet to be non-nil") } // Test empty items list _, err = builder.BuildItemRequestPacket(12345, []int32{}, 1000) if err == nil { t.Error("Expected error for empty items list") } // Test BuildConfirmationPacket item := NewMockItem(400, 400, "Test Item", 50) packet, err = builder.BuildConfirmationPacket(12345, item, 1000) if err != nil { t.Fatalf("Failed to build confirmation packet: %v", err) } if packet == nil { t.Error("Expected packet to be non-nil") } // Test nil item _, err = builder.BuildConfirmationPacket(12345, nil, 1000) if err == nil { t.Error("Expected error for nil item") } // Test BuildRewardPacket rewardItems := []Item{ NewMockItem(500, 500, "Reward 1", 30), NewMockItem(501, 501, "Reward 2", 30), } packet, err = builder.BuildRewardPacket(rewardItems, 1000) if err != nil { t.Fatalf("Failed to build reward packet: %v", err) } if packet == nil { t.Error("Expected packet to be non-nil") } // Test empty rewards _, err = builder.BuildRewardPacket([]Item{}, 1000) if err == nil { t.Error("Expected error for empty rewards list") } } // Benchmark tests func BenchmarkIsItemTransmutable(b *testing.B) { itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) item := NewMockItem(1000, 1000, "Test Item", 50) item.tier = ItemTagLegendary b.ResetTimer() for i := 0; i < b.N; i++ { transmuter.IsItemTransmutable(item) } } func BenchmarkDatabaseOperations(b *testing.B) { tempDir := b.TempDir() dbPath := filepath.Join(tempDir, "bench_db.db") db, err := OpenDB(dbPath) if err != nil { b.Fatalf("Failed to open benchmark database: %v", err) } defer db.Close() // Load initial tiers _, err = db.LoadTransmutingTiers() if err != nil { b.Fatalf("Failed to load initial tiers: %v", err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := db.GetTransmutingTierByLevel(int32(25 + (i % 75))) // Test levels 25-99 if err != nil { b.Fatalf("Failed to get tier by level: %v", err) } } } func BenchmarkManagerOperations(b *testing.B) { tempDir := b.TempDir() dbPath := filepath.Join(tempDir, "bench_manager.db") db, err := OpenDB(dbPath) if err != nil { b.Fatalf("Failed to open benchmark database: %v", err) } defer db.Close() itemMaster := NewMockItemMaster() spellMaster := NewMockSpellMaster() packetBuilder := NewPacketBuilder() manager := NewManager(db, itemMaster, spellMaster, packetBuilder) defer manager.Shutdown() err = manager.Initialize() if err != nil { b.Fatalf("Failed to initialize manager: %v", err) } item := NewMockItem(1000, 1000, "Benchmark Item", 50) item.tier = ItemTagLegendary b.ResetTimer() for i := 0; i < b.N; i++ { manager.IsItemTransmutable(item) } }