package collections import ( "context" "fmt" "sync" "testing" ) // Mock implementations for testing // MockCollectionDatabase implements CollectionDatabase interface for testing type MockCollectionDatabase struct { mu sync.RWMutex collections []CollectionData collectionItems map[int32][]CollectionItem collectionRewards map[int32][]CollectionRewardData playerCollections map[int32][]PlayerCollectionData playerCollectionItems map[string][]int32 // key: "charID-collectionID" failLoad bool failSave bool } func NewMockCollectionDatabase() *MockCollectionDatabase { return &MockCollectionDatabase{ collections: make([]CollectionData, 0), collectionItems: make(map[int32][]CollectionItem), collectionRewards: make(map[int32][]CollectionRewardData), playerCollections: make(map[int32][]PlayerCollectionData), playerCollectionItems: make(map[string][]int32), } } func (m *MockCollectionDatabase) LoadCollections(ctx context.Context) ([]CollectionData, error) { m.mu.RLock() defer m.mu.RUnlock() if m.failLoad { return nil, fmt.Errorf("mock load error") } return m.collections, nil } func (m *MockCollectionDatabase) LoadCollectionItems(ctx context.Context, collectionID int32) ([]CollectionItem, error) { m.mu.RLock() defer m.mu.RUnlock() if m.failLoad { return nil, fmt.Errorf("mock load error") } items, exists := m.collectionItems[collectionID] if !exists { return []CollectionItem{}, nil } return items, nil } func (m *MockCollectionDatabase) LoadCollectionRewards(ctx context.Context, collectionID int32) ([]CollectionRewardData, error) { m.mu.RLock() defer m.mu.RUnlock() if m.failLoad { return nil, fmt.Errorf("mock load error") } rewards, exists := m.collectionRewards[collectionID] if !exists { return []CollectionRewardData{}, nil } return rewards, nil } func (m *MockCollectionDatabase) LoadPlayerCollections(ctx context.Context, characterID int32) ([]PlayerCollectionData, error) { m.mu.RLock() defer m.mu.RUnlock() if m.failLoad { return nil, fmt.Errorf("mock load error") } collections, exists := m.playerCollections[characterID] if !exists { return []PlayerCollectionData{}, nil } return collections, nil } func (m *MockCollectionDatabase) LoadPlayerCollectionItems(ctx context.Context, characterID, collectionID int32) ([]int32, error) { m.mu.RLock() defer m.mu.RUnlock() if m.failLoad { return nil, fmt.Errorf("mock load error") } key := fmt.Sprintf("%d-%d", characterID, collectionID) items, exists := m.playerCollectionItems[key] if !exists { return []int32{}, nil } return items, nil } func (m *MockCollectionDatabase) SavePlayerCollection(ctx context.Context, characterID, collectionID int32, completed bool) error { m.mu.Lock() defer m.mu.Unlock() if m.failSave { return fmt.Errorf("mock save error") } // Update or add player collection found := false collections := m.playerCollections[characterID] for i := range collections { if collections[i].CollectionID == collectionID { collections[i].Completed = completed found = true break } } if !found { m.playerCollections[characterID] = append(collections, PlayerCollectionData{ CharacterID: characterID, CollectionID: collectionID, Completed: completed, }) } return nil } func (m *MockCollectionDatabase) SavePlayerCollectionItem(ctx context.Context, characterID, collectionID, itemID int32) error { m.mu.Lock() defer m.mu.Unlock() if m.failSave { return fmt.Errorf("mock save error") } key := fmt.Sprintf("%d-%d", characterID, collectionID) items := m.playerCollectionItems[key] // Check if already exists for _, existingItem := range items { if existingItem == itemID { return nil } } m.playerCollectionItems[key] = append(items, itemID) return nil } func (m *MockCollectionDatabase) SavePlayerCollections(ctx context.Context, characterID int32, collections []*Collection) error { if m.failSave { return fmt.Errorf("mock save error") } for _, collection := range collections { if collection.GetSaveNeeded() { err := m.SavePlayerCollection(ctx, characterID, collection.GetID(), collection.GetCompleted()) if err != nil { return err } // Save items items := collection.GetCollectionItems() for _, item := range items { if item.Found == ItemFound { err := m.SavePlayerCollectionItem(ctx, characterID, collection.GetID(), item.ItemID) if err != nil { return err } } } } } return nil } // MockItemLookup implements ItemLookup interface for testing type MockItemLookup struct { items map[int32]ItemInfo } func NewMockItemLookup() *MockItemLookup { return &MockItemLookup{ items: make(map[int32]ItemInfo), } } func (m *MockItemLookup) GetItem(itemID int32) (ItemInfo, error) { if item, exists := m.items[itemID]; exists { return item, nil } return ItemInfo{}, fmt.Errorf("item not found") } func (m *MockItemLookup) ItemExists(itemID int32) bool { _, exists := m.items[itemID] return exists } func (m *MockItemLookup) GetItemName(itemID int32) string { if item, exists := m.items[itemID]; exists { return item.Name } return "Unknown Item" } // MockPlayerManager implements PlayerManager interface for testing type MockPlayerManager struct { players map[int32]PlayerInfo } func NewMockPlayerManager() *MockPlayerManager { return &MockPlayerManager{ players: make(map[int32]PlayerInfo), } } func (m *MockPlayerManager) GetPlayerInfo(characterID int32) (PlayerInfo, error) { if player, exists := m.players[characterID]; exists { return player, nil } return PlayerInfo{}, fmt.Errorf("player not found") } func (m *MockPlayerManager) IsPlayerOnline(characterID int32) bool { if player, exists := m.players[characterID]; exists { return player.IsOnline } return false } func (m *MockPlayerManager) GetPlayerLevel(characterID int32) int8 { if player, exists := m.players[characterID]; exists { return player.Level } return 0 } // MockClientManager implements ClientManager interface for testing type MockClientManager struct { mu sync.Mutex sentUpdates []int32 sentCompletions []int32 sentLists []int32 sentProgress []int32 failSend bool } func NewMockClientManager() *MockClientManager { return &MockClientManager{ sentUpdates: make([]int32, 0), sentCompletions: make([]int32, 0), sentLists: make([]int32, 0), sentProgress: make([]int32, 0), } } func (m *MockClientManager) SendCollectionUpdate(characterID int32, collection *Collection) error { m.mu.Lock() defer m.mu.Unlock() if m.failSend { return fmt.Errorf("mock send error") } m.sentUpdates = append(m.sentUpdates, characterID) return nil } func (m *MockClientManager) SendCollectionComplete(characterID int32, collection *Collection) error { m.mu.Lock() defer m.mu.Unlock() if m.failSend { return fmt.Errorf("mock send error") } m.sentCompletions = append(m.sentCompletions, characterID) return nil } func (m *MockClientManager) SendCollectionList(characterID int32, collections []CollectionInfo) error { m.mu.Lock() defer m.mu.Unlock() if m.failSend { return fmt.Errorf("mock send error") } m.sentLists = append(m.sentLists, characterID) return nil } func (m *MockClientManager) SendCollectionProgress(characterID int32, progress []CollectionProgress) error { m.mu.Lock() defer m.mu.Unlock() if m.failSend { return fmt.Errorf("mock send error") } m.sentProgress = append(m.sentProgress, characterID) return nil } // MockRewardProvider implements RewardProvider interface for testing type MockRewardProvider struct { mu sync.Mutex givenItems []struct { CharacterID int32 ItemID int32 Quantity int8 } givenCoin []struct { CharacterID int32 Amount int64 } givenXP []struct { CharacterID int32 Amount int64 } failGive bool failValidate bool } func NewMockRewardProvider() *MockRewardProvider { return &MockRewardProvider{} } func (m *MockRewardProvider) GiveItem(characterID int32, itemID int32, quantity int8) error { m.mu.Lock() defer m.mu.Unlock() if m.failGive { return fmt.Errorf("mock give error") } m.givenItems = append(m.givenItems, struct { CharacterID int32 ItemID int32 Quantity int8 }{characterID, itemID, quantity}) return nil } func (m *MockRewardProvider) GiveCoin(characterID int32, amount int64) error { m.mu.Lock() defer m.mu.Unlock() if m.failGive { return fmt.Errorf("mock give error") } m.givenCoin = append(m.givenCoin, struct { CharacterID int32 Amount int64 }{characterID, amount}) return nil } func (m *MockRewardProvider) GiveXP(characterID int32, amount int64) error { m.mu.Lock() defer m.mu.Unlock() if m.failGive { return fmt.Errorf("mock give error") } m.givenXP = append(m.givenXP, struct { CharacterID int32 Amount int64 }{characterID, amount}) return nil } func (m *MockRewardProvider) ValidateRewards(characterID int32, rewards []CollectionRewardItem, coin, xp int64) error { if m.failValidate { return fmt.Errorf("mock validate error") } return nil } // Tests for Collection func TestNewCollection(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test Collection") c.SetCategory("Test Category") c.SetLevel(10) if c.GetID() != 1 { t.Errorf("Expected ID 1, got %d", c.GetID()) } if c.GetName() != "Test Collection" { t.Errorf("Expected name 'Test Collection', got %s", c.GetName()) } if c.GetCategory() != "Test Category" { t.Errorf("Expected category 'Test Category', got %s", c.GetCategory()) } if c.GetLevel() != 10 { t.Errorf("Expected level 10, got %d", c.GetLevel()) } if c.GetCompleted() { t.Error("Expected collection to be incomplete") } if c.GetSaveNeeded() { t.Error("Expected saveNeeded to be false") } } func TestCollectionCopy(t *testing.T) { c1 := NewCollection() c1.SetID(1) c1.SetName("Original") c1.SetCategory("Category") c1.SetLevel(5) c1.SetRewardCoin(1000) c1.SetRewardXP(5000) c1.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound}) c2 := NewCollectionFromData(c1) // Verify copy has same values if c2.GetID() != c1.GetID() { t.Error("Copy has different ID") } if c2.GetName() != c1.GetName() { t.Error("Copy has different name") } if c2.GetRewardCoin() != c1.GetRewardCoin() { t.Error("Copy has different reward coin") } // Verify deep copy (modifying copy doesn't affect original) c2.SetRewardCoin(2000) if c1.GetRewardCoin() == c2.GetRewardCoin() { t.Error("Modifying copy affected original") } } func TestCollectionItems(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) // Add items c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound}) c.AddCollectionItem(CollectionItem{ItemID: 101, Index: 1, Found: ItemNotFound}) c.AddCollectionItem(CollectionItem{ItemID: 102, Index: 2, Found: ItemNotFound}) items := c.GetCollectionItems() if len(items) != 3 { t.Errorf("Expected 3 items, got %d", len(items)) } // Test finding item found := c.MarkItemFound(101) if !found { t.Error("Expected to find item 101") } if !c.GetSaveNeeded() { t.Error("Expected saveNeeded to be true after finding item") } // Test finding non-existent item found = c.MarkItemFound(999) if found { t.Error("Should not find non-existent item") } // Check if item is needed if !c.NeedsItem(100) { t.Error("Expected NeedsItem to return true for unfound item") } if c.NeedsItem(101) { t.Error("Expected NeedsItem to return false for found item") } // Check ready status if c.GetIsReadyToTurnIn() { t.Error("Collection should not be ready with unfound items") } // Find remaining items c.MarkItemFound(100) c.MarkItemFound(102) if !c.GetIsReadyToTurnIn() { t.Error("Collection should be ready when all items found") } } func TestCollectionRewards(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) // Set rewards c.SetRewardCoin(1000) c.SetRewardXP(5000) // Add reward items c.AddRewardItem(CollectionRewardItem{ItemID: 200, Quantity: 1}) c.AddRewardItem(CollectionRewardItem{ItemID: 201, Quantity: 5}) // Add selectable rewards c.AddSelectableRewardItem(CollectionRewardItem{ItemID: 300, Quantity: 1}) c.AddSelectableRewardItem(CollectionRewardItem{ItemID: 301, Quantity: 1}) if c.GetRewardCoin() != 1000 { t.Errorf("Expected reward coin 1000, got %d", c.GetRewardCoin()) } if c.GetRewardXP() != 5000 { t.Errorf("Expected reward XP 5000, got %d", c.GetRewardXP()) } rewards := c.GetRewardItems() if len(rewards) != 2 { t.Errorf("Expected 2 reward items, got %d", len(rewards)) } selectable := c.GetSelectableRewardItems() if len(selectable) != 2 { t.Errorf("Expected 2 selectable rewards, got %d", len(selectable)) } } func TestCollectionCompletion(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound}) // Check not ready to complete without all items if c.GetIsReadyToTurnIn() { t.Error("Should not be ready to complete without all items") } // Find item c.MarkItemFound(100) if !c.GetIsReadyToTurnIn() { t.Error("Should be ready to complete when all items found") } // Mark as completed c.SetCompleted(true) if !c.GetCompleted() { t.Error("Expected collection to be completed") } } func TestCollectionProgress(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound}) c.AddCollectionItem(CollectionItem{ItemID: 101, Index: 1, Found: ItemNotFound}) c.AddCollectionItem(CollectionItem{ItemID: 102, Index: 2, Found: ItemNotFound}) c.AddCollectionItem(CollectionItem{ItemID: 103, Index: 3, Found: ItemNotFound}) // Initial progress progress := c.GetProgress() if progress != 0.0 { t.Errorf("Expected 0%% progress, got %.2f%%", progress) } // Find one item c.MarkItemFound(100) progress = c.GetProgress() if progress != 25.0 { t.Errorf("Expected 25%% progress, got %.2f%%", progress) } // Find more items c.MarkItemFound(101) c.MarkItemFound(102) progress = c.GetProgress() if progress != 75.0 { t.Errorf("Expected 75%% progress, got %.2f%%", progress) } // Complete collection c.MarkItemFound(103) c.SetCompleted(true) progress = c.GetProgress() if progress != 100.0 { t.Errorf("Expected 100%% progress, got %.2f%%", progress) } } // Tests for MasterCollectionList func TestNewMasterCollectionList(t *testing.T) { db := NewMockCollectionDatabase() ml := NewMasterCollectionList(db) if ml == nil { t.Fatal("Failed to create MasterCollectionList") } } func TestMasterCollectionListLoad(t *testing.T) { db := NewMockCollectionDatabase() // Add test data db.collections = []CollectionData{ {ID: 1, Name: "Collection 1", Category: "Category A", Level: 10}, {ID: 2, Name: "Collection 2", Category: "Category B", Level: 20}, } db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, {ItemID: 101, Index: 1, Found: ItemNotFound}, } db.collectionItems[2] = []CollectionItem{ {ItemID: 200, Index: 0, Found: ItemNotFound}, } db.collectionRewards[1] = []CollectionRewardData{ {CollectionID: 1, RewardType: RewardTypeCoin, RewardValue: "1000", Quantity: 1}, {CollectionID: 1, RewardType: RewardTypeXP, RewardValue: "5000", Quantity: 1}, } // Set up item lookup itemLookup := NewMockItemLookup() itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"} itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 3"} ml := NewMasterCollectionList(db) err := ml.Initialize(context.Background(), itemLookup) if err != nil { t.Fatalf("Failed to load collections: %v", err) } // Verify collections loaded collection := ml.GetCollection(1) if collection == nil { t.Fatal("Collection 1 not found") } if collection.GetName() != "Collection 1" { t.Errorf("Expected name 'Collection 1', got %s", collection.GetName()) } // Verify items loaded items := collection.GetCollectionItems() if len(items) != 2 { t.Errorf("Expected 2 items, got %d", len(items)) } // Verify rewards loaded if collection.GetRewardCoin() != 1000 { t.Errorf("Expected reward coin 1000, got %d", collection.GetRewardCoin()) } } func TestMasterCollectionListSearch(t *testing.T) { db := NewMockCollectionDatabase() db.collections = []CollectionData{ {ID: 1, Name: "Ancient Artifacts", Category: "Archaeology", Level: 10}, {ID: 2, Name: "Ancient Weapons", Category: "Combat", Level: 20}, {ID: 3, Name: "Modern Art", Category: "Art", Level: 15}, } // Add required items for each collection db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, } db.collectionItems[2] = []CollectionItem{ {ItemID: 200, Index: 0, Found: ItemNotFound}, } db.collectionItems[3] = []CollectionItem{ {ItemID: 300, Index: 0, Found: ItemNotFound}, } // Set up item lookup itemLookup := NewMockItemLookup() itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 2"} itemLookup.items[300] = ItemInfo{ID: 300, Name: "Test Item 3"} ml := NewMasterCollectionList(db) ml.Initialize(context.Background(), itemLookup) // Search by name results := ml.FindCollectionsByName("Ancient") if len(results) != 2 { t.Errorf("Expected 2 results for 'Ancient', got %d", len(results)) } // Search by category results = ml.GetCollectionsByCategory("Art") if len(results) != 1 { t.Errorf("Expected 1 result for category 'Art', got %d", len(results)) } // Search by level range results = ml.GetCollectionsByLevel(15, 25) if len(results) != 2 { t.Errorf("Expected 2 results for level range 15-25, got %d", len(results)) } } // Tests for PlayerCollectionList func TestNewPlayerCollectionList(t *testing.T) { db := NewMockCollectionDatabase() pl := NewPlayerCollectionList(1001, db) if pl == nil { t.Fatal("Failed to create PlayerCollectionList") } if pl.GetCharacterID() != 1001 { t.Errorf("Expected character ID 1001, got %d", pl.GetCharacterID()) } } func TestPlayerCollectionListLoad(t *testing.T) { db := NewMockCollectionDatabase() masterList := NewMasterCollectionList(db) // Set up master collections db.collections = []CollectionData{ {ID: 1, Name: "Collection 1", Category: "Category A", Level: 10}, {ID: 2, Name: "Collection 2", Category: "Category B", Level: 20}, } db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, {ItemID: 101, Index: 1, Found: ItemNotFound}, } db.collectionItems[2] = []CollectionItem{ {ItemID: 200, Index: 0, Found: ItemNotFound}, } // Set up item lookup itemLookup := NewMockItemLookup() itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"} itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 3"} masterList.Initialize(context.Background(), itemLookup) // Set up player data db.playerCollections[1001] = []PlayerCollectionData{ {CharacterID: 1001, CollectionID: 1, Completed: false}, {CharacterID: 1001, CollectionID: 2, Completed: true}, } db.playerCollectionItems["1001-1"] = []int32{100} // Found item 100 in collection 1 pl := NewPlayerCollectionList(1001, db) err := pl.Initialize(context.Background(), masterList) if err != nil { t.Fatalf("Failed to load player collections: %v", err) } // Check loaded collections collection := pl.GetCollection(1) if collection == nil { t.Fatal("Collection 1 not found") } if collection.GetCompleted() { t.Error("Collection 1 should not be completed") } // Check found items if collection.NeedsItem(100) { t.Error("Item 100 should be found") } if !collection.NeedsItem(101) { t.Error("Item 101 should not be found") } // Check completed collection collection2 := pl.GetCollection(2) if collection2 == nil { t.Fatal("Collection 2 not found") } if !collection2.GetCompleted() { t.Error("Collection 2 should be completed") } } func TestPlayerCollectionListAddItem(t *testing.T) { db := NewMockCollectionDatabase() masterList := NewMasterCollectionList(db) // Set up collections db.collections = []CollectionData{ {ID: 1, Name: "Collection 1", Category: "Category A", Level: 10}, } db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, {ItemID: 101, Index: 1, Found: ItemNotFound}, } // Set up item lookup itemLookup := NewMockItemLookup() itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"} masterList.Initialize(context.Background(), itemLookup) pl := NewPlayerCollectionList(1001, db) // Try to add collection item directly - this will depend on the actual API // For now, just test basic functionality if pl.GetCharacterID() != 1001 { t.Errorf("Expected character ID 1001, got %d", pl.GetCharacterID()) } // Load collections first err := pl.Initialize(context.Background(), masterList) if err != nil { t.Fatalf("Failed to load player collections: %v", err) } } // Tests for CollectionManager func TestNewCollectionManager(t *testing.T) { db := NewMockCollectionDatabase() itemLookup := NewMockItemLookup() cm := NewCollectionManager(db, itemLookup) if cm == nil { t.Fatal("Failed to create CollectionManager") } } func TestCollectionManagerInitialize(t *testing.T) { db := NewMockCollectionDatabase() itemLookup := NewMockItemLookup() // Add test data db.collections = []CollectionData{ {ID: 1, Name: "Collection 1", Category: "Category A", Level: 10}, } db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, } // Set up item lookup itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} cm := NewCollectionManager(db, itemLookup) err := cm.Initialize(context.Background()) if err != nil { t.Fatalf("Failed to initialize: %v", err) } // Verify master list loaded stats := cm.GetSystemStatistics() if stats.TotalCollections != 1 { t.Errorf("Expected 1 collection, got %d", stats.TotalCollections) } } func TestCollectionManagerPlayerOperations(t *testing.T) { db := NewMockCollectionDatabase() itemLookup := NewMockItemLookup() // Add test data db.collections = []CollectionData{ {ID: 1, Name: "Collection 1", Category: "Category A", Level: 10}, } db.collectionItems[1] = []CollectionItem{ {ItemID: 100, Index: 0, Found: ItemNotFound}, {ItemID: 101, Index: 1, Found: ItemNotFound}, } // Set up item lookup itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"} itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"} cm := NewCollectionManager(db, itemLookup) err := cm.Initialize(context.Background()) if err != nil { t.Fatalf("Failed to initialize: %v", err) } // Verify statistics stats := cm.GetSystemStatistics() if stats.TotalCollections != 1 { t.Errorf("Expected 1 collection, got %d", stats.TotalCollections) } // Get collection copy collection := cm.GetCollectionCopy(1) if collection == nil { t.Fatal("Failed to get collection copy") } if collection.GetName() != "Collection 1" { t.Errorf("Expected name 'Collection 1', got %s", collection.GetName()) } } // Note: Database integration tests were removed to eliminate dependency on internal/database wrapper. // Database functionality is tested through unit tests with mocks above. // Tests for EntityCollectionAdapter // Mock entity for testing type mockEntity struct { id int32 } func (m *mockEntity) GetID() int32 { return m.id } func TestEntityCollectionAdapter(t *testing.T) { entity := &mockEntity{id: 1001} // Mock player manager pm := NewMockPlayerManager() pm.players[1001] = PlayerInfo{ CharacterID: 1001, Level: 50, } adapter := &EntityCollectionAdapter{ entity: entity, playerManager: pm, } if adapter.GetCharacterID() != 1001 { t.Errorf("Expected character ID 1001, got %d", adapter.GetCharacterID()) } if adapter.GetLevel() != 50 { t.Errorf("Expected level 50, got %d", adapter.GetLevel()) } // Test HasItem (should return false as placeholder) if adapter.HasItem(100) { t.Error("HasItem should return false") } } // Concurrency tests func TestCollectionConcurrency(t *testing.T) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) // Add items for i := 0; i < 100; i++ { c.AddCollectionItem(CollectionItem{ItemID: int32(i), Index: int8(i), Found: ItemNotFound}) } // Concurrent reads and writes var wg sync.WaitGroup errors := make(chan error, 100) // Writers for i := 0; i < 50; i++ { wg.Add(1) go func(itemID int32) { defer wg.Done() c.MarkItemFound(itemID) }(int32(i)) } // Readers for i := 0; i < 50; i++ { wg.Add(1) go func() { defer wg.Done() _ = c.GetProgress() _ = c.GetCollectionItems() _ = c.GetIsReadyToTurnIn() }() } wg.Wait() close(errors) // Check for errors for err := range errors { if err != nil { t.Errorf("Concurrent operation error: %v", err) } } } func TestMasterCollectionListConcurrency(t *testing.T) { db := NewMockCollectionDatabase() // Add test data for i := 1; i <= 100; i++ { db.collections = append(db.collections, CollectionData{ ID: int32(i), Name: fmt.Sprintf("Collection %d", i), Category: fmt.Sprintf("Category %d", i%10), Level: int8(i % 50), }) // Add at least one item per collection db.collectionItems[int32(i)] = []CollectionItem{ {ItemID: int32(i * 100), Index: 0, Found: ItemNotFound}, } } // Set up item lookup for all items itemLookup := NewMockItemLookup() for i := 1; i <= 100; i++ { itemLookup.items[int32(i*100)] = ItemInfo{ID: int32(i * 100), Name: fmt.Sprintf("Item %d", i)} } ml := NewMasterCollectionList(db) ml.Initialize(context.Background(), itemLookup) var wg sync.WaitGroup // Concurrent reads for i := 0; i < 100; i++ { wg.Add(1) go func(id int32) { defer wg.Done() _ = ml.GetCollection(id) _ = ml.FindCollectionsByName("Collection 1") _ = ml.GetCollectionsByCategory("Category 1") _ = ml.GetAllCollections() }(int32(i + 1)) } wg.Wait() } // Benchmarks func BenchmarkCollectionMarkItemFound(b *testing.B) { c := NewCollection() c.SetID(1) c.SetName("Test") c.SetCategory("Category") c.SetLevel(1) // Add items for i := 0; i < 100; i++ { c.AddCollectionItem(CollectionItem{ItemID: int32(i), Index: int8(i), Found: ItemNotFound}) } b.ResetTimer() for i := 0; i < b.N; i++ { c.MarkItemFound(int32(i % 100)) } } func BenchmarkMasterCollectionListSearch(b *testing.B) { db := NewMockCollectionDatabase() // Add test data with diverse names for i := 1; i <= 1000; i++ { db.collections = append(db.collections, CollectionData{ ID: int32(i), Name: fmt.Sprintf("Collection %d", i), Category: fmt.Sprintf("Category %d", i%10), Level: int8(i % 50), }) // Add at least one item per collection db.collectionItems[int32(i)] = []CollectionItem{ {ItemID: int32(i * 100), Index: 0, Found: ItemNotFound}, } } // Set up item lookup for all items itemLookup := NewMockItemLookup() for i := 1; i <= 1000; i++ { itemLookup.items[int32(i*100)] = ItemInfo{ID: int32(i * 100), Name: fmt.Sprintf("Item %d", i)} } ml := NewMasterCollectionList(db) ml.Initialize(context.Background(), itemLookup) // Test with various search patterns searches := []string{ "Collection 500", // Exact match - 1 result "Collection 5", // Prefix match - ~111 results (5, 50-59, 150-159, etc.) "NonExistent", // No matches - 0 results } b.ResetTimer() for i := 0; i < b.N; i++ { search := searches[i%len(searches)] _ = ml.FindCollectionsByName(search) } } func BenchmarkPlayerCollectionListLoad(b *testing.B) { db := NewMockCollectionDatabase() masterList := NewMasterCollectionList(db) // Set up collections for i := 1; i <= 100; i++ { db.collections = append(db.collections, CollectionData{ ID: int32(i), Name: fmt.Sprintf("Collection %d", i), Category: "Category", Level: 10, }) items := make([]CollectionItem, 10) for j := 0; j < 10; j++ { items[j] = CollectionItem{ ItemID: int32(i*100 + j), Index: int8(j), Found: ItemNotFound, } } db.collectionItems[int32(i)] = items } // Set up item lookup for all items itemLookup := NewMockItemLookup() for i := 1; i <= 100; i++ { for j := 0; j < 10; j++ { itemID := int32(i*100 + j) itemLookup.items[itemID] = ItemInfo{ID: itemID, Name: fmt.Sprintf("Item %d", itemID)} } } masterList.Initialize(context.Background(), itemLookup) b.ResetTimer() for i := 0; i < b.N; i++ { pl := NewPlayerCollectionList(1001, db) pl.Initialize(context.Background(), masterList) } } // Test constants func TestConstants(t *testing.T) { // Test reward types if RewardTypeItem != "Item" { t.Errorf("Expected RewardTypeItem to be 'Item', got %s", RewardTypeItem) } if RewardTypeSelectable != "Selectable" { t.Errorf("Expected RewardTypeSelectable to be 'Selectable', got %s", RewardTypeSelectable) } if RewardTypeCoin != "Coin" { t.Errorf("Expected RewardTypeCoin to be 'Coin', got %s", RewardTypeCoin) } if RewardTypeXP != "XP" { t.Errorf("Expected RewardTypeXP to be 'XP', got %s", RewardTypeXP) } // Test item states if ItemNotFound != 0 { t.Errorf("Expected ItemNotFound to be 0, got %d", ItemNotFound) } if ItemFound != 1 { t.Errorf("Expected ItemFound to be 1, got %d", ItemFound) } // Test collection states if CollectionIncomplete != false { t.Error("Expected CollectionIncomplete to be false") } if CollectionCompleted != true { t.Error("Expected CollectionCompleted to be true") } }