eq2go/internal/tradeskills/tradeskills_test.go

429 lines
11 KiB
Go

package tradeskills
import (
"testing"
"time"
)
func TestTradeskillEvent(t *testing.T) {
event := &TradeskillEvent{
Name: "Test Event",
Icon: 1234,
Technique: TechniqueSkillAlchemy,
SuccessProgress: 25,
SuccessDurability: 0,
SuccessHP: 0,
SuccessPower: -10,
SuccessSpellID: 0,
SuccessItemID: 0,
FailProgress: -10,
FailDurability: -25,
FailHP: 0,
FailPower: 0,
}
// Test Copy method
copied := event.Copy()
if copied == nil {
t.Fatal("Copy returned nil")
}
if copied.Name != event.Name {
t.Errorf("Expected name %s, got %s", event.Name, copied.Name)
}
if copied.Technique != event.Technique {
t.Errorf("Expected technique %d, got %d", event.Technique, copied.Technique)
}
// Test Copy with nil
var nilEvent *TradeskillEvent
copiedNil := nilEvent.Copy()
if copiedNil != nil {
t.Error("Copy of nil should return nil")
}
}
func TestTradeskillManager(t *testing.T) {
manager := NewTradeskillManager()
if manager == nil {
t.Fatal("NewTradeskillManager returned nil")
}
// Test initial state
if manager.IsClientCrafting(12345) {
t.Error("New manager should not have any active crafting sessions")
}
// Test begin crafting
request := CraftingRequest{
PlayerID: 12345,
RecipeID: 67890,
TableSpawnID: 11111,
Components: []ComponentUsage{
{ItemUniqueID: 22222, Quantity: 2},
{ItemUniqueID: 33333, Quantity: 1},
},
Quantity: 1,
}
err := manager.BeginCrafting(request)
if err != nil {
t.Fatalf("BeginCrafting failed: %v", err)
}
// Test client is now crafting
if !manager.IsClientCrafting(12345) {
t.Error("Client should be crafting after BeginCrafting")
}
// Test get tradeskill
tradeskill := manager.GetTradeskill(12345)
if tradeskill == nil {
t.Fatal("GetTradeskill returned nil")
}
if tradeskill.PlayerID != 12345 {
t.Errorf("Expected player ID 12345, got %d", tradeskill.PlayerID)
}
if tradeskill.RecipeID != 67890 {
t.Errorf("Expected recipe ID 67890, got %d", tradeskill.RecipeID)
}
// Test stop crafting
err = manager.StopCrafting(12345)
if err != nil {
t.Fatalf("StopCrafting failed: %v", err)
}
// Test client is no longer crafting
if manager.IsClientCrafting(12345) {
t.Error("Client should not be crafting after StopCrafting")
}
}
func TestTradeskillSession(t *testing.T) {
now := time.Now()
tradeskill := &Tradeskill{
PlayerID: 12345,
TableSpawnID: 11111,
RecipeID: 67890,
CurrentProgress: 500,
CurrentDurability: 800,
NextUpdateTime: now.Add(time.Second),
UsedComponents: []ComponentUsage{
{ItemUniqueID: 22222, Quantity: 2},
},
StartTime: now,
LastUpdate: now,
}
// Test completion check
if tradeskill.IsComplete() {
t.Error("Tradeskill with 500 progress should not be complete")
}
tradeskill.CurrentProgress = MaxProgress
if !tradeskill.IsComplete() {
t.Error("Tradeskill with max progress should be complete")
}
// Test failure check
if tradeskill.IsFailed() {
t.Error("Tradeskill with 800 durability should not be failed")
}
tradeskill.CurrentDurability = MinDurability
if !tradeskill.IsFailed() {
t.Error("Tradeskill with min durability should be failed")
}
// Test update check
tradeskill.NextUpdateTime = now.Add(-time.Second) // Past time
if !tradeskill.NeedsUpdate() {
t.Error("Tradeskill with past update time should need update")
}
// Test reset
tradeskill.Reset()
if tradeskill.CurrentProgress != MinProgress {
t.Errorf("Expected progress %d after reset, got %d", MinProgress, tradeskill.CurrentProgress)
}
if tradeskill.CurrentDurability != MaxDurability {
t.Errorf("Expected durability %d after reset, got %d", MaxDurability, tradeskill.CurrentDurability)
}
}
func TestMasterTradeskillEventsList(t *testing.T) {
eventsList := NewMasterTradeskillEventsList()
if eventsList == nil {
t.Fatal("NewMasterTradeskillEventsList returned nil")
}
// Test initial state
if eventsList.Size() != 0 {
t.Error("New events list should be empty")
}
// Test add event
event := &TradeskillEvent{
Name: "Test Event",
Icon: 1234,
Technique: TechniqueSkillAlchemy,
}
eventsList.AddEvent(event)
if eventsList.Size() != 1 {
t.Errorf("Expected size 1 after adding event, got %d", eventsList.Size())
}
// Test get by technique
events := eventsList.GetEventByTechnique(TechniqueSkillAlchemy)
if len(events) != 1 {
t.Errorf("Expected 1 event for alchemy, got %d", len(events))
}
if events[0].Name != "Test Event" {
t.Errorf("Expected event name 'Test Event', got %s", events[0].Name)
}
// Test get by non-existent technique
noEvents := eventsList.GetEventByTechnique(TechniqueSkillFletching)
if len(noEvents) != 0 {
t.Errorf("Expected 0 events for fletching, got %d", len(noEvents))
}
// Test add nil event
eventsList.AddEvent(nil)
if eventsList.Size() != 1 {
t.Error("Adding nil event should not change size")
}
// Test clear
eventsList.Clear()
if eventsList.Size() != 0 {
t.Error("Events list should be empty after Clear")
}
}
func TestValidTechnique(t *testing.T) {
validTechniques := []uint32{
TechniqueSkillAlchemy,
TechniqueSkillTailoring,
TechniqueSkillFletching,
TechniqueSkillJewelcrafting,
TechniqueSkillProvisioning,
TechniqueSkillScribing,
TechniqueSkillTransmuting,
TechniqueSkillArtistry,
TechniqueSkillCarpentry,
TechniqueSkillMetalworking,
TechniqueSkillMetalshaping,
TechniqueSkillStoneworking,
}
for _, technique := range validTechniques {
if !IsValidTechnique(technique) {
t.Errorf("Technique %d should be valid", technique)
}
}
// Test invalid technique
if IsValidTechnique(999999) {
t.Error("Invalid technique should not be valid")
}
}
func TestDatabaseOperations(t *testing.T) {
t.Skip("Database operations require actual database connection - skipping for basic validation")
// This test would work with a real database connection
// For now, just test that the interface methods exist and compile
// Mock database service
var dbService DatabaseService
_ = dbService // Ensure interface compiles
}
func TestAnimationMethods(t *testing.T) {
manager := NewTradeskillManager()
testCases := []struct {
technique uint32
version int16
expectNonZero bool
}{
{TechniqueSkillAlchemy, 500, true},
{TechniqueSkillAlchemy, 1000, true},
{TechniqueSkillTailoring, 500, true},
{TechniqueSkillFletching, 1000, true},
{TechniqueSkillScribing, 500, false}, // No animations for scribing
{999999, 500, false}, // Invalid technique
}
for _, tc := range testCases {
successAnim := manager.GetTechniqueSuccessAnim(tc.version, tc.technique)
failureAnim := manager.GetTechniqueFailureAnim(tc.version, tc.technique)
idleAnim := manager.GetTechniqueIdleAnim(tc.version, tc.technique)
if tc.expectNonZero {
if successAnim == 0 {
t.Errorf("Expected non-zero success animation for technique %d, version %d", tc.technique, tc.version)
}
if failureAnim == 0 {
t.Errorf("Expected non-zero failure animation for technique %d, version %d", tc.technique, tc.version)
}
if idleAnim == 0 {
t.Errorf("Expected non-zero idle animation for technique %d, version %d", tc.technique, tc.version)
}
}
}
// Test miss target animations
missAnim := manager.GetMissTargetAnim(500)
if missAnim == 0 {
t.Error("Expected non-zero miss target animation for version 500")
}
killMissAnim := manager.GetKillMissTargetAnim(1000)
if killMissAnim == 0 {
t.Error("Expected non-zero kill miss target animation for version 1000")
}
}
func TestConfigurationUpdate(t *testing.T) {
manager := NewTradeskillManager()
// Test valid configuration
err := manager.UpdateConfiguration(1.0, 2.0, 10.0, 87.0, 30.0)
if err != nil {
t.Errorf("Valid configuration update failed: %v", err)
}
// Test invalid configuration (doesn't add to 100%)
err = manager.UpdateConfiguration(1.0, 2.0, 10.0, 80.0, 30.0) // Only adds to 93%
if err != nil {
t.Errorf("Invalid configuration should not return error, should use defaults: %v", err)
}
}
func TestPacketHelper(t *testing.T) {
helper := &PacketHelper{}
// Test progress stage calculation
testCases := []struct {
progress int32
expected int8
}{
{0, 0},
{300, 0},
{400, 1},
{550, 1},
{600, 2},
{750, 2},
{800, 3},
{950, 3},
{1000, 4},
{1100, 4}, // Clamped to max
}
for _, tc := range testCases {
result := helper.CalculateProgressStage(tc.progress)
if result != tc.expected {
t.Errorf("Progress %d: expected stage %d, got %d", tc.progress, tc.expected, result)
}
}
// Test mass production quantities
quantities := helper.GetMassProductionQuantities(3)
if len(quantities) == 0 {
t.Error("Should return at least base quantity")
}
if quantities[0] != 1 {
t.Error("First quantity should always be 1")
}
// Test component validation
components := []ComponentUsage{
{ItemUniqueID: 12345, Quantity: 2},
{ItemUniqueID: 67890, Quantity: 1},
}
err := helper.ValidateRecipeComponents(12345, components)
if err != nil {
t.Errorf("Valid components should pass validation: %v", err)
}
// Test invalid components
invalidComponents := []ComponentUsage{
{ItemUniqueID: 0, Quantity: 2}, // Invalid unique ID
}
err = helper.ValidateRecipeComponents(12345, invalidComponents)
if err == nil {
t.Error("Invalid components should fail validation")
}
// Test packet type calculation
packetType := helper.CalculateItemPacketType(500)
if packetType == 0 {
t.Error("Should return non-zero packet type")
}
// Test value clamping
clampedProgress := helper.ClampProgress(-100)
if clampedProgress != MinProgress {
t.Errorf("Expected clamped progress %d, got %d", MinProgress, clampedProgress)
}
clampedDurability := helper.ClampDurability(2000)
if clampedDurability != MaxDurability {
t.Errorf("Expected clamped durability %d, got %d", MaxDurability, clampedDurability)
}
}
func BenchmarkTradeskillManagerProcess(b *testing.B) {
manager := NewTradeskillManager()
// Add some test sessions
for i := 0; i < 10; i++ {
request := CraftingRequest{
PlayerID: uint32(i + 1),
RecipeID: 67890,
TableSpawnID: 11111,
Components: []ComponentUsage{
{ItemUniqueID: 22222, Quantity: 2},
},
Quantity: 1,
}
manager.BeginCrafting(request)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
manager.Process()
}
}
func BenchmarkEventListAccess(b *testing.B) {
eventsList := NewMasterTradeskillEventsList()
// Add test events
for i := 0; i < 100; i++ {
event := &TradeskillEvent{
Name: "Test Event",
Icon: int16(i),
Technique: TechniqueSkillAlchemy,
}
eventsList.AddEvent(event)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
eventsList.GetEventByTechnique(TechniqueSkillAlchemy)
}
}