package collections import ( "testing" ) // MockDatabase implements a simple in-memory database for testing type MockDatabase struct { collections map[int32]map[string]interface{} collectionItems map[int32][]map[string]interface{} collectionRewards map[int32][]map[string]interface{} characterCollections map[int32][]map[string]interface{} characterCollectionItems map[string][]int32 } func NewMockDatabase() *MockDatabase { return &MockDatabase{ collections: make(map[int32]map[string]interface{}), collectionItems: make(map[int32][]map[string]interface{}), collectionRewards: make(map[int32][]map[string]interface{}), characterCollections: make(map[int32][]map[string]interface{}), characterCollectionItems: make(map[string][]int32), } } // Mock database methods (simplified implementations for testing) func (db *MockDatabase) Query(query string, args ...interface{}) (interface{}, error) { // This is a simplified mock - in real tests you'd want more sophisticated query parsing return nil, nil } func (db *MockDatabase) QueryRow(query string, args ...interface{}) interface{} { return nil } func (db *MockDatabase) Exec(query string, args ...interface{}) (interface{}, error) { return nil, nil } func setupTestData(db *MockDatabase) { // Collection 1: Shiny Objects db.collections[1] = map[string]interface{}{ "id": 1, "collection_name": "Shiny Objects", "collection_category": "Common Collections", "level": 10, } db.collectionItems[1] = []map[string]interface{}{ {"item_id": 1001, "item_index": 0, "found": 0}, {"item_id": 1002, "item_index": 1, "found": 0}, {"item_id": 1003, "item_index": 2, "found": 0}, } db.collectionRewards[1] = []map[string]interface{}{ {"reward_type": "coin", "reward_value": "1000", "reward_quantity": 1}, {"reward_type": "xp", "reward_value": "500", "reward_quantity": 1}, } // Collection 2: Rare Gems db.collections[2] = map[string]interface{}{ "id": 2, "collection_name": "Rare Gems", "collection_category": "Valuable Collections", "level": 25, } db.collectionItems[2] = []map[string]interface{}{ {"item_id": 2001, "item_index": 0, "found": 0}, {"item_id": 2002, "item_index": 1, "found": 0}, } db.collectionRewards[2] = []map[string]interface{}{ {"reward_type": "item", "reward_value": "5001", "reward_quantity": 1}, } } func createTestManager() *Manager { db := NewMockDatabase() setupTestData(db) // For now, we'll create collections manually since we can't easily mock the database queries manager := NewManager(nil) // Pass nil for now since we're testing without real DB // Manually create test collections collection1 := NewWithData(1, "Shiny Objects", "Common Collections", 10, nil) collection1.CollectionItems = []CollectionItem{ {ItemID: 1001, Index: 0, Found: 0}, {ItemID: 1002, Index: 1, Found: 0}, {ItemID: 1003, Index: 2, Found: 0}, } collection1.RewardCoin = 1000 collection1.RewardXP = 500 collection2 := NewWithData(2, "Rare Gems", "Valuable Collections", 25, nil) collection2.CollectionItems = []CollectionItem{ {ItemID: 2001, Index: 0, Found: 0}, {ItemID: 2002, Index: 1, Found: 0}, } collection2.RewardItems = []CollectionRewardItem{ {ItemID: 5001, Quantity: 1}, } manager.AddCollection(collection1) manager.AddCollection(collection2) return manager } // Test Manager Creation and Initialization func TestManagerCreation(t *testing.T) { manager := NewManager(nil) if manager == nil { t.Fatal("NewManager returned nil") } if manager.masterList == nil { t.Error("Manager master list is nil") } if manager.Size() != 0 { t.Errorf("Expected empty manager, got size %d", manager.Size()) } // Check default configuration config := manager.GetConfiguration() if config["enable_caching"].(bool) != true { t.Error("Expected caching to be enabled by default") } if config["enable_statistics"].(bool) != true { t.Error("Expected statistics to be enabled by default") } if config["enable_validation"].(bool) != true { t.Error("Expected validation to be enabled by default") } } // Test Collection Operations func TestCollectionOperations(t *testing.T) { manager := createTestManager() // Test Size if manager.Size() != 2 { t.Errorf("Expected 2 collections, got %d", manager.Size()) } // Test GetCollection collection := manager.GetCollection(1) if collection == nil { t.Fatal("Failed to retrieve collection 1") } if collection.GetName() != "Shiny Objects" { t.Errorf("Expected 'Shiny Objects', got '%s'", collection.GetName()) } if collection.GetLevel() != 10 { t.Errorf("Expected level 10, got %d", collection.GetLevel()) } // Test non-existent collection nonExistent := manager.GetCollection(999) if nonExistent != nil { t.Error("Expected nil for non-existent collection") } // Test NeedsItem if !manager.NeedsItem(1001) { t.Error("Expected collection to need item 1001") } if manager.NeedsItem(9999) { t.Error("Expected no collection to need item 9999") } } // Test Collection Management func TestCollectionManagement(t *testing.T) { manager := createTestManager() // Test adding new collection newCollection := NewWithData(3, "Test Collection", "Test Category", 1, nil) newCollection.CollectionItems = []CollectionItem{ {ItemID: 3001, Index: 0, Found: 0}, } if !manager.AddCollection(newCollection) { t.Error("Failed to add new collection") } if manager.Size() != 3 { t.Errorf("Expected 3 collections after adding, got %d", manager.Size()) } // Test adding duplicate collection duplicate := NewWithData(3, "Duplicate", "Test", 1, nil) if manager.AddCollection(duplicate) { t.Error("Should not be able to add collection with duplicate ID") } // Test UpdateCollection newCollection.Name = "Updated Test Collection" err := manager.UpdateCollection(newCollection) if err != nil { t.Errorf("Failed to update collection: %v", err) } updated := manager.GetCollection(3) if updated.GetName() != "Updated Test Collection" { t.Error("Collection name was not updated") } } // Test Item Needs Detection func TestItemNeeds(t *testing.T) { manager := createTestManager() testCases := []struct { itemID int32 expected bool name string }{ {1001, true, "Collection 1 item 1"}, {1002, true, "Collection 1 item 2"}, {1003, true, "Collection 1 item 3"}, {2001, true, "Collection 2 item 1"}, {2002, true, "Collection 2 item 2"}, {9999, false, "Non-existent item"}, {0, false, "Invalid item ID"}, } for _, tc := range testCases { result := manager.NeedsItem(tc.itemID) if result != tc.expected { t.Errorf("%s: expected %v, got %v for item %d", tc.name, tc.expected, result, tc.itemID) } } } // Test Master List Operations func TestMasterListOperations(t *testing.T) { manager := createTestManager() masterList := manager.GetMasterList() if masterList == nil { t.Fatal("Master list is nil") } // Test getting all collections allCollections := masterList.GetAllCollections() if len(allCollections) != 2 { t.Errorf("Expected 2 collections in master list, got %d", len(allCollections)) } // Test collections by category commonCollections := masterList.FindCollectionsByCategory("Common Collections") if len(commonCollections) != 1 { t.Errorf("Expected 1 common collection, got %d", len(commonCollections)) } // Test collections by level level10Collections := masterList.GetCollectionsByExactLevel(10) if len(level10Collections) != 1 { t.Errorf("Expected 1 level 10 collection, got %d", len(level10Collections)) } levelRangeCollections := masterList.FindCollectionsByLevel(10, 30) if len(levelRangeCollections) != 2 { t.Errorf("Expected 2 collections in level range 10-30, got %d", len(levelRangeCollections)) } // Test collections needing specific item collectionsNeedingItem := masterList.GetCollectionsNeedingItem(1001) if len(collectionsNeedingItem) != 1 { t.Errorf("Expected 1 collection needing item 1001, got %d", len(collectionsNeedingItem)) } } // Test Player Collection Management func TestPlayerCollectionManagement(t *testing.T) { manager := createTestManager() // Create player list playerList := manager.CreatePlayerList(12345) if playerList == nil { t.Fatal("Failed to create player list") } if playerList.CharacterID != 12345 { t.Errorf("Expected character ID 12345, got %d", playerList.CharacterID) } if playerList.Size() != 0 { t.Errorf("Expected empty player list, got size %d", playerList.Size()) } // Add collection to player collection := manager.GetCollection(1).Clone() if !playerList.AddCollection(collection) { t.Error("Failed to add collection to player") } if playerList.Size() != 1 { t.Errorf("Expected 1 collection in player list, got %d", playerList.Size()) } // Test player collection operations if playerList.HasCollectionsToHandIn() { t.Error("Player should not have collections ready to turn in initially") } // Mark all items as found for i := range collection.CollectionItems { collection.CollectionItems[i].Found = 1 } if !playerList.HasCollectionsToHandIn() { t.Error("Player should have collections ready to turn in after finding all items") } readyCollections := playerList.GetCollectionsToHandIn() if len(readyCollections) != 1 { t.Errorf("Expected 1 collection ready to turn in, got %d", len(readyCollections)) } } // Test Packet Building (these would normally require packet definitions) func TestPacketBuilding(t *testing.T) { manager := createTestManager() // Since we don't have actual packet definitions loaded for testing, // these tests will expect errors, but we can test the code paths characterID := int32(12345) clientVersion := uint32(57048) // Test collection update packet - should fail without packet definitions playerCollections := make(map[int32]*Collection) playerCollections[1] = manager.GetCollection(1) _, err := manager.SendCollectionUpdate(characterID, clientVersion, playerCollections) if err == nil { t.Error("Expected error for missing packet definition, but got none") } // Test collection filter packet - should fail without packet definitions collectionsForItem := []*Collection{manager.GetCollection(1)} _, err = manager.SendCollectionFilter(characterID, clientVersion, 1001, collectionsForItem) if err == nil { t.Error("Expected error for missing packet definition, but got none") } // Test collection item packet - should fail without packet definitions _, err = manager.SendCollectionItem(characterID, clientVersion, 1, 1001) if err == nil { t.Error("Expected error for missing packet definition, but got none") } } // Test Statistics and Monitoring func TestStatisticsAndMonitoring(t *testing.T) { manager := createTestManager() // Perform some operations to generate statistics _ = manager.GetCollection(1) // Cache hit _ = manager.GetCollection(999) // Cache miss manager.NeedsItem(1001) manager.NeedsItem(9999) stats := manager.GetStatistics() if stats == nil { t.Fatal("Statistics returned nil") } // Check some expected statistics exist if _, exists := stats["collections_loaded"]; !exists { t.Error("Expected collections_loaded statistic") } if _, exists := stats["cache_hits"]; !exists { t.Error("Expected cache_hits statistic") } if _, exists := stats["cache_misses"]; !exists { t.Error("Expected cache_misses statistic") } if _, exists := stats["item_checks"]; !exists { t.Error("Expected item_checks statistic") } // Test reset statistics manager.ResetStatistics() newStats := manager.GetStatistics() if newStats["cache_hits"].(int64) != 0 { t.Error("Expected cache hits to be reset to 0") } } // Test Configuration Management func TestConfigurationManagement(t *testing.T) { manager := createTestManager() // Test default configuration config := manager.GetConfiguration() if !config["enable_caching"].(bool) { t.Error("Expected caching to be enabled by default") } // Test configuration update newConfig := map[string]interface{}{ "enable_caching": false, "enable_validation": false, "max_players_to_track": 500, } manager.SetConfiguration(newConfig) updatedConfig := manager.GetConfiguration() if updatedConfig["enable_caching"].(bool) { t.Error("Expected caching to be disabled after update") } if updatedConfig["enable_validation"].(bool) { t.Error("Expected validation to be disabled after update") } if updatedConfig["max_players_to_track"].(int) != 500 { t.Error("Expected max_players_to_track to be updated to 500") } } // Test Validation and Health Checks func TestValidationAndHealthChecks(t *testing.T) { manager := createTestManager() // Test health check - manager with nil db is not healthy by design if manager.IsHealthy() { t.Error("Manager with nil database should not be healthy") } // Test validation issues := manager.ValidateCollections() if len(issues) > 0 { t.Errorf("Expected no validation issues, got %d: %v", len(issues), issues) } // Add invalid collection to test validation invalidCollection := NewWithData(-1, "", "", -1, nil) manager.AddCollection(invalidCollection) validationIssues := manager.ValidateCollections() if len(validationIssues) == 0 { t.Error("Expected validation issues for invalid collection") } } // Test Thread Safety (basic test) func TestThreadSafety(t *testing.T) { manager := createTestManager() // Perform concurrent operations done := make(chan bool, 10) // Start multiple goroutines performing different operations for i := 0; i < 10; i++ { go func(id int) { defer func() { done <- true }() // Mix of read and write operations manager.GetCollection(1) manager.NeedsItem(1001) manager.GetStatistics() // Try to add a collection (most will fail due to duplicate IDs, which is expected) newCollection := NewWithData(int32(1000+id), "Test", "Category", 1, nil) manager.AddCollection(newCollection) }(i) } // Wait for all goroutines to complete for i := 0; i < 10; i++ { <-done } // Manager should still be functional if manager.Size() < 2 { // At least original 2 collections t.Error("Manager appears corrupted after concurrent access") } } // Test Collection Item Management func TestCollectionItemManagement(t *testing.T) { manager := createTestManager() collection := manager.GetCollection(1) if collection == nil { t.Fatal("Failed to get test collection") } // Test getting collection item by ID item := collection.GetCollectionItemByItemID(1001) if item == nil { t.Fatal("Failed to get collection item 1001") } if item.ItemID != 1001 { t.Errorf("Expected item ID 1001, got %d", item.ItemID) } // Test needs item if !collection.NeedsItem(1001) { t.Error("Collection should need item 1001") } // Test marking item found if !collection.MarkItemFound(1001) { t.Error("Failed to mark item 1001 as found") } // Test item is no longer needed if collection.NeedsItem(1001) { t.Error("Collection should not need item 1001 after marking as found") } // Test collection is not ready to turn in (still missing items) if collection.GetIsReadyToTurnIn() { t.Error("Collection should not be ready to turn in with only 1 of 3 items found") } // Mark all items as found collection.MarkItemFound(1002) collection.MarkItemFound(1003) // Test collection is now ready to turn in if !collection.GetIsReadyToTurnIn() { t.Error("Collection should be ready to turn in after finding all items") } // Test progress calculation progress := collection.GetProgress() if progress != 100.0 { t.Errorf("Expected 100%% progress, got %.2f%%", progress) } } // Test Collection Cloning func TestCollectionCloning(t *testing.T) { manager := createTestManager() original := manager.GetCollection(1) if original == nil { t.Fatal("Failed to get original collection") } // Clone the collection clone := original.Clone() if clone == nil { t.Fatal("Failed to clone collection") } // Verify clone has same data if clone.GetID() != original.GetID() { t.Error("Clone has different ID") } if clone.GetName() != original.GetName() { t.Error("Clone has different name") } if clone.GetCategory() != original.GetCategory() { t.Error("Clone has different category") } if clone.GetLevel() != original.GetLevel() { t.Error("Clone has different level") } // Verify clone is independent (modify clone) clone.Name = "Modified Clone" if original.GetName() == "Modified Clone" { t.Error("Modifying clone affected original") } // Verify collection items were cloned if len(clone.CollectionItems) != len(original.CollectionItems) { t.Error("Clone has different number of collection items") } // Modify clone items and verify original is unaffected if len(clone.CollectionItems) > 0 { clone.CollectionItems[0].Found = 1 if original.CollectionItems[0].Found == 1 { t.Error("Modifying clone item affected original") } } } // Test Edge Cases and Error Conditions func TestEdgeCases(t *testing.T) { manager := createTestManager() // Test nil operations if manager.AddCollection(nil) { t.Error("Should not be able to add nil collection") } err := manager.UpdateCollection(nil) if err == nil { t.Error("Should return error for nil collection update") } // Test invalid collection data invalidCollection := NewWithData(0, "", "", -5, nil) err = manager.UpdateCollection(invalidCollection) if err == nil { t.Error("Should return error for invalid collection data") } // Test empty manager operations emptyManager := NewManager(nil) if emptyManager.Size() != 0 { t.Error("Empty manager should have size 0") } if emptyManager.NeedsItem(1001) { t.Error("Empty manager should not need any items") } // Test clear collections manager.ClearCollections() if manager.Size() != 0 { t.Error("Manager should be empty after clearing collections") } // Test operations on cleared manager if manager.GetCollection(1) != nil { t.Error("Should return nil for collection after clearing") } if manager.NeedsItem(1001) { t.Error("Cleared manager should not need any items") } } // Benchmark tests func BenchmarkGetCollection(b *testing.B) { manager := createTestManager() b.ResetTimer() for i := 0; i < b.N; i++ { manager.GetCollection(1) } } func BenchmarkNeedsItem(b *testing.B) { manager := createTestManager() b.ResetTimer() for i := 0; i < b.N; i++ { manager.NeedsItem(1001) } } func BenchmarkGetStatistics(b *testing.B) { manager := createTestManager() b.ResetTimer() for i := 0; i < b.N; i++ { manager.GetStatistics() } }