862 lines
22 KiB
Go
862 lines
22 KiB
Go
package titles
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
|
|
// Test Title struct creation and basic methods
|
|
func TestNewTitle(t *testing.T) {
|
|
title := NewTitle(1, "Test Title")
|
|
if title == nil {
|
|
t.Fatal("NewTitle returned nil")
|
|
}
|
|
|
|
if title.ID != 1 {
|
|
t.Errorf("Expected ID 1, got %d", title.ID)
|
|
}
|
|
|
|
if title.Name != "Test Title" {
|
|
t.Errorf("Expected name 'Test Title', got '%s'", title.Name)
|
|
}
|
|
|
|
if title.Position != TitlePositionSuffix {
|
|
t.Errorf("Expected default position %d, got %d", TitlePositionSuffix, title.Position)
|
|
}
|
|
}
|
|
|
|
func TestTitleFlags(t *testing.T) {
|
|
title := NewTitle(1, "Test Title")
|
|
|
|
// Test setting flags
|
|
title.SetFlag(FlagHidden)
|
|
if !title.HasFlag(FlagHidden) {
|
|
t.Error("Expected title to have FlagHidden after setting")
|
|
}
|
|
|
|
title.SetFlag(FlagTemporary)
|
|
if !title.HasFlag(FlagTemporary) {
|
|
t.Error("Expected title to have FlagTemporary after setting")
|
|
}
|
|
|
|
// Test clearing flags
|
|
title.ClearFlag(FlagHidden)
|
|
if title.HasFlag(FlagHidden) {
|
|
t.Error("Expected title not to have FlagHidden after clearing")
|
|
}
|
|
|
|
// Test toggle flags (manually implement toggle)
|
|
if title.HasFlag(FlagUnique) {
|
|
title.ClearFlag(FlagUnique)
|
|
} else {
|
|
title.SetFlag(FlagUnique)
|
|
}
|
|
if !title.HasFlag(FlagUnique) {
|
|
t.Error("Expected title to have FlagUnique after toggling")
|
|
}
|
|
|
|
// Toggle again
|
|
if title.HasFlag(FlagUnique) {
|
|
title.ClearFlag(FlagUnique)
|
|
} else {
|
|
title.SetFlag(FlagUnique)
|
|
}
|
|
if title.HasFlag(FlagUnique) {
|
|
t.Error("Expected title not to have FlagUnique after toggling again")
|
|
}
|
|
}
|
|
|
|
func TestTitleProperties(t *testing.T) {
|
|
title := NewTitle(1, "Test Title")
|
|
|
|
// Test description
|
|
title.SetDescription("A test description")
|
|
if title.Description != "A test description" {
|
|
t.Errorf("Expected description 'A test description', got '%s'", title.Description)
|
|
}
|
|
|
|
// Test category
|
|
title.SetCategory(CategoryAchievement)
|
|
if title.Category != CategoryAchievement {
|
|
t.Errorf("Expected category '%s', got '%s'", CategoryAchievement, title.Category)
|
|
}
|
|
|
|
// Test rarity
|
|
title.SetRarity(TitleRarityRare)
|
|
if title.Rarity != TitleRarityRare {
|
|
t.Errorf("Expected rarity %d, got %d", TitleRarityRare, title.Rarity)
|
|
}
|
|
|
|
// Test display name
|
|
title.Position = TitlePositionPrefix
|
|
displayName := title.GetDisplayName()
|
|
if displayName != "Test Title" {
|
|
t.Errorf("Expected display name 'Test Title', got '%s'", displayName)
|
|
}
|
|
}
|
|
|
|
func TestTitleTypeMethods(t *testing.T) {
|
|
title := NewTitle(1, "Test Title")
|
|
|
|
// Test hidden flag
|
|
title.SetFlag(FlagHidden)
|
|
if !title.IsHidden() {
|
|
t.Error("Expected title to be hidden")
|
|
}
|
|
|
|
// Test temporary flag
|
|
title.ClearFlag(FlagHidden)
|
|
title.SetFlag(FlagTemporary)
|
|
if !title.IsTemporary() {
|
|
t.Error("Expected title to be temporary")
|
|
}
|
|
|
|
// Test unique flag
|
|
title.ClearFlag(FlagTemporary)
|
|
title.SetFlag(FlagUnique)
|
|
if !title.IsUnique() {
|
|
t.Error("Expected title to be unique")
|
|
}
|
|
|
|
// Test account-wide flag
|
|
title.ClearFlag(FlagUnique)
|
|
title.SetFlag(FlagAccountWide)
|
|
if !title.IsAccountWide() {
|
|
t.Error("Expected title to be account-wide")
|
|
}
|
|
}
|
|
|
|
// Test MasterTitlesList
|
|
func TestMasterTitlesList(t *testing.T) {
|
|
mtl := NewMasterTitlesList()
|
|
if mtl == nil {
|
|
t.Fatal("NewMasterTitlesList returned nil")
|
|
}
|
|
|
|
// Test default titles are loaded
|
|
citizen, exists := mtl.GetTitle(TitleIDCitizen)
|
|
if !exists || citizen == nil {
|
|
t.Error("Expected Citizen title to be loaded by default")
|
|
} else if citizen.Name != "Citizen" {
|
|
t.Errorf("Expected Citizen title name, got '%s'", citizen.Name)
|
|
}
|
|
|
|
visitor, exists := mtl.GetTitle(TitleIDVisitor)
|
|
if !exists || visitor == nil {
|
|
t.Error("Expected Visitor title to be loaded by default")
|
|
}
|
|
|
|
newcomer, exists := mtl.GetTitle(TitleIDNewcomer)
|
|
if !exists || newcomer == nil {
|
|
t.Error("Expected Newcomer title to be loaded by default")
|
|
}
|
|
|
|
returning, exists := mtl.GetTitle(TitleIDReturning)
|
|
if !exists || returning == nil {
|
|
t.Error("Expected Returning title to be loaded by default")
|
|
}
|
|
|
|
// Test adding new title
|
|
testTitle := NewTitle(0, "Test Title") // ID will be assigned
|
|
testTitle.SetDescription("Test description")
|
|
testTitle.SetCategory(CategoryMiscellaneous)
|
|
|
|
err := mtl.AddTitle(testTitle)
|
|
if err != nil {
|
|
t.Fatalf("Failed to add title: %v", err)
|
|
}
|
|
|
|
// ID should have been assigned
|
|
if testTitle.ID == 0 {
|
|
t.Error("Title ID should have been assigned")
|
|
}
|
|
|
|
retrieved, exists := mtl.GetTitle(testTitle.ID)
|
|
if !exists || retrieved == nil {
|
|
t.Error("Failed to retrieve added title")
|
|
} else if retrieved.Name != "Test Title" {
|
|
t.Errorf("Expected retrieved title name 'Test Title', got '%s'", retrieved.Name)
|
|
}
|
|
|
|
// Test duplicate ID
|
|
duplicateTitle := NewTitle(testTitle.ID, "Duplicate")
|
|
err = mtl.AddTitle(duplicateTitle)
|
|
if err == nil {
|
|
t.Error("Should have failed to add title with duplicate ID")
|
|
}
|
|
}
|
|
|
|
func TestMasterTitlesListCategories(t *testing.T) {
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Add titles in different categories
|
|
for i := 0; i < 3; i++ {
|
|
title := NewTitle(0, fmt.Sprintf("Achievement Title %d", i))
|
|
title.SetCategory(CategoryAchievement)
|
|
mtl.AddTitle(title)
|
|
}
|
|
|
|
for i := 0; i < 2; i++ {
|
|
title := NewTitle(0, fmt.Sprintf("Quest Title %d", i))
|
|
title.SetCategory(CategoryQuest)
|
|
mtl.AddTitle(title)
|
|
}
|
|
|
|
// Test getting titles by category
|
|
achievementTitles := mtl.GetTitlesByCategory(CategoryAchievement)
|
|
if len(achievementTitles) < 3 {
|
|
t.Errorf("Expected at least 3 achievement titles, got %d", len(achievementTitles))
|
|
}
|
|
|
|
questTitles := mtl.GetTitlesByCategory(CategoryQuest)
|
|
if len(questTitles) < 2 {
|
|
t.Errorf("Expected at least 2 quest titles, got %d", len(questTitles))
|
|
}
|
|
|
|
// Test getting available categories
|
|
categories := mtl.GetAvailableCategories()
|
|
hasAchievement := false
|
|
hasQuest := false
|
|
for _, cat := range categories {
|
|
if cat == CategoryAchievement {
|
|
hasAchievement = true
|
|
}
|
|
if cat == CategoryQuest {
|
|
hasQuest = true
|
|
}
|
|
}
|
|
|
|
if !hasAchievement {
|
|
t.Error("Expected CategoryAchievement in available categories")
|
|
}
|
|
if !hasQuest {
|
|
t.Error("Expected CategoryQuest in available categories")
|
|
}
|
|
}
|
|
|
|
func TestMasterTitlesListSourceAndRarity(t *testing.T) {
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Add titles with different sources
|
|
title1 := NewTitle(0, "Achievement Source")
|
|
title1.Source = TitleSourceAchievement
|
|
title1.SetRarity(TitleRarityCommon)
|
|
mtl.AddTitle(title1)
|
|
|
|
title2 := NewTitle(0, "Quest Source")
|
|
title2.Source = TitleSourceQuest
|
|
title2.SetRarity(TitleRarityRare)
|
|
mtl.AddTitle(title2)
|
|
|
|
title3 := NewTitle(0, "PvP Source")
|
|
title3.Source = TitleSourcePvP
|
|
title3.SetRarity(TitleRarityEpic)
|
|
mtl.AddTitle(title3)
|
|
|
|
// Test getting titles by source
|
|
achievementSourceTitles := mtl.GetTitlesBySource(TitleSourceAchievement)
|
|
found := false
|
|
for _, t := range achievementSourceTitles {
|
|
if t.Name == "Achievement Source" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Failed to find achievement source title")
|
|
}
|
|
|
|
// Test getting titles by rarity
|
|
rareTitles := mtl.GetTitlesByRarity(TitleRarityRare)
|
|
found = false
|
|
for _, t := range rareTitles {
|
|
if t.Name == "Quest Source" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Failed to find rare title")
|
|
}
|
|
}
|
|
|
|
// Test PlayerTitlesList
|
|
func TestPlayerTitlesList(t *testing.T) {
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Add some titles to master list
|
|
title1 := NewTitle(100, "Title One")
|
|
title1.Position = TitlePositionPrefix
|
|
mtl.AddTitle(title1)
|
|
|
|
title2 := NewTitle(101, "Title Two")
|
|
title2.Position = TitlePositionSuffix
|
|
mtl.AddTitle(title2)
|
|
|
|
ptl := NewPlayerTitlesList(123, mtl)
|
|
if ptl == nil {
|
|
t.Fatal("NewPlayerTitlesList returned nil")
|
|
}
|
|
|
|
// Test adding title
|
|
err := ptl.AddTitle(100, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to add title to player: %v", err)
|
|
}
|
|
|
|
err = ptl.AddTitle(101, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to add second title to player: %v", err)
|
|
}
|
|
|
|
// Test getting titles (player starts with default Citizen title)
|
|
titles := ptl.GetAllTitles()
|
|
expectedTitleCount := 3 // Citizen + our 2 titles
|
|
if len(titles) != expectedTitleCount {
|
|
t.Errorf("Expected %d titles, got %d", expectedTitleCount, len(titles))
|
|
}
|
|
|
|
// Test has title
|
|
if !ptl.HasTitle(100) {
|
|
t.Error("Expected player to have title 100")
|
|
}
|
|
|
|
if !ptl.HasTitle(101) {
|
|
t.Error("Expected player to have title 101")
|
|
}
|
|
|
|
if ptl.HasTitle(999) {
|
|
t.Error("Expected player not to have title 999")
|
|
}
|
|
|
|
// Test setting active prefix
|
|
err = ptl.SetActivePrefix(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to set active prefix: %v", err)
|
|
}
|
|
|
|
activePrefixTitle, hasPrefix := ptl.GetActivePrefixTitle()
|
|
if !hasPrefix || activePrefixTitle.ID != 100 {
|
|
t.Errorf("Expected active prefix 100, got title ID: %v", activePrefixTitle)
|
|
}
|
|
|
|
// Test setting active suffix
|
|
err = ptl.SetActiveSuffix(101)
|
|
if err != nil {
|
|
t.Fatalf("Failed to set active suffix: %v", err)
|
|
}
|
|
|
|
activeSuffixTitle, hasSuffix := ptl.GetActiveSuffixTitle()
|
|
if !hasSuffix || activeSuffixTitle.ID != 101 {
|
|
t.Errorf("Expected active suffix 101, got title ID: %v", activeSuffixTitle)
|
|
}
|
|
|
|
// Test formatted name (the actual format may vary)
|
|
formattedName := ptl.GetFormattedName("PlayerName")
|
|
// Just check that it contains both titles and the player name
|
|
if !contains(formattedName, "Title One") {
|
|
t.Errorf("Formatted name should contain 'Title One', got '%s'", formattedName)
|
|
}
|
|
if !contains(formattedName, "Title Two") {
|
|
t.Errorf("Formatted name should contain 'Title Two', got '%s'", formattedName)
|
|
}
|
|
if !contains(formattedName, "PlayerName") {
|
|
t.Errorf("Formatted name should contain 'PlayerName', got '%s'", formattedName)
|
|
}
|
|
|
|
// Test removing title
|
|
err = ptl.RemoveTitle(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to remove title: %v", err)
|
|
}
|
|
|
|
if ptl.HasTitle(100) {
|
|
t.Error("Title 100 should have been removed")
|
|
}
|
|
|
|
// Active prefix should be cleared
|
|
_, hasPrefix = ptl.GetActivePrefixTitle()
|
|
if hasPrefix {
|
|
t.Error("Active prefix should be cleared after removing the title")
|
|
}
|
|
}
|
|
|
|
func TestPlayerTitlesExpiration(t *testing.T) {
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Add a temporary title to master list
|
|
tempTitle := NewTitle(200, "Temporary Title")
|
|
tempTitle.SetFlag(FlagTemporary)
|
|
tempTitle.ExpirationHours = 1 // Expires after 1 hour
|
|
mtl.AddTitle(tempTitle)
|
|
|
|
ptl := NewPlayerTitlesList(456, mtl)
|
|
|
|
// Add the temporary title
|
|
err := ptl.AddTitle(200, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to add temporary title: %v", err)
|
|
}
|
|
|
|
// Title should exist
|
|
if !ptl.HasTitle(200) {
|
|
t.Error("Expected player to have temporary title")
|
|
}
|
|
|
|
// Manually set expiration to past
|
|
if playerTitle, exists := ptl.titles[200]; exists {
|
|
playerTitle.ExpiresAt = time.Now().Add(-1 * time.Hour)
|
|
}
|
|
|
|
// Clean up expired titles
|
|
expired := ptl.CleanupExpiredTitles()
|
|
if expired != 1 {
|
|
t.Errorf("Expected 1 expired title, got %d", expired)
|
|
}
|
|
|
|
// Title should be removed
|
|
if ptl.HasTitle(200) {
|
|
t.Error("Expired title should have been removed")
|
|
}
|
|
}
|
|
|
|
// Test TitleManager
|
|
func TestTitleManager(t *testing.T) {
|
|
tm := NewTitleManager()
|
|
if tm == nil {
|
|
t.Fatal("NewTitleManager returned nil")
|
|
}
|
|
|
|
defer tm.Shutdown()
|
|
|
|
// Test getting player titles
|
|
playerTitles := tm.GetPlayerTitles(456)
|
|
if playerTitles == nil {
|
|
t.Error("GetPlayerTitles returned nil")
|
|
}
|
|
|
|
// Test granting title (use Visitor instead of Citizen to avoid conflicts)
|
|
err := tm.GrantTitle(456, TitleIDVisitor, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to grant title: %v", err)
|
|
}
|
|
|
|
// Verify title was granted (player starts with default Citizen title)
|
|
playerTitles = tm.GetPlayerTitles(456)
|
|
titles := playerTitles.GetAllTitles()
|
|
expectedCount := 2 // Citizen + Visitor
|
|
if len(titles) != expectedCount {
|
|
t.Errorf("Expected %d titles for player, got %d", expectedCount, len(titles))
|
|
}
|
|
|
|
// Verify the player has the visitor title
|
|
if !playerTitles.HasTitle(TitleIDVisitor) {
|
|
t.Error("Player should have Visitor title")
|
|
}
|
|
|
|
// Test revoking title
|
|
err = tm.RevokeTitle(456, TitleIDVisitor)
|
|
if err != nil {
|
|
t.Fatalf("Failed to revoke title: %v", err)
|
|
}
|
|
|
|
// Verify title was revoked (should still have Citizen)
|
|
playerTitles = tm.GetPlayerTitles(456)
|
|
titles = playerTitles.GetAllTitles()
|
|
if len(titles) != 1 {
|
|
t.Errorf("Expected 1 title after revoke, got %d", len(titles))
|
|
}
|
|
}
|
|
|
|
func TestTitleManagerCreateVariants(t *testing.T) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Test creating regular title
|
|
title, err := tm.CreateTitle("Regular Title", "A regular title", CategoryMiscellaneous, TitlePositionSuffix, TitleSourceQuest, TitleRarityCommon)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create regular title: %v", err)
|
|
}
|
|
if title == nil {
|
|
t.Fatal("CreateTitle returned nil")
|
|
}
|
|
|
|
// Test creating achievement title
|
|
achievementTitle, err := tm.CreateAchievementTitle("Achievement Title", "An achievement title", 1000, TitlePositionPrefix, TitleRarityRare)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create achievement title: %v", err)
|
|
}
|
|
if achievementTitle.AchievementID != 1000 {
|
|
t.Errorf("Expected achievement ID 1000, got %d", achievementTitle.AchievementID)
|
|
}
|
|
|
|
// Test creating temporary title
|
|
tempTitle, err := tm.CreateTemporaryTitle("Temp Title", "A temporary title", 24, TitlePositionSuffix, TitleSourceHoliday, TitleRarityCommon)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temporary title: %v", err)
|
|
}
|
|
if !tempTitle.HasFlag(FlagTemporary) {
|
|
t.Error("Temporary title should have FlagTemporary")
|
|
}
|
|
if tempTitle.ExpirationHours != 24 {
|
|
t.Errorf("Expected expiration hours 24, got %d", tempTitle.ExpirationHours)
|
|
}
|
|
|
|
// Test creating unique title
|
|
uniqueTitle, err := tm.CreateUniqueTitle("Unique Title", "A unique title", TitlePositionPrefix, TitleSourceMiscellaneous)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create unique title: %v", err)
|
|
}
|
|
if !uniqueTitle.HasFlag(FlagUnique) {
|
|
t.Error("Unique title should have FlagUnique")
|
|
}
|
|
if uniqueTitle.Rarity != TitleRarityUnique {
|
|
t.Errorf("Expected rarity %d for unique title, got %d", TitleRarityUnique, uniqueTitle.Rarity)
|
|
}
|
|
}
|
|
|
|
func TestTitleManagerSearch(t *testing.T) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Create test titles
|
|
tm.CreateTitle("Dragon Slayer", "Defeated a dragon", CategoryCombat, TitlePositionSuffix, TitleSourceQuest, TitleRarityEpic)
|
|
tm.CreateTitle("Master Crafter", "Master of crafting", CategoryTradeskill, TitlePositionPrefix, TitleSourceTradeskill, TitleRarityRare)
|
|
tm.CreateTitle("Explorer", "Explored the world", CategoryExploration, TitlePositionSuffix, TitleSourceAchievement, TitleRarityCommon)
|
|
|
|
// Test search
|
|
results := tm.SearchTitles("dragon")
|
|
found := false
|
|
for _, title := range results {
|
|
if title.Name == "Dragon Slayer" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Failed to find 'Dragon Slayer' in search results")
|
|
}
|
|
|
|
// Test search with description
|
|
results = tm.SearchTitles("crafting")
|
|
found = false
|
|
for _, title := range results {
|
|
if title.Name == "Master Crafter" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Failed to find 'Master Crafter' when searching description")
|
|
}
|
|
}
|
|
|
|
func TestTitleManagerStatistics(t *testing.T) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Grant some titles (avoid Citizen which may be default)
|
|
tm.GrantTitle(1, TitleIDVisitor, 0, 0)
|
|
tm.GrantTitle(2, TitleIDNewcomer, 0, 0)
|
|
tm.GrantTitle(1, TitleIDReturning, 0, 0)
|
|
|
|
stats := tm.GetStatistics()
|
|
|
|
// Check statistics
|
|
if totalPlayers, ok := stats["total_players"].(int); ok {
|
|
if totalPlayers != 2 {
|
|
t.Errorf("Expected 2 total players, got %d", totalPlayers)
|
|
}
|
|
} else {
|
|
t.Error("Missing total_players in statistics")
|
|
}
|
|
|
|
if titlesGranted, ok := stats["titles_granted"].(int64); ok {
|
|
if titlesGranted != 3 {
|
|
t.Errorf("Expected 3 titles granted, got %d", titlesGranted)
|
|
}
|
|
} else {
|
|
t.Error("Missing titles_granted in statistics")
|
|
}
|
|
}
|
|
|
|
func TestTitleManagerConcurrency(t *testing.T) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Test concurrent access to player titles
|
|
var wg sync.WaitGroup
|
|
numPlayers := 10
|
|
numOperations := 100
|
|
|
|
for i := 0; i < numPlayers; i++ {
|
|
wg.Add(1)
|
|
go func(playerID int32) {
|
|
defer wg.Done()
|
|
|
|
for j := 0; j < numOperations; j++ {
|
|
// Grant title
|
|
tm.GrantTitle(playerID, TitleIDCitizen, 0, 0)
|
|
|
|
// Get player titles
|
|
ptl := tm.GetPlayerTitles(playerID)
|
|
_ = ptl.GetAllTitles()
|
|
|
|
// Set active title
|
|
tm.SetPlayerActivePrefix(playerID, TitleIDCitizen)
|
|
|
|
// Get formatted name
|
|
tm.GetPlayerFormattedName(playerID, fmt.Sprintf("Player%d", playerID))
|
|
|
|
// Revoke title
|
|
tm.RevokeTitle(playerID, TitleIDCitizen)
|
|
}
|
|
}(int32(i))
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Verify no crashes or data races
|
|
stats := tm.GetStatistics()
|
|
if stats == nil {
|
|
t.Error("Failed to get statistics after concurrent operations")
|
|
}
|
|
}
|
|
|
|
// Test Database Integration
|
|
func TestDatabaseIntegration(t *testing.T) {
|
|
// Create temporary database
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test_titles.db")
|
|
|
|
db, err := OpenDB(dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Create tables
|
|
err = db.CreateTables()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create tables: %v", err)
|
|
}
|
|
|
|
// Test master list database operations
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Add some test titles
|
|
title1 := NewTitle(500, "Database Test Title 1")
|
|
title1.SetDescription("Test description 1")
|
|
title1.SetCategory(CategoryMiscellaneous)
|
|
title1.Position = TitlePositionPrefix // Set as prefix title
|
|
mtl.AddTitle(title1)
|
|
|
|
title2 := NewTitle(501, "Database Test Title 2")
|
|
title2.SetDescription("Test description 2")
|
|
title2.SetCategory(CategoryAchievement)
|
|
title2.Position = TitlePositionSuffix // Set as suffix title
|
|
title2.AchievementID = 2000
|
|
mtl.AddTitle(title2)
|
|
|
|
// Save to database
|
|
err = mtl.SaveToDatabase(db)
|
|
if err != nil {
|
|
t.Fatalf("Failed to save master titles to database: %v", err)
|
|
}
|
|
|
|
// Create new master list and load from database
|
|
mtl2 := &MasterTitlesList{
|
|
titles: make(map[int32]*Title),
|
|
categorized: make(map[string][]*Title),
|
|
bySource: make(map[int32][]*Title),
|
|
byRarity: make(map[int32][]*Title),
|
|
byAchievement: make(map[uint32]*Title),
|
|
nextID: 1,
|
|
}
|
|
|
|
err = mtl2.LoadFromDatabase(db)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load master titles from database: %v", err)
|
|
}
|
|
|
|
// Verify loaded titles
|
|
loadedTitle1, exists := mtl2.GetTitle(500)
|
|
if !exists || loadedTitle1 == nil {
|
|
t.Fatal("Failed to load title 500 from database")
|
|
}
|
|
if loadedTitle1.Name != "Database Test Title 1" {
|
|
t.Errorf("Expected title name 'Database Test Title 1', got '%s'", loadedTitle1.Name)
|
|
}
|
|
|
|
loadedTitle2, exists := mtl2.GetTitle(501)
|
|
if !exists || loadedTitle2 == nil {
|
|
t.Fatal("Failed to load title 501 from database")
|
|
}
|
|
if loadedTitle2.AchievementID != 2000 {
|
|
t.Errorf("Expected achievement ID 2000, got %d", loadedTitle2.AchievementID)
|
|
}
|
|
|
|
// Test player titles database operations
|
|
ptl := NewPlayerTitlesList(789, mtl2) // Use mtl2 which has titles loaded from database
|
|
ptl.AddTitle(500, 0, 0)
|
|
ptl.AddTitle(501, 2000, 0)
|
|
ptl.SetActivePrefix(500)
|
|
ptl.SetActiveSuffix(501)
|
|
|
|
// Save player titles
|
|
err = ptl.SaveToDatabase(db)
|
|
if err != nil {
|
|
t.Fatalf("Failed to save player titles to database: %v", err)
|
|
}
|
|
|
|
// Load player titles
|
|
ptl2 := NewPlayerTitlesList(789, mtl2)
|
|
err = ptl2.LoadFromDatabase(db)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load player titles from database: %v", err)
|
|
}
|
|
|
|
// Verify loaded player titles
|
|
if !ptl2.HasTitle(500) {
|
|
t.Error("Expected player to have title 500")
|
|
}
|
|
if !ptl2.HasTitle(501) {
|
|
t.Error("Expected player to have title 501")
|
|
}
|
|
|
|
|
|
activePrefixTitle, hasPrefix := ptl2.GetActivePrefixTitle()
|
|
if !hasPrefix {
|
|
t.Error("Expected to have active prefix title")
|
|
} else if activePrefixTitle.ID != 500 {
|
|
t.Errorf("Expected active prefix 500, got %d", activePrefixTitle.ID)
|
|
}
|
|
activeSuffixTitle, hasSuffix := ptl2.GetActiveSuffixTitle()
|
|
if !hasSuffix {
|
|
t.Error("Expected to have active suffix title")
|
|
} else if activeSuffixTitle.ID != 501 {
|
|
t.Errorf("Expected active suffix 501, got %d", activeSuffixTitle.ID)
|
|
}
|
|
}
|
|
|
|
func TestDatabaseHelperFunctions(t *testing.T) {
|
|
// Test nullableUint32
|
|
if val := nullableUint32(0); val != nil {
|
|
t.Error("nullableUint32(0) should return nil")
|
|
}
|
|
|
|
if val := nullableUint32(123); val != uint32(123) {
|
|
t.Errorf("nullableUint32(123) should return 123, got %v", val)
|
|
}
|
|
|
|
// Test nullableTime
|
|
zeroTime := time.Time{}
|
|
if val := nullableTime(zeroTime); val != nil {
|
|
t.Error("nullableTime(zero) should return nil")
|
|
}
|
|
|
|
now := time.Now()
|
|
if val := nullableTime(now); val != now.Unix() {
|
|
t.Errorf("nullableTime(now) should return Unix timestamp, got %v", val)
|
|
}
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkTitleCreation(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
title := NewTitle(int32(i), fmt.Sprintf("Title %d", i))
|
|
title.SetDescription("Description")
|
|
title.SetCategory(CategoryMiscellaneous)
|
|
}
|
|
}
|
|
|
|
func BenchmarkMasterListAdd(b *testing.B) {
|
|
mtl := NewMasterTitlesList()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
title := NewTitle(int32(i+1000), fmt.Sprintf("Title %d", i))
|
|
mtl.AddTitle(title)
|
|
}
|
|
}
|
|
|
|
func BenchmarkPlayerListAdd(b *testing.B) {
|
|
mtl := NewMasterTitlesList()
|
|
|
|
// Pre-populate master list
|
|
for i := 0; i < 1000; i++ {
|
|
title := NewTitle(int32(i), fmt.Sprintf("Title %d", i))
|
|
mtl.AddTitle(title)
|
|
}
|
|
|
|
ptl := NewPlayerTitlesList(1, mtl)
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
titleID := int32(i % 1000)
|
|
ptl.AddTitle(titleID, 0, 0)
|
|
ptl.RemoveTitle(titleID)
|
|
}
|
|
}
|
|
|
|
func BenchmarkTitleManagerGrant(b *testing.B) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Pre-create titles
|
|
for i := 0; i < 100; i++ {
|
|
tm.CreateTitle(fmt.Sprintf("Title %d", i), "Description", CategoryMiscellaneous, TitlePositionSuffix, TitleSourceQuest, TitleRarityCommon)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
playerID := int32(i % 100)
|
|
titleID := int32((i % 100) + 1)
|
|
tm.GrantTitle(playerID, titleID, 0, 0)
|
|
}
|
|
}
|
|
|
|
func BenchmarkTitleSearch(b *testing.B) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Create fewer titles for faster benchmark
|
|
for i := 0; i < 100; i++ {
|
|
name := fmt.Sprintf("Title %d", i)
|
|
if i%10 == 0 {
|
|
name = fmt.Sprintf("Dragon %d", i)
|
|
}
|
|
tm.CreateTitle(name, "Description", CategoryMiscellaneous, TitlePositionSuffix, TitleSourceQuest, TitleRarityCommon)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
tm.SearchTitles("dragon")
|
|
}
|
|
}
|
|
|
|
func BenchmarkConcurrentAccess(b *testing.B) {
|
|
tm := NewTitleManager()
|
|
defer tm.Shutdown()
|
|
|
|
// Pre-populate with titles
|
|
for i := 0; i < 10; i++ {
|
|
tm.CreateTitle(fmt.Sprintf("Title %d", i), "Description", CategoryMiscellaneous, TitlePositionSuffix, TitleSourceQuest, TitleRarityCommon)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
// Use simpler sequential approach instead of RunParallel to avoid deadlocks
|
|
for i := 0; i < b.N; i++ {
|
|
playerID := int32(i % 10)
|
|
titleID := int32((i % 10) + 1)
|
|
tm.GrantTitle(playerID, titleID, 0, 0)
|
|
tm.GetPlayerFormattedName(playerID, "Player")
|
|
}
|
|
} |