eq2go/internal/player/player_test.go
2025-08-06 17:55:41 -05:00

662 lines
15 KiB
Go

package player
import (
"fmt"
"strings"
"testing"
"time"
"eq2emu/internal/quests"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
)
// TestNewPlayer tests player creation
func TestNewPlayer(t *testing.T) {
p := NewPlayer()
if p == nil {
t.Fatal("NewPlayer returned nil")
}
// Verify default values
if p.GetCharacterID() != 0 {
t.Errorf("Expected character ID 0, got %d", p.GetCharacterID())
}
if p.GetTutorialStep() != 0 {
t.Errorf("Expected tutorial step 0, got %d", p.GetTutorialStep())
}
if !p.IsPlayer() {
t.Error("Expected IsPlayer to return true")
}
// Check that maps are initialized
if p.playerQuests == nil {
t.Error("playerQuests map not initialized")
}
if p.completedQuests == nil {
t.Error("completedQuests map not initialized")
}
if p.friendList == nil {
t.Error("friendList map not initialized")
}
}
// TestPlayerManager tests the player manager functionality
func TestPlayerManager(t *testing.T) {
config := ManagerConfig{
MaxPlayers: 100,
SaveInterval: time.Minute * 5,
StatsInterval: time.Second * 30,
}
manager := NewManager(config)
if manager == nil {
t.Fatal("NewManager returned nil")
}
// Test adding a player
player := NewPlayer()
player.SetSpawnID(1001) // Set unique spawn ID
player.SetCharacterID(123)
player.SetName("TestPlayer")
player.SetLevel(10)
err := manager.AddPlayer(player)
if err != nil {
t.Fatalf("Failed to add player: %v", err)
}
// Test retrieving player by ID
retrieved := manager.GetPlayer(player.GetSpawnID())
if retrieved == nil {
t.Error("Failed to retrieve player by ID")
}
// Test retrieving player by name
byName := manager.GetPlayerByName("TestPlayer")
if byName == nil {
// Debug: Check what name was actually stored
allPlayers := manager.GetAllPlayers()
if len(allPlayers) > 0 {
t.Errorf("Failed to retrieve player by name. Player has name: %s", allPlayers[0].GetName())
} else {
t.Error("Failed to retrieve player by name. No players in manager")
}
}
// Test retrieving player by character ID
byCharID := manager.GetPlayerByCharacterID(123)
if byCharID == nil {
t.Error("Failed to retrieve player by character ID")
}
// Test player count
count := manager.GetPlayerCount()
if count != 1 {
t.Errorf("Expected player count 1, got %d", count)
}
// Test removing player
err = manager.RemovePlayer(player.GetSpawnID())
if err != nil {
t.Fatalf("Failed to remove player: %v", err)
}
count = manager.GetPlayerCount()
if count != 0 {
t.Errorf("Expected player count 0 after removal, got %d", count)
}
}
// TestPlayerDatabase tests database operations
func TestPlayerDatabase(t *testing.T) {
// Create in-memory database for testing
conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate)
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
defer conn.Close()
// Create test table
createTable := `
CREATE TABLE IF NOT EXISTS characters (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
level INTEGER DEFAULT 1,
race INTEGER DEFAULT 0,
class INTEGER DEFAULT 0,
zone_id INTEGER DEFAULT 0,
x REAL DEFAULT 0,
y REAL DEFAULT 0,
z REAL DEFAULT 0,
heading REAL DEFAULT 0,
created_date TEXT,
last_save TEXT
)`
err = sqlitex.Execute(conn, createTable, nil)
if err != nil {
t.Fatalf("Failed to create table: %v", err)
}
db := NewPlayerDatabase(conn)
// Create test player
player := NewPlayer()
player.SetCharacterID(1)
player.SetName("TestHero")
player.SetLevel(20)
player.SetClass(1)
player.SetRace(2)
player.SetX(100.5)
player.SetY(200.5, false)
player.SetZ(300.5)
// Test saving player
err = db.SavePlayer(player)
if err != nil {
t.Fatalf("Failed to save player: %v", err)
}
// Test loading player
loaded, err := db.LoadPlayer(1)
if err != nil {
t.Fatalf("Failed to load player: %v", err)
}
loadedName := strings.TrimSpace(strings.Trim(loaded.GetName(), "\x00"))
if loadedName != "TestHero" {
t.Errorf("Expected name TestHero, got %s", loadedName)
}
loadedLevel := loaded.GetLevel()
if loadedLevel != 20 {
t.Errorf("Expected level 20, got %d", loadedLevel)
}
// Test updating player
loaded.SetLevel(21)
err = db.SavePlayer(loaded)
if err != nil {
t.Fatalf("Failed to update player: %v", err)
}
// Test deleting player
err = db.DeletePlayer(1)
if err != nil {
t.Fatalf("Failed to delete player: %v", err)
}
// Verify deletion
_, err = db.LoadPlayer(1)
if err == nil {
t.Error("Expected error loading deleted player")
}
}
// TestPlayerCombat tests combat-related functionality
func TestPlayerCombat(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
player.SetLevel(10)
player.SetHP(100)
player.SetTotalHP(100)
// Test combat state
player.InCombat(true, false)
if player.GetPlayerEngageCommands() == 0 {
t.Error("Expected player to be in combat")
}
player.InCombat(false, false)
if player.GetPlayerEngageCommands() != 0 {
t.Error("Expected player to not be in combat")
}
// Test death state
player.SetHP(0)
if !player.IsDead() {
t.Error("Expected player to be dead with 0 HP")
}
player.SetHP(50)
if player.IsDead() {
t.Error("Expected player to be alive with 50 HP")
}
// Test mentorship
player.SetMentorStats(5, 0, true)
player.EnableResetMentorship()
if !player.ResetMentorship() {
t.Error("Expected mentorship reset to succeed")
}
}
// TestPlayerExperience tests experience system
func TestPlayerExperience(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
player.SetLevel(1)
// Set initial XP
player.SetXP(0)
player.SetNeededXP(1000)
// Test XP gain
leveledUp := player.AddXP(500)
if leveledUp {
t.Error("Should not level up with 500/1000 XP")
}
currentXP := player.GetXP()
if currentXP != 500 {
t.Errorf("Expected XP 500, got %d", currentXP)
}
// Test level up
leveledUp = player.AddXP(600) // Total: 1100, should level up
if !leveledUp {
t.Error("Should level up with 1100/1000 XP")
}
newLevel := player.GetLevel()
if newLevel != 2 {
t.Errorf("Expected level 2 after level up, got %d", newLevel)
}
// Test tradeskill XP
player.SetTSXP(0)
player.SetNeededTSXP(500)
leveledUp = player.AddTSXP(600)
if !leveledUp {
t.Error("Should level up tradeskill with 600/500 XP")
}
}
// TestPlayerQuests tests quest management
func TestPlayerQuests(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Create test quest
quest := &quests.Quest{
ID: 100,
Name: "Test Quest",
}
// Test adding quest
// Note: AddQuest method doesn't exist yet - would need implementation
// player.AddQuest(quest)
player.playerQuests[100] = quest
retrieved := player.GetQuest(100)
if retrieved == nil {
t.Error("Failed to retrieve added quest")
}
allQuests := player.GetPlayerQuests()
if len(allQuests) != 1 {
t.Errorf("Expected 1 quest, got %d", len(allQuests))
}
// Test completing quest
player.RemoveQuest(100, true)
retrieved = player.GetQuest(100)
if retrieved != nil {
t.Error("Quest should be removed after completion")
}
completed := player.GetCompletedQuest(100)
if completed == nil {
t.Error("Quest should be in completed list")
}
}
// TestPlayerSkills tests skill management
func TestPlayerSkills(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test adding skill
player.AddSkill(1, 10, 100, true)
// Test skill retrieval by name
skill := player.GetSkillByName("TestSkill", false)
// Note: This will return nil since we're using stubs
_ = skill
// Test removing skill
player.RemovePlayerSkill(1, false)
}
// TestPlayerFlags tests character flag management
func TestPlayerFlags(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test setting flags
player.SetCharacterFlag(CF_ANONYMOUS)
if !player.GetCharacterFlag(CF_ANONYMOUS) {
t.Error("Expected anonymous flag to be set")
}
// Test resetting flags
player.ResetCharacterFlag(CF_ANONYMOUS)
if player.GetCharacterFlag(CF_ANONYMOUS) {
t.Error("Expected anonymous flag to be reset")
}
// Test toggle
player.ToggleCharacterFlag(CF_ANONYMOUS)
if !player.GetCharacterFlag(CF_ANONYMOUS) {
t.Error("Expected anonymous flag to be toggled on")
}
player.ToggleCharacterFlag(CF_ANONYMOUS)
if player.GetCharacterFlag(CF_ANONYMOUS) {
t.Error("Expected anonymous flag to be toggled off")
}
}
// TestPlayerFriends tests friend list management
func TestPlayerFriends(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test adding friend
player.AddFriend("BestFriend", false)
if !player.IsFriend("BestFriend") {
t.Error("Expected BestFriend to be in friend list")
}
friends := player.GetFriends()
if len(friends) != 1 {
t.Errorf("Expected 1 friend, got %d", len(friends))
}
// Test removing friend
player.RemoveFriend("BestFriend")
if player.IsFriend("BestFriend") {
t.Error("Expected BestFriend to be removed from friend list")
}
}
// TestPlayerIgnore tests ignore list management
func TestPlayerIgnore(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test adding to ignore list
player.AddIgnore("Annoying", false)
if !player.IsIgnored("Annoying") {
t.Error("Expected Annoying to be in ignore list")
}
ignored := player.GetIgnoredPlayers()
if len(ignored) != 1 {
t.Errorf("Expected 1 ignored player, got %d", len(ignored))
}
// Test removing from ignore list
player.RemoveIgnore("Annoying")
if player.IsIgnored("Annoying") {
t.Error("Expected Annoying to be removed from ignore list")
}
}
// TestPlayerMovement tests movement-related methods
func TestPlayerMovement(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test position
player.SetX(100.5)
player.SetY(200.5, false)
player.SetZ(300.5)
if player.GetX() != 100.5 {
t.Errorf("Expected X 100.5, got %f", player.GetX())
}
// Test heading
player.SetHeadingFromFloat(180.0)
heading := player.GetHeading()
_ = heading // Heading conversion is complex, just ensure it doesn't panic
// Test distance calculation
distance := player.GetDistance(150.5, 250.5, 350.5, true)
if distance <= 0 {
t.Error("Expected positive distance")
}
// Test movement speeds
player.SetSideSpeed(5.0, false)
if player.GetSideSpeed() != 5.0 {
t.Errorf("Expected side speed 5.0, got %f", player.GetSideSpeed())
}
player.SetVertSpeed(3.0, false)
if player.GetVertSpeed() != 3.0 {
t.Errorf("Expected vert speed 3.0, got %f", player.GetVertSpeed())
}
}
// TestPlayerCurrency tests currency management
func TestPlayerCurrency(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test adding coins
player.AddCoins(1000)
if !player.HasCoins(1000) {
t.Error("Expected player to have 1000 coins")
}
// Test removing coins
success := player.RemoveCoins(500)
if !success {
t.Error("Expected to successfully remove 500 coins")
}
if !player.HasCoins(500) {
t.Error("Expected player to have 500 coins remaining")
}
// Test insufficient coins
success = player.RemoveCoins(1000)
if success {
t.Error("Should not be able to remove 1000 coins when only 500 available")
}
}
// TestPlayerSpells tests spell management
func TestPlayerSpells(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Test spell book
player.AddSpellBookEntry(100, 1, 1, 0, 0, true)
hasSpell := player.HasSpell(100, 1, false, false)
if !hasSpell {
t.Error("Expected player to have spell 100")
}
// Test removing spell
player.RemoveSpellBookEntry(100, false)
hasSpell = player.HasSpell(100, 1, false, false)
if hasSpell {
t.Error("Expected spell to be removed")
}
// Test passive spells
player.ApplyPassiveSpells()
player.RemoveAllPassives()
}
// TestPlayerInfo tests PlayerInfo functionality
func TestPlayerInfo(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
info := NewPlayerInfo(player)
if info == nil {
t.Fatal("NewPlayerInfo returned nil")
}
// Test bind point
info.SetBindZone(100)
info.SetBindX(50.0)
info.SetBindY(60.0)
info.SetBindZ(70.0)
info.SetBindHeading(90.0)
// Test house zone
info.SetHouseZone(200)
// Test account age
info.SetAccountAge(365)
// Test XP calculations
player.SetXP(500)
player.SetNeededXP(1000)
info.CalculateXPPercentages()
// Test TS XP calculations
player.SetTSXP(250)
player.SetNeededTSXP(500)
info.CalculateTSXPPercentages()
}
// TestPlayerEquipment tests equipment and appearance
func TestPlayerEquipment(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
player.SetHP(100) // Set HP so player is not dead
player.SetTotalHP(100)
// Test equipment allowance check
canEquip := player.IsAllowedCombatEquip(0, false)
if !canEquip {
t.Error("Expected to be able to change equipment out of combat")
}
// Test in combat equipment change
player.InCombat(true, false)
canEquip = player.IsAllowedCombatEquip(0, false)
if canEquip {
t.Error("Should not be able to change primary weapon in combat")
}
}
// TestPlayerCleanup tests cleanup methods
func TestPlayerCleanup(t *testing.T) {
player := NewPlayer()
player.SetCharacterID(1)
// Add some data
player.AddFriend("Friend1", false)
player.AddIgnore("Ignored1", false)
quest := &quests.Quest{ID: 1, Name: "Quest1"}
// player.AddQuest(quest) - method doesn't exist yet
player.playerQuests[1] = quest
// Test cleanup
player.ClearEverything()
// Verify data is cleared
friends := player.GetFriends()
if len(friends) != 0 {
t.Error("Expected friends list to be cleared")
}
ignored := player.GetIgnoredPlayers()
if len(ignored) != 0 {
t.Error("Expected ignore list to be cleared")
}
}
// BenchmarkPlayerCreation benchmarks player creation
func BenchmarkPlayerCreation(b *testing.B) {
for i := 0; i < b.N; i++ {
p := NewPlayer()
p.SetCharacterID(int32(i))
p.SetName(fmt.Sprintf("Player%d", i))
}
}
// BenchmarkManagerOperations benchmarks manager operations
func BenchmarkManagerOperations(b *testing.B) {
config := ManagerConfig{
MaxPlayers: 1000,
}
manager := NewManager(config)
// Create players
players := make([]*Player, 100)
for i := 0; i < 100; i++ {
players[i] = NewPlayer()
players[i].SetCharacterID(int32(i))
players[i].SetName(fmt.Sprintf("Player%d", i))
players[i].SetSpawnID(int32(2000 + i)) // Unique spawn IDs
manager.AddPlayer(players[i])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Benchmark lookups
_ = manager.GetPlayer(int32(i % 100))
_ = manager.GetPlayerByCharacterID(int32(i % 100))
}
}
// TestConcurrentAccess tests thread safety
func TestConcurrentAccess(t *testing.T) {
config := ManagerConfig{
MaxPlayers: 100,
}
manager := NewManager(config)
// Start manager
err := manager.Start()
if err != nil {
t.Fatalf("Failed to start manager: %v", err)
}
defer manager.Stop()
// Concurrent player additions
done := make(chan bool, 10)
for i := 0; i < 10; i++ {
go func(id int) {
player := NewPlayer()
player.SetCharacterID(int32(id))
player.SetName(fmt.Sprintf("Player%d", id))
player.SetSpawnID(int32(1000 + id)) // Unique spawn IDs
manager.AddPlayer(player)
done <- true
}(i)
}
// Wait for all goroutines
for i := 0; i < 10; i++ {
<-done
}
// Verify all players added
count := manager.GetPlayerCount()
if count != 10 {
t.Errorf("Expected 10 players, got %d", count)
}
}