1187 lines
30 KiB
Go
1187 lines
30 KiB
Go
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")
|
|
}
|
|
} |