package classes import ( "strings" "testing" ) func TestClassBasics(t *testing.T) { class := &Class{ ID: ClassWarrior, Name: "WARRIOR", DisplayName: "Warrior", BaseClass: ClassFighter, SecondaryBaseClass: ClassCommoner, ClassType: ClassTypeAdventure, IsAdventure: true, IsTradeskill: false, } if class.GetID() != ClassWarrior { t.Errorf("Expected ID %d, got %d", ClassWarrior, class.GetID()) } if class.GetName() != "WARRIOR" { t.Errorf("Expected name 'WARRIOR', got %s", class.GetName()) } if class.GetDisplayName() != "Warrior" { t.Errorf("Expected display name 'Warrior', got %s", class.GetDisplayName()) } if class.GetBaseClass() != ClassFighter { t.Errorf("Expected base class %d, got %d", ClassFighter, class.GetBaseClass()) } if class.GetSecondaryBaseClass() != ClassCommoner { t.Errorf("Expected secondary base class %d, got %d", ClassCommoner, class.GetSecondaryBaseClass()) } if !class.IsValid() { t.Error("Expected class to be valid") } // Test invalid class invalidClass := &Class{ID: int8(99)} if invalidClass.IsValid() { t.Error("Expected invalid class to be invalid") } } func TestManagerCreation(t *testing.T) { manager := NewManager() if manager == nil { t.Fatal("Manager creation failed") } stats := manager.GetStatistics() if stats["classes_loaded"].(int32) == 0 { t.Error("Expected classes to be loaded during initialization") } if stats["total_classes"].(int32) != MaxClasses { t.Errorf("Expected %d total classes, got %d", MaxClasses, stats["total_classes"]) } t.Logf("Manager initialized with %d classes", stats["total_classes"]) } func TestGetClassID(t *testing.T) { manager := NewManager() // Test valid class names (C++ API compatibility) testCases := map[string]int8{ "WARRIOR": ClassWarrior, "GUARDIAN": ClassGuardian, "TEMPLAR": ClassTemplar, "WIZARD": ClassWizard, "RANGER": ClassRanger, "BEASTLORD": ClassBeastlord, "CHANNELER": ClassChanneler, "ARTISAN": ClassArtisan, "PROVISIONER": ClassProvisioner, "ALCHEMIST": ClassAlchemist, "COMMONER": ClassCommoner, } for name, expectedID := range testCases { id := manager.GetClassID(name) if id != expectedID { t.Errorf("GetClassID('%s'): expected %d, got %d", name, expectedID, id) } // Test case insensitivity and whitespace handling id = manager.GetClassID(" " + strings.ToLower(name) + " ") if id != expectedID { t.Errorf("GetClassID('%s') with whitespace and lowercase: expected %d, got %d", name, expectedID, id) } } // Test invalid class name invalidID := manager.GetClassID("INVALID_CLASS") if invalidID != -1 { t.Errorf("Expected -1 for invalid class name, got %d", invalidID) } // Test empty string emptyID := manager.GetClassID("") if emptyID != -1 { t.Errorf("Expected -1 for empty class name, got %d", emptyID) } } func TestGetClassName(t *testing.T) { manager := NewManager() // Test valid class IDs (C++ API compatibility) testCases := map[int8]string{ ClassWarrior: "WARRIOR", ClassGuardian: "GUARDIAN", ClassTemplar: "TEMPLAR", ClassWizard: "WIZARD", ClassRanger: "RANGER", ClassBeastlord: "BEASTLORD", ClassChanneler: "CHANNELER", ClassArtisan: "ARTISAN", ClassProvisioner: "PROVISIONER", ClassAlchemist: "ALCHEMIST", ClassCommoner: "COMMONER", } for classID, expectedName := range testCases { name := manager.GetClassName(classID) if name != expectedName { t.Errorf("GetClassName(%d): expected '%s', got '%s'", classID, expectedName, name) } } // Test invalid class ID invalidName := manager.GetClassName(int8(99)) if invalidName != "" { t.Errorf("Expected empty string for invalid class ID, got '%s'", invalidName) } } func TestGetClassNameCase(t *testing.T) { manager := NewManager() // Test display names (C++ API compatibility) testCases := map[int8]string{ ClassWarrior: "Warrior", ClassGuardian: "Guardian", ClassTemplar: "Templar", ClassWizard: "Wizard", ClassRanger: "Ranger", ClassBeastlord: "Beastlord", ClassChanneler: "Channeler", ClassArtisan: "Artisan", ClassProvisioner: "Provisioner", ClassAlchemist: "Alchemist", ClassCommoner: "Commoner", } for classID, expectedDisplayName := range testCases { displayName := manager.GetClassNameCase(classID) if displayName != expectedDisplayName { t.Errorf("GetClassNameCase(%d): expected '%s', got '%s'", classID, expectedDisplayName, displayName) } } // Test invalid class ID invalidDisplayName := manager.GetClassNameCase(int8(99)) if invalidDisplayName != "" { t.Errorf("Expected empty string for invalid class ID, got '%s'", invalidDisplayName) } } func TestGetBaseClass(t *testing.T) { manager := NewManager() // Test base class mappings (C++ API compatibility) testCases := map[int8]int8{ // Fighter archetype ClassWarrior: ClassFighter, ClassGuardian: ClassFighter, ClassBerserker: ClassFighter, ClassMonk: ClassFighter, ClassShadowknight: ClassFighter, ClassPaladin: ClassFighter, // Priest archetype ClassCleric: ClassPriest, ClassTemplar: ClassPriest, ClassInquisitor: ClassPriest, ClassWarden: ClassPriest, ClassFury: ClassPriest, ClassMystic: ClassPriest, ClassDefiler: ClassPriest, ClassChanneler: ClassPriest, // Mage archetype ClassSorcerer: ClassMage, ClassWizard: ClassMage, ClassWarlock: ClassMage, ClassIllusionist: ClassMage, ClassCoercer: ClassMage, ClassConjuror: ClassMage, ClassNecromancer: ClassMage, // Scout archetype ClassRogue: ClassScout, ClassSwashbuckler: ClassScout, ClassBrigand: ClassScout, ClassTroubador: ClassScout, ClassDirge: ClassScout, ClassRanger: ClassScout, ClassAssassin: ClassScout, ClassBeastlord: ClassScout, // Base classes should return themselves ClassFighter: ClassFighter, ClassPriest: ClassPriest, ClassMage: ClassMage, ClassScout: ClassScout, ClassCommoner: ClassCommoner, } for classID, expectedBase := range testCases { base := manager.GetBaseClass(classID) if base != expectedBase { t.Errorf("GetBaseClass(%d): expected %d, got %d", classID, expectedBase, base) } } // Test invalid class ID invalidBase := manager.GetBaseClass(int8(99)) if invalidBase != ClassCommoner { t.Errorf("Expected %d (Commoner) for invalid class ID, got %d", ClassCommoner, invalidBase) } } func TestGetSecondaryBaseClass(t *testing.T) { manager := NewManager() // Test secondary base class mappings (C++ API compatibility) testCases := map[int8]int8{ ClassGuardian: ClassWarrior, ClassBerserker: ClassWarrior, ClassMonk: ClassBrawler, ClassBruiser: ClassBrawler, ClassShadowknight: ClassCrusader, ClassPaladin: ClassCrusader, ClassTemplar: ClassCleric, ClassInquisitor: ClassCleric, ClassWarden: ClassDruid, ClassFury: ClassDruid, ClassMystic: ClassShaman, ClassDefiler: ClassShaman, ClassWizard: ClassSorcerer, ClassWarlock: ClassSorcerer, ClassIllusionist: ClassEnchanter, ClassCoercer: ClassEnchanter, ClassConjuror: ClassSummoner, ClassNecromancer: ClassSummoner, ClassSwashbuckler: ClassRogue, ClassBrigand: ClassRogue, ClassTroubador: ClassBard, ClassDirge: ClassBard, ClassRanger: ClassPredator, ClassAssassin: ClassPredator, ClassBeastlord: ClassAnimalist, ClassChanneler: ClassShaper, } for classID, expectedSecondary := range testCases { secondary := manager.GetSecondaryBaseClass(classID) if secondary != expectedSecondary { t.Errorf("GetSecondaryBaseClass(%d): expected %d, got %d", classID, expectedSecondary, secondary) } } // Test base classes (should return Commoner) baseClasses := []int8{ClassCommoner, ClassFighter, ClassPriest, ClassMage, ClassScout} for _, classID := range baseClasses { secondary := manager.GetSecondaryBaseClass(classID) if secondary != ClassCommoner { t.Errorf("GetSecondaryBaseClass(%d): expected %d (Commoner), got %d", classID, ClassCommoner, secondary) } } } func TestClassValidation(t *testing.T) { manager := NewManager() // Test valid class IDs validClasses := []int8{ ClassCommoner, ClassFighter, ClassWarrior, ClassGuardian, ClassTemplar, ClassWizard, ClassRanger, ClassBeastlord, ClassChanneler, ClassArtisan, ClassAlchemist, } for _, classID := range validClasses { if !manager.IsValidClassID(classID) { t.Errorf("Expected class ID %d to be valid", classID) } } // Test invalid class IDs invalidClasses := []int8{-1, -10, 58, 99, 127} for _, classID := range invalidClasses { if manager.IsValidClassID(classID) { t.Errorf("Expected class ID %d to be invalid", classID) } } // Test boundary conditions if !manager.IsValidClassID(MinClassID) { t.Errorf("Expected MinClassID (%d) to be valid", MinClassID) } if !manager.IsValidClassID(MaxClassID) { t.Errorf("Expected MaxClassID (%d) to be valid", MaxClassID) } } func TestClassTypeChecking(t *testing.T) { manager := NewManager() // Test adventure classes adventureClasses := []int8{ ClassCommoner, ClassFighter, ClassWarrior, ClassGuardian, ClassTemplar, ClassWizard, ClassRanger, ClassBeastlord, ClassChanneler, } for _, classID := range adventureClasses { if !manager.IsAdventureClass(classID) { t.Errorf("Expected class ID %d to be an adventure class", classID) } if manager.IsTradeskillClass(classID) { t.Errorf("Expected class ID %d to not be a tradeskill class", classID) } if manager.GetClassType(classID) != ClassTypeAdventure { t.Errorf("Expected class ID %d to have type '%s', got '%s'", classID, ClassTypeAdventure, manager.GetClassType(classID)) } } // Test tradeskill classes tradeskillClasses := []int8{ ClassArtisan, ClassCraftsman, ClassProvisioner, ClassOutfitter, ClassArmorer, ClassScholar, ClassJeweler, ClassAlchemist, } for _, classID := range tradeskillClasses { if manager.IsAdventureClass(classID) { t.Errorf("Expected class ID %d to not be an adventure class", classID) } if !manager.IsTradeskillClass(classID) { t.Errorf("Expected class ID %d to be a tradeskill class", classID) } if manager.GetClassType(classID) != ClassTypeTradeskill { t.Errorf("Expected class ID %d to have type '%s', got '%s'", classID, ClassTypeTradeskill, manager.GetClassType(classID)) } } } func TestClassHierarchy(t *testing.T) { manager := NewManager() // Test Guardian hierarchy: Guardian -> Warrior -> Fighter -> Commoner guardianHierarchy := manager.GetClassHierarchy(ClassGuardian) expectedGuardian := []int8{ClassGuardian, ClassWarrior, ClassFighter, ClassCommoner} if len(guardianHierarchy) != len(expectedGuardian) { t.Errorf("Guardian hierarchy length: expected %d, got %d", len(expectedGuardian), len(guardianHierarchy)) } else { for i, expected := range expectedGuardian { if i < len(guardianHierarchy) && guardianHierarchy[i] != expected { t.Errorf("Guardian hierarchy[%d]: expected %d, got %d", i, expected, guardianHierarchy[i]) } } } // Test Templar hierarchy: Templar -> Cleric -> Priest -> Commoner templarHierarchy := manager.GetClassHierarchy(ClassTemplar) expectedTemplar := []int8{ClassTemplar, ClassCleric, ClassPriest, ClassCommoner} if len(templarHierarchy) != len(expectedTemplar) { t.Errorf("Templar hierarchy length: expected %d, got %d", len(expectedTemplar), len(templarHierarchy)) } else { for i, expected := range expectedTemplar { if i < len(templarHierarchy) && templarHierarchy[i] != expected { t.Errorf("Templar hierarchy[%d]: expected %d, got %d", i, expected, templarHierarchy[i]) } } } // Test Commoner hierarchy: just Commoner commonerHierarchy := manager.GetClassHierarchy(ClassCommoner) if len(commonerHierarchy) != 1 || commonerHierarchy[0] != ClassCommoner { t.Errorf("Commoner hierarchy: expected [%d], got %v", ClassCommoner, commonerHierarchy) } // Test invalid class invalidHierarchy := manager.GetClassHierarchy(int8(99)) if invalidHierarchy != nil { t.Errorf("Expected nil hierarchy for invalid class, got %v", invalidHierarchy) } } func TestArchetypeOperations(t *testing.T) { manager := NewManager() // Test same archetype checking if !manager.IsSameArchetype(ClassWarrior, ClassGuardian) { t.Error("Expected Warrior and Guardian to be same archetype (Fighter)") } if !manager.IsSameArchetype(ClassTemplar, ClassMystic) { t.Error("Expected Templar and Mystic to be same archetype (Priest)") } if manager.IsSameArchetype(ClassWarrior, ClassTemplar) { t.Error("Expected Warrior and Templar to be different archetypes") } // Test archetype class listing fighterClasses := manager.GetArchetypeClasses(ClassFighter) expectedFighterCount := 10 // Including base Fighter class (Fighter, Warrior, Guardian, Berserker, Brawler, Monk, Bruiser, Crusader, Shadowknight, Paladin) if len(fighterClasses) != expectedFighterCount { t.Errorf("Expected %d Fighter archetype classes, got %d", expectedFighterCount, len(fighterClasses)) } // Verify Fighter is in the list foundFighter := false for _, classID := range fighterClasses { if classID == ClassFighter { foundFighter = true break } } if !foundFighter { t.Error("Expected Fighter base class to be in Fighter archetype list") } } func TestTradeskillClassOperations(t *testing.T) { manager := NewManager() // Test tradeskill base class operations (C++ API compatibility) // Note: These functions use specific C++ logic with offsets // Test with adventure class (should return as-is for most) warriorTS := manager.GetTSBaseClass(ClassWarrior) if warriorTS != ClassWarrior { t.Errorf("Expected GetTSBaseClass(%d) to return %d, got %d", ClassWarrior, ClassWarrior, warriorTS) } // Test secondary tradeskill operations warriorSecondaryTS := manager.GetSecondaryTSBaseClass(ClassWarrior) // This should follow the C++ logic with +42 offset calculations t.Logf("GetSecondaryTSBaseClass(ClassWarrior) = %d", warriorSecondaryTS) } func TestManagerStatistics(t *testing.T) { manager := NewManager() // Perform some operations to generate statistics manager.GetClassID("WARRIOR") manager.GetClassName(ClassTemplar) manager.GetClassNameCase(ClassRanger) manager.GetBaseClass(ClassGuardian) manager.GetClass(ClassBeastlord) manager.GetClassID("INVALID_CLASS") // This should increment cache misses stats := manager.GetStatistics() // Check basic statistics if stats["classes_loaded"].(int32) == 0 { t.Error("Expected classes to be loaded") } if stats["lookups_performed"].(int32) == 0 { t.Error("Expected lookups to be performed") } if stats["total_classes"].(int32) != MaxClasses { t.Errorf("Expected %d total classes, got %d", MaxClasses, stats["total_classes"]) } // Check adventure vs tradeskill counts adventureCount := stats["adventure_classes"].(int32) tradeskillCount := stats["tradeskill_classes"].(int32) if adventureCount == 0 { t.Error("Expected some adventure classes") } if tradeskillCount == 0 { t.Error("Expected some tradeskill classes") } t.Logf("Statistics: %+v", stats) } func TestGlobalFunctions(t *testing.T) { // Test global functions that should initialize manager automatically // Test basic operations id := GetClassID("WARRIOR") if id != ClassWarrior { t.Errorf("GetClassID('WARRIOR'): expected %d, got %d", ClassWarrior, id) } name := GetClassName(ClassTemplar) if name != "TEMPLAR" { t.Errorf("GetClassName(%d): expected 'TEMPLAR', got '%s'", ClassTemplar, name) } displayName := GetClassNameCase(ClassRanger) if displayName != "Ranger" { t.Errorf("GetClassNameCase(%d): expected 'Ranger', got '%s'", ClassRanger, displayName) } base := GetBaseClass(ClassGuardian) if base != ClassFighter { t.Errorf("GetBaseClass(%d): expected %d, got %d", ClassGuardian, ClassFighter, base) } secondary := GetSecondaryBaseClass(ClassGuardian) if secondary != ClassWarrior { t.Errorf("GetSecondaryBaseClass(%d): expected %d, got %d", ClassGuardian, ClassWarrior, secondary) } // Test utility functions if !IsValidClassID(ClassBeastlord) { t.Errorf("Expected ClassBeastlord (%d) to be valid", ClassBeastlord) } if !IsAdventureClass(ClassChanneler) { t.Errorf("Expected ClassChanneler (%d) to be adventure class", ClassChanneler) } if !IsTradeskillClass(ClassAlchemist) { t.Errorf("Expected ClassAlchemist (%d) to be tradeskill class", ClassAlchemist) } classType := GetClassType(ClassWarrior) if classType != ClassTypeAdventure { t.Errorf("Expected ClassWarrior type to be '%s', got '%s'", ClassTypeAdventure, classType) } // Test collection functions allClasses := GetAllClasses() if len(allClasses) != MaxClasses { t.Errorf("Expected %d classes in GetAllClasses(), got %d", MaxClasses, len(allClasses)) } info := GetClassInfo(ClassTemplar) if !info["valid"].(bool) { t.Error("Expected ClassTemplar info to be valid") } hierarchy := GetClassHierarchy(ClassGuardian) if len(hierarchy) == 0 { t.Error("Expected non-empty hierarchy for ClassGuardian") } if !IsSameArchetype(ClassWarrior, ClassGuardian) { t.Error("Expected Warrior and Guardian to be same archetype") } fighterClasses := GetArchetypeClasses(ClassFighter) if len(fighterClasses) == 0 { t.Error("Expected non-empty list of Fighter archetype classes") } }