eq2go/internal/transmute/transmute_test.go

1257 lines
34 KiB
Go

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