eq2go/internal/common/master_list_test.go

305 lines
6.8 KiB
Go

package common
import (
"fmt"
"testing"
)
// TestItem implements Identifiable for testing
type TestItem struct {
ID int32 `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
}
func (t *TestItem) GetID() int32 {
return t.ID
}
// TestMasterList tests the basic functionality of the generic master list
func TestMasterList(t *testing.T) {
ml := NewMasterList[int32, *TestItem]()
// Test initial state
if !ml.IsEmpty() {
t.Error("New master list should be empty")
}
if ml.Size() != 0 {
t.Error("New master list should have size 0")
}
// Test adding items
item1 := &TestItem{ID: 1, Name: "Item One", Category: "A"}
item2 := &TestItem{ID: 2, Name: "Item Two", Category: "B"}
item3 := &TestItem{ID: 3, Name: "Item Three", Category: "A"}
if !ml.Add(item1) {
t.Error("Should successfully add item1")
}
if !ml.Add(item2) {
t.Error("Should successfully add item2")
}
if !ml.Add(item3) {
t.Error("Should successfully add item3")
}
// Test duplicate addition
if ml.Add(item1) {
t.Error("Should not add duplicate item")
}
// Test size
if ml.Size() != 3 {
t.Errorf("Expected size 3, got %d", ml.Size())
}
if ml.IsEmpty() {
t.Error("List should not be empty")
}
// Test retrieval
retrieved := ml.Get(1)
if retrieved == nil || retrieved.Name != "Item One" {
t.Error("Failed to retrieve item1")
}
// Test safe retrieval
retrievedSafe, exists := ml.GetSafe(1)
if !exists || retrievedSafe.Name != "Item One" {
t.Error("Failed to safely retrieve item1")
}
_, exists = ml.GetSafe(999)
if exists {
t.Error("Should not find non-existent item")
}
// Test existence
if !ml.Exists(1) {
t.Error("Item 1 should exist")
}
if ml.Exists(999) {
t.Error("Item 999 should not exist")
}
// Test update
updatedItem := &TestItem{ID: 1, Name: "Updated Item One", Category: "A"}
if err := ml.Update(updatedItem); err != nil {
t.Errorf("Should successfully update item: %v", err)
}
retrieved = ml.Get(1)
if retrieved.Name != "Updated Item One" {
t.Error("Item was not updated correctly")
}
// Test update non-existent item
nonExistent := &TestItem{ID: 999, Name: "Non Existent", Category: "Z"}
if err := ml.Update(nonExistent); err == nil {
t.Error("Should fail to update non-existent item")
}
// Test AddOrUpdate
newItem := &TestItem{ID: 4, Name: "Item Four", Category: "C"}
if !ml.AddOrUpdate(newItem) {
t.Error("Should successfully add new item with AddOrUpdate")
}
updateExisting := &TestItem{ID: 1, Name: "Double Updated Item One", Category: "A"}
if !ml.AddOrUpdate(updateExisting) {
t.Error("Should successfully update existing item with AddOrUpdate")
}
retrieved = ml.Get(1)
if retrieved.Name != "Double Updated Item One" {
t.Error("Item was not updated correctly with AddOrUpdate")
}
if ml.Size() != 4 {
t.Errorf("Expected size 4 after AddOrUpdate, got %d", ml.Size())
}
// Test removal
if !ml.Remove(2) {
t.Error("Should successfully remove item2")
}
if ml.Remove(2) {
t.Error("Should not remove already removed item")
}
if ml.Size() != 3 {
t.Errorf("Expected size 3 after removal, got %d", ml.Size())
}
// Test GetAll
all := ml.GetAll()
if len(all) != 3 {
t.Errorf("Expected 3 items in GetAll, got %d", len(all))
}
// Verify we can modify the returned map without affecting the original
all[999] = &TestItem{ID: 999, Name: "Should not affect original", Category: "Z"}
if ml.Exists(999) {
t.Error("Modifying returned map should not affect original list")
}
// Test GetAllSlice
slice := ml.GetAllSlice()
if len(slice) != 3 {
t.Errorf("Expected 3 items in GetAllSlice, got %d", len(slice))
}
// Test GetAllIDs
ids := ml.GetAllIDs()
if len(ids) != 3 {
t.Errorf("Expected 3 IDs in GetAllIDs, got %d", len(ids))
}
// Test Clear
ml.Clear()
if !ml.IsEmpty() {
t.Error("List should be empty after Clear")
}
if ml.Size() != 0 {
t.Error("List should have size 0 after Clear")
}
}
// TestMasterListSearch tests search functionality
func TestMasterListSearch(t *testing.T) {
ml := NewMasterList[int32, *TestItem]()
// Add test items
items := []*TestItem{
{ID: 1, Name: "Alpha", Category: "A"},
{ID: 2, Name: "Beta", Category: "B"},
{ID: 3, Name: "Gamma", Category: "A"},
{ID: 4, Name: "Delta", Category: "C"},
{ID: 5, Name: "Alpha Two", Category: "A"},
}
for _, item := range items {
ml.Add(item)
}
// Test Filter
categoryA := ml.Filter(func(item *TestItem) bool {
return item.Category == "A"
})
if len(categoryA) != 3 {
t.Errorf("Expected 3 items in category A, got %d", len(categoryA))
}
// Test Find
found, exists := ml.Find(func(item *TestItem) bool {
return item.Name == "Beta"
})
if !exists || found.ID != 2 {
t.Error("Should find Beta with ID 2")
}
notFound, exists := ml.Find(func(item *TestItem) bool {
return item.Name == "Nonexistent"
})
if exists || notFound != nil {
t.Error("Should not find nonexistent item")
}
// Test Count
count := ml.Count(func(item *TestItem) bool {
return item.Category == "A"
})
if count != 3 {
t.Errorf("Expected count of 3 for category A, got %d", count)
}
// Test ForEach
var visitedIDs []int32
ml.ForEach(func(id int32, item *TestItem) {
visitedIDs = append(visitedIDs, id)
})
if len(visitedIDs) != 5 {
t.Errorf("Expected to visit 5 items, visited %d", len(visitedIDs))
}
}
// TestMasterListConcurrency tests thread safety (basic test)
func TestMasterListConcurrency(t *testing.T) {
ml := NewMasterList[int32, *TestItem]()
// Test WithReadLock
ml.Add(&TestItem{ID: 1, Name: "Test", Category: "A"})
var foundItem *TestItem
ml.WithReadLock(func(items map[int32]*TestItem) {
foundItem = items[1]
})
if foundItem == nil || foundItem.Name != "Test" {
t.Error("WithReadLock should provide access to internal map")
}
// Test WithWriteLock
ml.WithWriteLock(func(items map[int32]*TestItem) {
items[2] = &TestItem{ID: 2, Name: "Added via WriteLock", Category: "B"}
})
if !ml.Exists(2) {
t.Error("Item added via WithWriteLock should exist")
}
retrieved := ml.Get(2)
if retrieved.Name != "Added via WriteLock" {
t.Error("Item added via WithWriteLock not found correctly")
}
}
// BenchmarkMasterList tests performance of basic operations
func BenchmarkMasterList(b *testing.B) {
ml := NewMasterList[int32, *TestItem]()
// Pre-populate for benchmarks
for i := int32(0); i < 1000; i++ {
ml.Add(&TestItem{
ID: i,
Name: fmt.Sprintf("Item %d", i),
Category: fmt.Sprintf("Category %d", i%10),
})
}
b.Run("Get", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ml.Get(int32(i % 1000))
}
})
b.Run("Add", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ml.AddOrUpdate(&TestItem{
ID: int32(1000 + i),
Name: fmt.Sprintf("Bench Item %d", i),
Category: "Bench",
})
}
})
b.Run("Filter", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ml.Filter(func(item *TestItem) bool {
return item.Category == "Category 5"
})
}
})
}