533 lines
15 KiB
Go
533 lines
15 KiB
Go
package alt_advancement
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Mock logger implementation for tests
|
|
type mockLogger struct {
|
|
logs []string
|
|
}
|
|
|
|
func (m *mockLogger) LogInfo(system, format string, args ...interface{}) {
|
|
m.logs = append(m.logs, fmt.Sprintf("[INFO][%s] "+format, append([]interface{}{system}, args...)...))
|
|
}
|
|
|
|
func (m *mockLogger) LogError(system, format string, args ...interface{}) {
|
|
m.logs = append(m.logs, fmt.Sprintf("[ERROR][%s] "+format, append([]interface{}{system}, args...)...))
|
|
}
|
|
|
|
func (m *mockLogger) LogDebug(system, format string, args ...interface{}) {
|
|
m.logs = append(m.logs, fmt.Sprintf("[DEBUG][%s] "+format, append([]interface{}{system}, args...)...))
|
|
}
|
|
|
|
func (m *mockLogger) LogWarning(system, format string, args ...interface{}) {
|
|
m.logs = append(m.logs, fmt.Sprintf("[WARNING][%s] "+format, append([]interface{}{system}, args...)...))
|
|
}
|
|
|
|
// Mock player manager implementation for tests
|
|
type mockPlayerManager struct {
|
|
players map[int32]*mockPlayer
|
|
}
|
|
|
|
type mockPlayer struct {
|
|
level int8
|
|
class int8
|
|
totalPoints int32
|
|
spentPoints int32
|
|
availPoints int32
|
|
expansions int32
|
|
name string
|
|
}
|
|
|
|
func newMockPlayerManager() *mockPlayerManager {
|
|
return &mockPlayerManager{
|
|
players: make(map[int32]*mockPlayer),
|
|
}
|
|
}
|
|
|
|
func (m *mockPlayerManager) addPlayer(characterID int32, level, class int8, totalPoints, spentPoints, availPoints int32, expansions int32, name string) {
|
|
m.players[characterID] = &mockPlayer{
|
|
level: level,
|
|
class: class,
|
|
totalPoints: totalPoints,
|
|
spentPoints: spentPoints,
|
|
availPoints: availPoints,
|
|
expansions: expansions,
|
|
name: name,
|
|
}
|
|
}
|
|
|
|
func (m *mockPlayerManager) GetPlayerLevel(characterID int32) (int8, error) {
|
|
if player, exists := m.players[characterID]; exists {
|
|
return player.level, nil
|
|
}
|
|
return 0, fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) GetPlayerClass(characterID int32) (int8, error) {
|
|
if player, exists := m.players[characterID]; exists {
|
|
return player.class, nil
|
|
}
|
|
return 0, fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) GetPlayerAAPoints(characterID int32) (total, spent, available int32, err error) {
|
|
if player, exists := m.players[characterID]; exists {
|
|
return player.totalPoints, player.spentPoints, player.availPoints, nil
|
|
}
|
|
return 0, 0, 0, fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) SpendAAPoints(characterID int32, points int32) error {
|
|
if player, exists := m.players[characterID]; exists {
|
|
if player.availPoints < points {
|
|
return fmt.Errorf("insufficient AA points: need %d, have %d", points, player.availPoints)
|
|
}
|
|
player.availPoints -= points
|
|
player.spentPoints += points
|
|
return nil
|
|
}
|
|
return fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) AwardAAPoints(characterID int32, points int32, reason string) error {
|
|
if player, exists := m.players[characterID]; exists {
|
|
player.totalPoints += points
|
|
player.availPoints += points
|
|
return nil
|
|
}
|
|
return fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) GetPlayerExpansions(characterID int32) (int32, error) {
|
|
if player, exists := m.players[characterID]; exists {
|
|
return player.expansions, nil
|
|
}
|
|
return 0, fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
func (m *mockPlayerManager) GetPlayerName(characterID int32) (string, error) {
|
|
if player, exists := m.players[characterID]; exists {
|
|
return player.name, nil
|
|
}
|
|
return "", fmt.Errorf("player %d not found", characterID)
|
|
}
|
|
|
|
// Helper function to set up test manager without database dependencies
|
|
func setupTestManager() (*AAManager, *mockLogger) {
|
|
logger := &mockLogger{}
|
|
config := DefaultAAConfig()
|
|
config.AutoSave = false // Disable auto-save for tests
|
|
config.DatabaseEnabled = false // Disable database for tests
|
|
|
|
manager := NewAAManager(nil, logger, config)
|
|
|
|
return manager, logger
|
|
}
|
|
|
|
// Helper to create test AA data
|
|
func createTestAA(nodeID int32, name string, group int8, minLevel int8, maxRank int8, rankCost int8) *AltAdvancement {
|
|
return &AltAdvancement{
|
|
NodeID: nodeID,
|
|
Name: name,
|
|
Description: fmt.Sprintf("Test AA: %s", name),
|
|
Group: group,
|
|
MinLevel: minLevel,
|
|
MaxRank: maxRank,
|
|
RankCost: rankCost,
|
|
Icon: 100,
|
|
Col: 1,
|
|
Row: 1,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
func TestSimpleAAManager(t *testing.T) {
|
|
// Test basic manager creation
|
|
manager, logger := setupTestManager()
|
|
|
|
if manager == nil {
|
|
t.Fatal("Expected manager to be non-nil")
|
|
}
|
|
|
|
// Test adding AAs manually (without database)
|
|
aa1 := createTestAA(1, "Test AA", AA_CLASS, 10, 5, 2)
|
|
manager.altAdvancements[1] = aa1
|
|
manager.byGroup[AA_CLASS] = []*AltAdvancement{aa1}
|
|
|
|
// Test retrieval
|
|
retrieved, exists := manager.GetAltAdvancement(1)
|
|
if !exists {
|
|
t.Error("Expected to find AA with ID 1")
|
|
}
|
|
if retrieved.Name != "Test AA" {
|
|
t.Errorf("Expected name 'Test AA', got '%s'", retrieved.Name)
|
|
}
|
|
|
|
// Test group lookup
|
|
classAAs := manager.GetAltAdvancementsByGroup(AA_CLASS)
|
|
if len(classAAs) != 1 {
|
|
t.Errorf("Expected 1 class AA, got %d", len(classAAs))
|
|
}
|
|
|
|
// Check logger was used
|
|
if len(logger.logs) == 0 {
|
|
t.Log("Note: No log entries recorded (this is normal for basic tests)")
|
|
}
|
|
}
|
|
|
|
func TestPlayerAAState(t *testing.T) {
|
|
characterID := int32(12345)
|
|
state := NewPlayerAAState(characterID)
|
|
|
|
if state == nil {
|
|
t.Fatal("Expected state to be non-nil")
|
|
}
|
|
if state.CharacterID != characterID {
|
|
t.Errorf("Expected CharacterID %d, got %d", characterID, state.CharacterID)
|
|
}
|
|
if state.TotalPoints != 0 {
|
|
t.Errorf("Expected TotalPoints 0, got %d", state.TotalPoints)
|
|
}
|
|
if state.ActiveTemplate != AA_TEMPLATE_CURRENT {
|
|
t.Errorf("Expected ActiveTemplate %d, got %d", AA_TEMPLATE_CURRENT, state.ActiveTemplate)
|
|
}
|
|
if state.Templates == nil {
|
|
t.Error("Expected Templates to be non-nil")
|
|
}
|
|
if state.Tabs == nil {
|
|
t.Error("Expected Tabs to be non-nil")
|
|
}
|
|
if state.AAProgress == nil {
|
|
t.Error("Expected AAProgress to be non-nil")
|
|
}
|
|
}
|
|
|
|
func TestAATemplate(t *testing.T) {
|
|
templateID := int8(AA_TEMPLATE_PERSONAL_1)
|
|
name := "Test Template"
|
|
template := NewAATemplate(templateID, name)
|
|
|
|
if template == nil {
|
|
t.Fatal("Expected template to be non-nil")
|
|
}
|
|
if template.TemplateID != templateID {
|
|
t.Errorf("Expected TemplateID %d, got %d", templateID, template.TemplateID)
|
|
}
|
|
if template.Name != name {
|
|
t.Errorf("Expected name '%s', got '%s'", name, template.Name)
|
|
}
|
|
if !template.IsPersonal {
|
|
t.Error("Expected template to be personal")
|
|
}
|
|
if template.IsServer {
|
|
t.Error("Expected template not to be server template")
|
|
}
|
|
if template.Entries == nil {
|
|
t.Error("Expected Entries to be non-nil")
|
|
}
|
|
}
|
|
|
|
func TestAATab(t *testing.T) {
|
|
tabID := int8(AA_CLASS)
|
|
group := int8(AA_CLASS)
|
|
name := "Class"
|
|
tab := NewAATab(tabID, group, name)
|
|
|
|
if tab == nil {
|
|
t.Fatal("Expected tab to be non-nil")
|
|
}
|
|
if tab.TabID != tabID {
|
|
t.Errorf("Expected TabID %d, got %d", tabID, tab.TabID)
|
|
}
|
|
if tab.Group != group {
|
|
t.Errorf("Expected Group %d, got %d", group, tab.Group)
|
|
}
|
|
if tab.Name != name {
|
|
t.Errorf("Expected name '%s', got '%s'", name, tab.Name)
|
|
}
|
|
if tab.Nodes == nil {
|
|
t.Error("Expected Nodes to be non-nil")
|
|
}
|
|
}
|
|
|
|
func TestConstants(t *testing.T) {
|
|
// Test tab names
|
|
className := GetTabName(AA_CLASS)
|
|
if className != "Class" {
|
|
t.Errorf("Expected 'Class', got '%s'", className)
|
|
}
|
|
|
|
unknownName := GetTabName(99)
|
|
if unknownName != "Unknown" {
|
|
t.Errorf("Expected 'Unknown', got '%s'", unknownName)
|
|
}
|
|
|
|
// Test template names
|
|
personal1Name := GetTemplateName(AA_TEMPLATE_PERSONAL_1)
|
|
if personal1Name != "Personal Template 1" {
|
|
t.Errorf("Expected 'Personal Template 1', got '%s'", personal1Name)
|
|
}
|
|
|
|
// Test max AA for tabs
|
|
classMax := GetMaxAAForTab(AA_CLASS)
|
|
if classMax != 100 {
|
|
t.Errorf("Expected 100, got %d", classMax)
|
|
}
|
|
|
|
shadowsMax := GetMaxAAForTab(AA_SHADOW)
|
|
if shadowsMax != 70 {
|
|
t.Errorf("Expected 70, got %d", shadowsMax)
|
|
}
|
|
|
|
// Test expansion checks
|
|
combined := EXPANSION_RUINS_OF_KUNARK | EXPANSION_SHADOWS_OF_LUCLIN
|
|
if !IsExpansionRequired(combined, EXPANSION_RUINS_OF_KUNARK) {
|
|
t.Error("Expected expansion check to return true")
|
|
}
|
|
if IsExpansionRequired(EXPANSION_RUINS_OF_KUNARK, EXPANSION_SHADOWS_OF_LUCLIN) {
|
|
t.Error("Expected expansion check to return false")
|
|
}
|
|
|
|
// Test error messages
|
|
successMsg := GetAAErrorMessage(AA_ERROR_NONE)
|
|
if successMsg != "Success" {
|
|
t.Errorf("Expected 'Success', got '%s'", successMsg)
|
|
}
|
|
|
|
unknownMsg := GetAAErrorMessage(999)
|
|
if unknownMsg != "Unknown error" {
|
|
t.Errorf("Expected 'Unknown error', got '%s'", unknownMsg)
|
|
}
|
|
|
|
// Test template validation
|
|
if !ValidateTemplateID(AA_TEMPLATE_PERSONAL_1) {
|
|
t.Error("Expected valid template ID")
|
|
}
|
|
if ValidateTemplateID(0) {
|
|
t.Error("Expected invalid template ID")
|
|
}
|
|
|
|
// Test template type checks
|
|
if !IsPersonalTemplate(AA_TEMPLATE_PERSONAL_1) {
|
|
t.Error("Expected personal template")
|
|
}
|
|
if IsServerTemplate(AA_TEMPLATE_PERSONAL_1) {
|
|
t.Error("Expected not server template")
|
|
}
|
|
if !IsServerTemplate(AA_TEMPLATE_SERVER_1) {
|
|
t.Error("Expected server template")
|
|
}
|
|
if !IsCurrentTemplate(AA_TEMPLATE_CURRENT) {
|
|
t.Error("Expected current template")
|
|
}
|
|
|
|
// Test AA group validation
|
|
if !ValidateAAGroup(AA_CLASS) {
|
|
t.Error("Expected valid AA group")
|
|
}
|
|
if ValidateAAGroup(-1) {
|
|
t.Error("Expected invalid AA group")
|
|
}
|
|
}
|
|
|
|
func TestExpansionNames(t *testing.T) {
|
|
kunarkName := GetExpansionNameByFlag(EXPANSION_RUINS_OF_KUNARK)
|
|
if kunarkName != "Ruins of Kunark" {
|
|
t.Errorf("Expected 'Ruins of Kunark', got '%s'", kunarkName)
|
|
}
|
|
|
|
unknownExpansion := GetExpansionNameByFlag(999)
|
|
if unknownExpansion != "Unknown Expansion" {
|
|
t.Errorf("Expected 'Unknown Expansion', got '%s'", unknownExpansion)
|
|
}
|
|
}
|
|
|
|
func TestInMemoryOperations(t *testing.T) {
|
|
manager, _ := setupTestManager()
|
|
|
|
// Add test data
|
|
aa1 := createTestAA(1, "Fireball", AA_CLASS, 10, 5, 1)
|
|
aa2 := createTestAA(2, "Ice Bolt", AA_CLASS, 15, 3, 2)
|
|
aa3 := createTestAA(3, "Heal", AA_SUBCLASS, 8, 4, 1)
|
|
|
|
manager.altAdvancements[1] = aa1
|
|
manager.altAdvancements[2] = aa2
|
|
manager.altAdvancements[3] = aa3
|
|
|
|
manager.byGroup[AA_CLASS] = []*AltAdvancement{aa1, aa2}
|
|
manager.byGroup[AA_SUBCLASS] = []*AltAdvancement{aa3}
|
|
|
|
manager.byLevel[10] = []*AltAdvancement{aa1}
|
|
manager.byLevel[15] = []*AltAdvancement{aa2}
|
|
manager.byLevel[8] = []*AltAdvancement{aa3}
|
|
|
|
// Test retrievals
|
|
classAAs := manager.GetAltAdvancementsByGroup(AA_CLASS)
|
|
if len(classAAs) != 2 {
|
|
t.Errorf("Expected 2 class AAs, got %d", len(classAAs))
|
|
}
|
|
|
|
level10AAs := manager.GetAltAdvancementsByLevel(10)
|
|
if len(level10AAs) != 1 {
|
|
t.Errorf("Expected 1 level 10 AA, got %d", len(level10AAs))
|
|
}
|
|
|
|
// Test player state operations
|
|
characterID := int32(12345)
|
|
state := NewPlayerAAState(characterID)
|
|
manager.playerStates[characterID] = state
|
|
|
|
stats := manager.GetPlayerAAStats(characterID)
|
|
if stats == nil {
|
|
t.Fatal("Expected stats to be non-nil")
|
|
}
|
|
if stats["character_id"] != characterID {
|
|
t.Errorf("Expected character_id %d, got %v", characterID, stats["character_id"])
|
|
}
|
|
}
|
|
|
|
func TestSystemStats(t *testing.T) {
|
|
manager, _ := setupTestManager()
|
|
|
|
// Set some test stats
|
|
manager.stats.TotalAAsLoaded = 150
|
|
manager.stats.TotalAAPurchases = 25
|
|
manager.stats.TotalPointsSpent = 500
|
|
|
|
// Add some player states
|
|
manager.playerStates[1] = NewPlayerAAState(1)
|
|
manager.playerStates[2] = NewPlayerAAState(2)
|
|
|
|
stats := manager.GetSystemStats()
|
|
if stats == nil {
|
|
t.Fatal("Expected stats to be non-nil")
|
|
}
|
|
if stats.TotalAAsLoaded != 150 {
|
|
t.Errorf("Expected TotalAAsLoaded 150, got %d", stats.TotalAAsLoaded)
|
|
}
|
|
if stats.ActivePlayers != 2 {
|
|
t.Errorf("Expected ActivePlayers 2, got %d", stats.ActivePlayers)
|
|
}
|
|
if stats.AveragePointsSpent != 20.0 { // 500 / 25
|
|
t.Errorf("Expected AveragePointsSpent 20.0, got %f", stats.AveragePointsSpent)
|
|
}
|
|
}
|
|
|
|
func TestMockPlayerManager(t *testing.T) {
|
|
playerManager := newMockPlayerManager()
|
|
characterID := int32(12345)
|
|
|
|
// Test player not found
|
|
_, err := playerManager.GetPlayerLevel(characterID)
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent player")
|
|
}
|
|
|
|
// Add player and test
|
|
playerManager.addPlayer(characterID, 25, 3, 100, 50, 50, EXPANSION_RUINS_OF_KUNARK, "TestPlayer")
|
|
|
|
level, err := playerManager.GetPlayerLevel(characterID)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if level != 25 {
|
|
t.Errorf("Expected level 25, got %d", level)
|
|
}
|
|
|
|
class, err := playerManager.GetPlayerClass(characterID)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if class != 3 {
|
|
t.Errorf("Expected class 3, got %d", class)
|
|
}
|
|
|
|
total, spent, avail, err := playerManager.GetPlayerAAPoints(characterID)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if total != 100 || spent != 50 || avail != 50 {
|
|
t.Errorf("Expected points 100/50/50, got %d/%d/%d", total, spent, avail)
|
|
}
|
|
|
|
// Test spending points
|
|
err = playerManager.SpendAAPoints(characterID, 10)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
player := playerManager.players[characterID]
|
|
if player.availPoints != 40 {
|
|
t.Errorf("Expected availPoints 40, got %d", player.availPoints)
|
|
}
|
|
if player.spentPoints != 60 {
|
|
t.Errorf("Expected spentPoints 60, got %d", player.spentPoints)
|
|
}
|
|
|
|
// Test insufficient points
|
|
err = playerManager.SpendAAPoints(characterID, 100)
|
|
if err == nil {
|
|
t.Error("Expected error for insufficient points")
|
|
}
|
|
}
|
|
|
|
func TestPacketBuilding(t *testing.T) {
|
|
manager, _ := setupTestManager()
|
|
|
|
characterID := int32(12345)
|
|
clientVersion := uint32(1142)
|
|
|
|
// Add some test AAs
|
|
manager.altAdvancements[1] = createTestAA(1, "Test AA 1", AA_CLASS, 10, 5, 2)
|
|
manager.altAdvancements[2] = createTestAA(2, "Test AA 2", AA_SUBCLASS, 15, 3, 1)
|
|
|
|
// Set up player state
|
|
state := NewPlayerAAState(characterID)
|
|
state.TotalPoints = 100
|
|
state.SpentPoints = 60
|
|
state.AvailablePoints = 40
|
|
manager.playerStates[characterID] = state
|
|
|
|
// Test GetAAListPacket - should find packet but fail on missing fields
|
|
_, err := manager.GetAAListPacket(characterID, clientVersion)
|
|
if err == nil {
|
|
t.Error("Expected error due to missing packet fields")
|
|
}
|
|
if !contains(err.Error(), "failed to build AA packet") {
|
|
t.Errorf("Expected 'failed to build AA packet' error, got: %v", err)
|
|
}
|
|
|
|
// Test DisplayAA
|
|
_, err = manager.DisplayAA(characterID, AA_TEMPLATE_CURRENT, 0, clientVersion)
|
|
if err == nil {
|
|
t.Error("Expected error due to missing packet fields")
|
|
}
|
|
|
|
// Test SendAAListPacket wrapper
|
|
_, err = manager.SendAAListPacket(characterID, clientVersion)
|
|
if err == nil {
|
|
t.Error("Expected error due to missing packet fields")
|
|
}
|
|
|
|
// Verify packet errors are tracked (we successfully found packets but failed to build them)
|
|
if manager.stats.PacketErrors < 3 {
|
|
t.Errorf("Expected at least 3 packet errors, got %d", manager.stats.PacketErrors)
|
|
}
|
|
|
|
t.Logf("Packet integration working: found AdventureList packet but needs proper field mapping")
|
|
}
|
|
|
|
// Helper function to check if string contains substring
|
|
func contains(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
} |