package achievements import ( "sync" "testing" "eq2emu/internal/database" ) // TestSimpleAchievement tests the basic new Achievement functionality func TestSimpleAchievement(t *testing.T) { db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer db.Close() // Test creating a new achievement achievement := New(db) if achievement == nil { t.Fatal("New returned nil") } if !achievement.IsNew() { t.Error("New achievement should be marked as new") } // Test setting values achievement.AchievementID = 1001 achievement.Title = "Test Achievement" achievement.Category = "Testing" if achievement.GetID() != 1001 { t.Errorf("Expected GetID() to return 1001, got %d", achievement.GetID()) } // Test adding requirements and rewards achievement.AddRequirement("kill_monsters", 10) achievement.AddReward("experience:1000") if len(achievement.Requirements) != 1 { t.Errorf("Expected 1 requirement, got %d", len(achievement.Requirements)) } if len(achievement.Rewards) != 1 { t.Errorf("Expected 1 reward, got %d", len(achievement.Rewards)) } // Test Clone clone := achievement.Clone() if clone == nil { t.Fatal("Clone returned nil") } if clone.AchievementID != achievement.AchievementID { t.Errorf("Expected clone ID %d, got %d", achievement.AchievementID, clone.AchievementID) } if clone.Title != achievement.Title { t.Errorf("Expected clone title %s, got %s", achievement.Title, clone.Title) } } // TestMasterList tests the bespoke master list implementation func TestMasterList(t *testing.T) { masterList := NewMasterList() if masterList == nil { t.Fatal("NewMasterList returned nil") } if masterList.Size() != 0 { t.Errorf("Expected size 0, got %d", masterList.Size()) } // Create test database db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer db.Close() // Create achievements for testing achievement1 := New(db) achievement1.AchievementID = 1001 achievement1.Title = "Test Achievement 1" achievement1.Category = "Testing" achievement1.Expansion = "Classic" achievement2 := New(db) achievement2.AchievementID = 1002 achievement2.Title = "Test Achievement 2" achievement2.Category = "Combat" achievement2.Expansion = "Classic" achievement3 := New(db) achievement3.AchievementID = 1003 achievement3.Title = "Test Achievement 3" achievement3.Category = "Testing" achievement3.Expansion = "Expansion1" // Test adding if !masterList.AddAchievement(achievement1) { t.Error("Should successfully add achievement1") } if !masterList.AddAchievement(achievement2) { t.Error("Should successfully add achievement2") } if !masterList.AddAchievement(achievement3) { t.Error("Should successfully add achievement3") } if masterList.Size() != 3 { t.Errorf("Expected size 3, got %d", masterList.Size()) } // Test duplicate add (should fail) if masterList.AddAchievement(achievement1) { t.Error("Should not add duplicate achievement") } // Test retrieving retrieved := masterList.GetAchievement(1001) if retrieved == nil { t.Error("Should retrieve added achievement") } if retrieved.Title != "Test Achievement 1" { t.Errorf("Expected title 'Test Achievement 1', got '%s'", retrieved.Title) } // Test category filtering testingAchievements := masterList.GetAchievementsByCategory("Testing") if len(testingAchievements) != 2 { t.Errorf("Expected 2 achievements in Testing category, got %d", len(testingAchievements)) } combatAchievements := masterList.GetAchievementsByCategory("Combat") if len(combatAchievements) != 1 { t.Errorf("Expected 1 achievement in Combat category, got %d", len(combatAchievements)) } // Test expansion filtering classicAchievements := masterList.GetAchievementsByExpansion("Classic") if len(classicAchievements) != 2 { t.Errorf("Expected 2 achievements in Classic expansion, got %d", len(classicAchievements)) } expansion1Achievements := masterList.GetAchievementsByExpansion("Expansion1") if len(expansion1Achievements) != 1 { t.Errorf("Expected 1 achievement in Expansion1, got %d", len(expansion1Achievements)) } // Test combined filtering combined := masterList.GetAchievementsByCategoryAndExpansion("Testing", "Classic") if len(combined) != 1 { t.Errorf("Expected 1 achievement matching Testing+Classic, got %d", len(combined)) } // Test metadata caching categories := masterList.GetCategories() if len(categories) != 2 { t.Errorf("Expected 2 unique categories, got %d", len(categories)) } expansions := masterList.GetExpansions() if len(expansions) != 2 { t.Errorf("Expected 2 unique expansions, got %d", len(expansions)) } // Test clone clone := masterList.GetAchievementClone(1001) if clone == nil { t.Error("Should return cloned achievement") } if clone.Title != "Test Achievement 1" { t.Errorf("Expected cloned title 'Test Achievement 1', got '%s'", clone.Title) } // Test GetAllAchievements allAchievements := masterList.GetAllAchievements() if len(allAchievements) != 3 { t.Errorf("Expected 3 achievements in GetAll, got %d", len(allAchievements)) } // Test update updatedAchievement := New(db) updatedAchievement.AchievementID = 1001 updatedAchievement.Title = "Updated Achievement" updatedAchievement.Category = "Updated" updatedAchievement.Expansion = "Updated" if err := masterList.UpdateAchievement(updatedAchievement); err != nil { t.Errorf("Update should succeed: %v", err) } // Verify update worked retrievedUpdated := masterList.GetAchievement(1001) if retrievedUpdated.Title != "Updated Achievement" { t.Errorf("Expected updated title 'Updated Achievement', got '%s'", retrievedUpdated.Title) } // Verify category index updated updatedCategoryAchievements := masterList.GetAchievementsByCategory("Updated") if len(updatedCategoryAchievements) != 1 { t.Errorf("Expected 1 achievement in Updated category, got %d", len(updatedCategoryAchievements)) } // Test removal if !masterList.RemoveAchievement(1001) { t.Error("Should successfully remove achievement") } if masterList.Size() != 2 { t.Errorf("Expected size 2 after removal, got %d", masterList.Size()) } // Test clear masterList.Clear() if masterList.Size() != 0 { t.Errorf("Expected size 0 after clear, got %d", masterList.Size()) } } // TestMasterListConcurrency tests thread safety of the master list func TestMasterListConcurrency(t *testing.T) { masterList := NewMasterList() // Create test database db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer db.Close() const numWorkers = 10 const achievementsPerWorker = 100 var wg sync.WaitGroup // Concurrently add achievements wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { go func(workerID int) { defer wg.Done() for j := 0; j < achievementsPerWorker; j++ { achievement := New(db) achievement.AchievementID = uint32(workerID*achievementsPerWorker + j + 1) achievement.Title = "Concurrent Test" achievement.Category = "Concurrency" achievement.Expansion = "Test" masterList.AddAchievement(achievement) } }(i) } // Concurrently read achievements wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { go func() { defer wg.Done() for j := 0; j < achievementsPerWorker; j++ { // Random reads _ = masterList.GetAchievement(uint32(j + 1)) _ = masterList.GetAchievementsByCategory("Concurrency") _ = masterList.GetAchievementsByExpansion("Test") _ = masterList.Size() } }() } wg.Wait() // Verify final state expectedSize := numWorkers * achievementsPerWorker if masterList.Size() != expectedSize { t.Errorf("Expected size %d, got %d", expectedSize, masterList.Size()) } categories := masterList.GetCategories() if len(categories) != 1 || categories[0] != "Concurrency" { t.Errorf("Expected 1 category 'Concurrency', got %v", categories) } }