573 lines
18 KiB
Go
573 lines
18 KiB
Go
package collections
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
func TestNewMasterList(t *testing.T) {
|
|
masterList := NewMasterList()
|
|
|
|
if masterList == nil {
|
|
t.Fatal("NewMasterList returned nil")
|
|
}
|
|
|
|
if masterList.GetCollectionCount() != 0 {
|
|
t.Errorf("Expected count 0, got %d", masterList.GetCollectionCount())
|
|
}
|
|
}
|
|
|
|
func TestMasterListBasicOperations(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Create test collections
|
|
collection1 := NewWithData(1001, "Heritage Collection", "Heritage", 20, db)
|
|
collection2 := NewWithData(1002, "Treasured Collection", "Treasured", 30, db)
|
|
|
|
// Test adding
|
|
if !masterList.AddCollection(collection1) {
|
|
t.Error("Should successfully add collection1")
|
|
}
|
|
|
|
if !masterList.AddCollection(collection2) {
|
|
t.Error("Should successfully add collection2")
|
|
}
|
|
|
|
// Test duplicate add (should fail)
|
|
if masterList.AddCollection(collection1) {
|
|
t.Error("Should not add duplicate collection")
|
|
}
|
|
|
|
if masterList.GetCollectionCount() != 2 {
|
|
t.Errorf("Expected count 2, got %d", masterList.GetCollectionCount())
|
|
}
|
|
|
|
// Test retrieving
|
|
retrieved := masterList.GetCollection(1001)
|
|
if retrieved == nil {
|
|
t.Error("Should retrieve added collection")
|
|
}
|
|
|
|
if retrieved.GetName() != "Heritage Collection" {
|
|
t.Errorf("Expected name 'Heritage Collection', got '%s'", retrieved.GetName())
|
|
}
|
|
|
|
// Test safe retrieval
|
|
retrieved, exists := masterList.GetCollectionSafe(1001)
|
|
if !exists || retrieved == nil {
|
|
t.Error("GetCollectionSafe should return collection and true")
|
|
}
|
|
|
|
_, exists = masterList.GetCollectionSafe(9999)
|
|
if exists {
|
|
t.Error("GetCollectionSafe should return false for non-existent ID")
|
|
}
|
|
|
|
// Test HasCollection
|
|
if !masterList.HasCollection(1001) {
|
|
t.Error("HasCollection should return true for existing ID")
|
|
}
|
|
|
|
if masterList.HasCollection(9999) {
|
|
t.Error("HasCollection should return false for non-existent ID")
|
|
}
|
|
|
|
// Test removing
|
|
if !masterList.RemoveCollection(1001) {
|
|
t.Error("Should successfully remove collection")
|
|
}
|
|
|
|
if masterList.GetCollectionCount() != 1 {
|
|
t.Errorf("Expected count 1, got %d", masterList.GetCollectionCount())
|
|
}
|
|
|
|
if masterList.HasCollection(1001) {
|
|
t.Error("Collection should be removed")
|
|
}
|
|
|
|
// Test clear
|
|
masterList.ClearCollections()
|
|
if masterList.GetCollectionCount() != 0 {
|
|
t.Errorf("Expected count 0 after clear, got %d", masterList.GetCollectionCount())
|
|
}
|
|
}
|
|
|
|
func TestMasterListItemNeeds(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Create collections with items
|
|
collection1 := NewWithData(1001, "Heritage Collection", "Heritage", 20, db)
|
|
collection1.CollectionItems = append(collection1.CollectionItems, CollectionItem{
|
|
ItemID: 12345,
|
|
Index: 0,
|
|
Found: ItemNotFound,
|
|
})
|
|
collection1.CollectionItems = append(collection1.CollectionItems, CollectionItem{
|
|
ItemID: 12346,
|
|
Index: 1,
|
|
Found: ItemFound, // Already found
|
|
})
|
|
|
|
collection2 := NewWithData(1002, "Treasured Collection", "Treasured", 30, db)
|
|
collection2.CollectionItems = append(collection2.CollectionItems, CollectionItem{
|
|
ItemID: 12347,
|
|
Index: 0,
|
|
Found: ItemNotFound,
|
|
})
|
|
|
|
masterList.AddCollection(collection1)
|
|
masterList.AddCollection(collection2)
|
|
|
|
// Test NeedsItem
|
|
if !masterList.NeedsItem(12345) {
|
|
t.Error("MasterList should need item 12345")
|
|
}
|
|
|
|
if masterList.NeedsItem(12346) {
|
|
t.Error("MasterList should not need item 12346 (already found)")
|
|
}
|
|
|
|
if !masterList.NeedsItem(12347) {
|
|
t.Error("MasterList should need item 12347")
|
|
}
|
|
|
|
if masterList.NeedsItem(99999) {
|
|
t.Error("MasterList should not need item 99999")
|
|
}
|
|
|
|
// Test GetCollectionsNeedingItem
|
|
needingItem := masterList.GetCollectionsNeedingItem(12345)
|
|
if len(needingItem) != 1 {
|
|
t.Errorf("Expected 1 collection needing item 12345, got %d", len(needingItem))
|
|
}
|
|
|
|
needingNone := masterList.GetCollectionsNeedingItem(99999)
|
|
if len(needingNone) != 0 {
|
|
t.Errorf("Expected 0 collections needing item 99999, got %d", len(needingNone))
|
|
}
|
|
}
|
|
|
|
func TestMasterListFiltering(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add test collections
|
|
collections := []*Collection{
|
|
NewWithData(1, "Heritage 1", "Heritage", 10, db),
|
|
NewWithData(2, "Heritage 2", "Heritage", 20, db),
|
|
NewWithData(3, "Treasured 1", "Treasured", 15, db),
|
|
NewWithData(4, "Treasured 2", "Treasured", 25, db),
|
|
NewWithData(5, "Legendary 1", "Legendary", 30, db),
|
|
}
|
|
|
|
for _, collection := range collections {
|
|
masterList.AddCollection(collection)
|
|
}
|
|
|
|
// Test FindCollectionsByCategory
|
|
heritageCollections := masterList.FindCollectionsByCategory("Heritage")
|
|
if len(heritageCollections) != 2 {
|
|
t.Errorf("FindCollectionsByCategory('Heritage') returned %v results, want 2", len(heritageCollections))
|
|
}
|
|
|
|
treasuredCollections := masterList.FindCollectionsByCategory("Treasured")
|
|
if len(treasuredCollections) != 2 {
|
|
t.Errorf("FindCollectionsByCategory('Treasured') returned %v results, want 2", len(treasuredCollections))
|
|
}
|
|
|
|
// Test FindCollectionsByLevel
|
|
lowLevel := masterList.FindCollectionsByLevel(10, 15)
|
|
if len(lowLevel) != 2 {
|
|
t.Errorf("FindCollectionsByLevel(10, 15) returned %v results, want 2", len(lowLevel))
|
|
}
|
|
|
|
midLevel := masterList.FindCollectionsByLevel(20, 25)
|
|
if len(midLevel) != 2 {
|
|
t.Errorf("FindCollectionsByLevel(20, 25) returned %v results, want 2", len(midLevel))
|
|
}
|
|
|
|
highLevel := masterList.FindCollectionsByLevel(30, 40)
|
|
if len(highLevel) != 1 {
|
|
t.Errorf("FindCollectionsByLevel(30, 40) returned %v results, want 1", len(highLevel))
|
|
}
|
|
}
|
|
|
|
func TestMasterListCategories(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add collections with different categories
|
|
masterList.AddCollection(NewWithData(1, "Test1", "Heritage", 10, db))
|
|
masterList.AddCollection(NewWithData(2, "Test2", "Heritage", 20, db))
|
|
masterList.AddCollection(NewWithData(3, "Test3", "Treasured", 15, db))
|
|
masterList.AddCollection(NewWithData(4, "Test4", "Legendary", 30, db))
|
|
|
|
categories := masterList.GetCategories()
|
|
|
|
expectedCategories := []string{"Heritage", "Treasured", "Legendary"}
|
|
if len(categories) != len(expectedCategories) {
|
|
t.Errorf("Expected %d categories, got %d", len(expectedCategories), len(categories))
|
|
}
|
|
|
|
// Check that all expected categories are present
|
|
categoryMap := make(map[string]bool)
|
|
for _, category := range categories {
|
|
categoryMap[category] = true
|
|
}
|
|
|
|
for _, expected := range expectedCategories {
|
|
if !categoryMap[expected] {
|
|
t.Errorf("Expected category '%s' not found", expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMasterListGetAll(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add test collections
|
|
for i := int32(1); i <= 3; i++ {
|
|
collection := NewWithData(i*100, "Test", "Heritage", 20, db)
|
|
masterList.AddCollection(collection)
|
|
}
|
|
|
|
// Test GetAllCollections (map)
|
|
allMap := masterList.GetAllCollections()
|
|
if len(allMap) != 3 {
|
|
t.Errorf("GetAllCollections() returned %v items, want 3", len(allMap))
|
|
}
|
|
|
|
// Verify it's a copy by modifying returned map
|
|
delete(allMap, 100)
|
|
if masterList.GetCollectionCount() != 3 {
|
|
t.Error("Modifying returned map affected internal state")
|
|
}
|
|
|
|
// Test GetAllCollectionsList (slice)
|
|
allList := masterList.GetAllCollectionsList()
|
|
if len(allList) != 3 {
|
|
t.Errorf("GetAllCollectionsList() returned %v items, want 3", len(allList))
|
|
}
|
|
}
|
|
|
|
func TestMasterListValidation(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add valid collection
|
|
collection1 := NewWithData(100, "Valid Collection", "Heritage", 20, db)
|
|
collection1.CollectionItems = append(collection1.CollectionItems, CollectionItem{
|
|
ItemID: 12345,
|
|
Index: 0,
|
|
Found: ItemNotFound,
|
|
})
|
|
masterList.AddCollection(collection1)
|
|
|
|
issues := masterList.ValidateCollections()
|
|
if len(issues) != 0 {
|
|
t.Errorf("ValidateCollections() returned issues for valid data: %v", issues)
|
|
}
|
|
|
|
if !masterList.IsValid() {
|
|
t.Error("IsValid() should return true for valid data")
|
|
}
|
|
|
|
// Add invalid collection (empty name)
|
|
collection2 := NewWithData(200, "", "Heritage", 20, db)
|
|
masterList.AddCollection(collection2)
|
|
|
|
issues = masterList.ValidateCollections()
|
|
if len(issues) == 0 {
|
|
t.Error("ValidateCollections() should return issues for invalid data")
|
|
}
|
|
|
|
if masterList.IsValid() {
|
|
t.Error("IsValid() should return false for invalid data")
|
|
}
|
|
}
|
|
|
|
func TestMasterListStatistics(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add collections with different categories and levels
|
|
collection1 := NewWithData(10, "Heritage1", "Heritage", 10, db)
|
|
collection1.CollectionItems = append(collection1.CollectionItems, CollectionItem{ItemID: 1, Index: 0, Found: 0})
|
|
collection1.CollectionItems = append(collection1.CollectionItems, CollectionItem{ItemID: 2, Index: 1, Found: 0})
|
|
collection1.RewardItems = append(collection1.RewardItems, CollectionRewardItem{ItemID: 1001, Quantity: 1})
|
|
|
|
collection2 := NewWithData(20, "Heritage2", "Heritage", 20, db)
|
|
collection2.CollectionItems = append(collection2.CollectionItems, CollectionItem{ItemID: 3, Index: 0, Found: 0})
|
|
collection2.RewardItems = append(collection2.RewardItems, CollectionRewardItem{ItemID: 1002, Quantity: 1})
|
|
collection2.SelectableRewardItems = append(collection2.SelectableRewardItems, CollectionRewardItem{ItemID: 1003, Quantity: 1})
|
|
|
|
collection3 := NewWithData(30, "Treasured1", "Treasured", 30, db)
|
|
collection3.CollectionItems = append(collection3.CollectionItems, CollectionItem{ItemID: 4, Index: 0, Found: 0})
|
|
|
|
masterList.AddCollection(collection1)
|
|
masterList.AddCollection(collection2)
|
|
masterList.AddCollection(collection3)
|
|
|
|
stats := masterList.GetStatistics()
|
|
|
|
if total, ok := stats["total_collections"].(int); !ok || total != 3 {
|
|
t.Errorf("total_collections = %v, want 3", stats["total_collections"])
|
|
}
|
|
|
|
if totalItems, ok := stats["total_collection_items"].(int); !ok || totalItems != 4 {
|
|
t.Errorf("total_collection_items = %v, want 4", stats["total_collection_items"])
|
|
}
|
|
|
|
if totalRewards, ok := stats["total_rewards"].(int); !ok || totalRewards != 3 {
|
|
t.Errorf("total_rewards = %v, want 3", stats["total_rewards"])
|
|
}
|
|
|
|
if minLevel, ok := stats["min_level"].(int8); !ok || minLevel != 10 {
|
|
t.Errorf("min_level = %v, want 10", stats["min_level"])
|
|
}
|
|
|
|
if maxLevel, ok := stats["max_level"].(int8); !ok || maxLevel != 30 {
|
|
t.Errorf("max_level = %v, want 30", stats["max_level"])
|
|
}
|
|
|
|
if categoryCounts, ok := stats["collections_by_category"].(map[string]int); ok {
|
|
if categoryCounts["Heritage"] != 2 {
|
|
t.Errorf("Heritage collections = %v, want 2", categoryCounts["Heritage"])
|
|
}
|
|
if categoryCounts["Treasured"] != 1 {
|
|
t.Errorf("Treasured collections = %v, want 1", categoryCounts["Treasured"])
|
|
}
|
|
} else {
|
|
t.Error("collections_by_category not found in statistics")
|
|
}
|
|
|
|
if avgItems, ok := stats["average_items_per_collection"].(float64); !ok || avgItems != float64(4)/3 {
|
|
t.Errorf("average_items_per_collection = %v, want %v", avgItems, float64(4)/3)
|
|
}
|
|
}
|
|
|
|
func TestMasterListBespokeFeatures(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Create collections with different properties
|
|
col1 := NewWithData(101, "Heritage Quest", "Heritage", 10, db)
|
|
col1.CollectionItems = []CollectionItem{
|
|
{ItemID: 1001, Index: 0, Found: ItemNotFound},
|
|
{ItemID: 1002, Index: 1, Found: ItemFound},
|
|
}
|
|
col1.Completed = false
|
|
|
|
col2 := NewWithData(102, "Treasured Quest", "Treasured", 20, db)
|
|
col2.CollectionItems = []CollectionItem{
|
|
{ItemID: 1003, Index: 0, Found: ItemFound},
|
|
{ItemID: 1004, Index: 1, Found: ItemFound},
|
|
}
|
|
col2.Completed = true
|
|
|
|
col3 := NewWithData(103, "Legendary Quest", "Legendary", 10, db)
|
|
col3.CollectionItems = []CollectionItem{
|
|
{ItemID: 1001, Index: 0, Found: ItemNotFound}, // Same item as col1
|
|
}
|
|
col3.Completed = false
|
|
|
|
masterList.AddCollection(col1)
|
|
masterList.AddCollection(col2)
|
|
masterList.AddCollection(col3)
|
|
|
|
// Test GetCollectionsByExactLevel
|
|
level10Collections := masterList.GetCollectionsByExactLevel(10)
|
|
if len(level10Collections) != 2 {
|
|
t.Errorf("GetCollectionsByExactLevel(10) returned %v results, want 2", len(level10Collections))
|
|
}
|
|
|
|
level20Collections := masterList.GetCollectionsByExactLevel(20)
|
|
if len(level20Collections) != 1 {
|
|
t.Errorf("GetCollectionsByExactLevel(20) returned %v results, want 1", len(level20Collections))
|
|
}
|
|
|
|
// Test GetCollectionByName
|
|
found := masterList.GetCollectionByName("heritage quest")
|
|
if found == nil || found.ID != 101 {
|
|
t.Error("GetCollectionByName should find 'Heritage Quest' (case insensitive)")
|
|
}
|
|
|
|
found = masterList.GetCollectionByName("TREASURED QUEST")
|
|
if found == nil || found.ID != 102 {
|
|
t.Error("GetCollectionByName should find 'Treasured Quest' (uppercase)")
|
|
}
|
|
|
|
found = masterList.GetCollectionByName("NonExistent")
|
|
if found != nil {
|
|
t.Error("GetCollectionByName should return nil for non-existent collection")
|
|
}
|
|
|
|
// Test completion status filtering
|
|
completedCollections := masterList.GetCompletedCollections()
|
|
if len(completedCollections) != 1 {
|
|
t.Errorf("GetCompletedCollections() returned %v results, want 1", len(completedCollections))
|
|
}
|
|
|
|
incompleteCollections := masterList.GetIncompleteCollections()
|
|
if len(incompleteCollections) != 2 {
|
|
t.Errorf("GetIncompleteCollections() returned %v results, want 2", len(incompleteCollections))
|
|
}
|
|
|
|
// Test GetCollectionsNeedingItem (multiple collections need same item)
|
|
collectionsNeedingItem := masterList.GetCollectionsNeedingItem(1001)
|
|
if len(collectionsNeedingItem) != 2 {
|
|
t.Errorf("GetCollectionsNeedingItem(1001) returned %v results, want 2", len(collectionsNeedingItem))
|
|
}
|
|
|
|
// Test GetReadyToTurnInCollections
|
|
readyCollections := masterList.GetReadyToTurnInCollections()
|
|
if len(readyCollections) != 0 { // col1 has one item not found, col3 has one item not found
|
|
t.Errorf("GetReadyToTurnInCollections() returned %v results, want 0", len(readyCollections))
|
|
}
|
|
|
|
// Mark col1 as ready to turn in
|
|
col1.CollectionItems[0].Found = ItemFound
|
|
masterList.RefreshCollectionIndices(col1)
|
|
|
|
readyCollections = masterList.GetReadyToTurnInCollections()
|
|
if len(readyCollections) != 1 {
|
|
t.Errorf("GetReadyToTurnInCollections() returned %v results, want 1 after marking items found", len(readyCollections))
|
|
}
|
|
|
|
// Test UpdateCollection
|
|
updatedCol := &Collection{
|
|
ID: 101,
|
|
Name: "Updated Heritage Quest",
|
|
Category: "Updated",
|
|
Level: 25,
|
|
db: db,
|
|
isNew: false,
|
|
CollectionItems: []CollectionItem{
|
|
{ItemID: 2001, Index: 0, Found: ItemNotFound},
|
|
},
|
|
}
|
|
|
|
err := masterList.UpdateCollection(updatedCol)
|
|
if err != nil {
|
|
t.Errorf("UpdateCollection failed: %v", err)
|
|
}
|
|
|
|
// Verify the update worked
|
|
retrieved := masterList.GetCollection(101)
|
|
if retrieved.Name != "Updated Heritage Quest" {
|
|
t.Errorf("Expected updated name 'Updated Heritage Quest', got '%s'", retrieved.Name)
|
|
}
|
|
|
|
if retrieved.Category != "Updated" {
|
|
t.Errorf("Expected updated category 'Updated', got '%s'", retrieved.Category)
|
|
}
|
|
|
|
// Test updating non-existent collection
|
|
nonExistentCol := &Collection{ID: 9999, Name: "Non-existent", db: db}
|
|
err = masterList.UpdateCollection(nonExistentCol)
|
|
if err == nil {
|
|
t.Error("UpdateCollection should fail for non-existent collection")
|
|
}
|
|
|
|
// Test GetLevels and GetItemsNeeded
|
|
levels := masterList.GetLevels()
|
|
if len(levels) == 0 {
|
|
t.Error("GetLevels() should return levels")
|
|
}
|
|
|
|
itemsNeeded := masterList.GetItemsNeeded()
|
|
if len(itemsNeeded) == 0 {
|
|
t.Error("GetItemsNeeded() should return items needed")
|
|
}
|
|
|
|
// Test GetCollectionClone
|
|
cloned := masterList.GetCollectionClone(101)
|
|
if cloned == nil {
|
|
t.Error("GetCollectionClone should return a clone")
|
|
}
|
|
if cloned == retrieved {
|
|
t.Error("GetCollectionClone should return a different object")
|
|
}
|
|
}
|
|
|
|
func TestMasterListConcurrency(t *testing.T) {
|
|
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
defer db.Close()
|
|
|
|
masterList := NewMasterList()
|
|
|
|
// Add initial collections
|
|
for i := 1; i <= 100; i++ {
|
|
col := NewWithData(int32(i), fmt.Sprintf("Collection%d", i), "Heritage", 10, db)
|
|
col.CollectionItems = []CollectionItem{
|
|
{ItemID: int32(i + 1000), Index: 0, Found: ItemNotFound},
|
|
}
|
|
masterList.AddCollection(col)
|
|
}
|
|
|
|
// Test concurrent access
|
|
done := make(chan bool, 10)
|
|
|
|
// Concurrent readers
|
|
for i := 0; i < 5; i++ {
|
|
go func() {
|
|
defer func() { done <- true }()
|
|
for j := 0; j < 100; j++ {
|
|
masterList.GetCollection(int32(j%100 + 1))
|
|
masterList.FindCollectionsByCategory("Heritage")
|
|
masterList.GetCollectionByName(fmt.Sprintf("collection%d", j%100+1))
|
|
masterList.NeedsItem(int32(j + 1000))
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Concurrent writers
|
|
for i := 0; i < 5; i++ {
|
|
go func(workerID int) {
|
|
defer func() { done <- true }()
|
|
for j := 0; j < 10; j++ {
|
|
colID := int32(workerID*1000 + j + 1)
|
|
col := NewWithData(colID, fmt.Sprintf("Worker%d-Collection%d", workerID, j), "Treasured", 20, db)
|
|
col.CollectionItems = []CollectionItem{
|
|
{ItemID: colID + 10000, Index: 0, Found: ItemNotFound},
|
|
}
|
|
masterList.AddCollection(col) // Some may fail due to concurrent additions
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all goroutines
|
|
for i := 0; i < 10; i++ {
|
|
<-done
|
|
}
|
|
|
|
// Verify final state - should have at least 100 initial collections
|
|
finalCount := masterList.GetCollectionCount()
|
|
if finalCount < 100 {
|
|
t.Errorf("Expected at least 100 collections after concurrent operations, got %d", finalCount)
|
|
}
|
|
if finalCount > 150 {
|
|
t.Errorf("Expected at most 150 collections after concurrent operations, got %d", finalCount)
|
|
}
|
|
} |