package recipes import ( "testing" ) // Test Recipe creation and basic operations func TestNewRecipe(t *testing.T) { recipe := NewRecipe() if recipe == nil { t.Error("NewRecipe should not return nil") } if recipe.Components == nil { t.Error("Components map should be initialized") } if recipe.Products == nil { t.Error("Products map should be initialized") } } func TestNewRecipeFromRecipe(t *testing.T) { // Test with nil source recipe := NewRecipeFromRecipe(nil) if recipe == nil { t.Error("NewRecipeFromRecipe with nil should return valid recipe") } // Test with valid source source := NewRecipe() source.ID = 12345 source.Name = "Test Recipe" source.Level = 50 source.Tier = 5 source.Icon = 100 source.Components[0] = []int32{1001, 1002} source.Products[0] = &RecipeProducts{ ProductID: 2001, ProductQty: 1, } copied := NewRecipeFromRecipe(source) if copied == nil { t.Error("NewRecipeFromRecipe should not return nil") } if copied.ID != source.ID { t.Error("Copied recipe should have same ID") } if copied.Name != source.Name { t.Error("Copied recipe should have same name") } if copied.Level != source.Level { t.Error("Copied recipe should have same level") } if copied.Tier != source.Tier { t.Error("Copied recipe should have same tier") } // Check component copying if len(copied.Components[0]) != len(source.Components[0]) { t.Error("Components should be copied") } // Check product copying if copied.Products[0] == nil { t.Error("Products should be copied") } if copied.Products[0].ProductID != source.Products[0].ProductID { t.Error("Product data should be copied correctly") } } func TestRecipeIsValid(t *testing.T) { // Test invalid recipe (empty) recipe := NewRecipe() if recipe.IsValid() { t.Error("Empty recipe should not be valid") } // Test valid recipe recipe.ID = 12345 recipe.Name = "Valid Recipe" recipe.Level = 50 recipe.Tier = 5 // Need valid tier for validation if !recipe.IsValid() { t.Error("Recipe with valid data should be valid") } // Test invalid ID recipe.ID = 0 if recipe.IsValid() { t.Error("Recipe with ID 0 should not be valid") } // Test empty name recipe.ID = 12345 recipe.Name = "" if recipe.IsValid() { t.Error("Recipe with empty name should not be valid") } // Test invalid level recipe.Name = "Valid Recipe" recipe.Tier = 5 recipe.Level = -1 if recipe.IsValid() { t.Error("Recipe with negative level should not be valid") } recipe.Level = 101 if recipe.IsValid() { t.Error("Recipe with level > 100 should not be valid") } // Test invalid tier recipe.Level = 50 recipe.Tier = 0 if recipe.IsValid() { t.Error("Recipe with tier 0 should not be valid") } recipe.Tier = 11 if recipe.IsValid() { t.Error("Recipe with tier > 10 should not be valid") } } func TestRecipeGetTotalBuildComponents(t *testing.T) { recipe := NewRecipe() // Test with no build components if recipe.GetTotalBuildComponents() != 0 { t.Error("Recipe with no build components should return 0") } // Add build components to different slots recipe.Components[SlotBuild1] = []int32{1001} recipe.Components[SlotBuild3] = []int32{1003, 1004} count := recipe.GetTotalBuildComponents() if count != 2 { t.Errorf("Expected 2 build component slots, got %d", count) } } func TestRecipeCanUseRecipeByClass(t *testing.T) { recipe := NewRecipe() // Test "any class" recipe recipe.Classes = 2 if !recipe.CanUseRecipeByClass(1) { t.Error("Any class recipe should be usable by any class") } // Test specific class requirement recipe.Classes = 1 << 3 // Bit 3 set (class ID 3) if !recipe.CanUseRecipeByClass(3) { t.Error("Recipe should be usable by matching class") } if recipe.CanUseRecipeByClass(2) { t.Error("Recipe should not be usable by non-matching class") } } func TestRecipeComponentOperations(t *testing.T) { recipe := NewRecipe() // Test getting components by slot recipe.Components[0] = []int32{1001, 1002, 1003} components := recipe.GetComponentsBySlot(0) if len(components) != 3 { t.Error("Should return all components for slot") } // Test with empty slot emptyComponents := recipe.GetComponentsBySlot(1) if len(emptyComponents) != 0 { t.Error("Empty slot should return empty slice") } } func TestRecipeProductOperations(t *testing.T) { recipe := NewRecipe() // Test getting products for stage recipe.Products[0] = &RecipeProducts{ ProductID: 2001, ProductQty: 1, ByproductID: 2002, ByproductQty: 2, } products := recipe.GetProductsForStage(0) if products == nil { t.Error("Should return products for stage") } if products.ProductID != 2001 { t.Error("Product ID should match") } if products.ByproductID != 2002 { t.Error("Byproduct ID should match") } // Test with empty stage emptyProducts := recipe.GetProductsForStage(1) if emptyProducts != nil { t.Error("Empty stage should return nil") } } // Test MasterRecipeList operations func TestNewMasterRecipeList(t *testing.T) { list := NewMasterRecipeList() if list == nil { t.Error("NewMasterRecipeList should not return nil") } if list.recipes == nil { t.Error("Recipes map should be initialized") } if list.stats == nil { t.Error("Statistics should be initialized") } } func TestMasterRecipeListAddRecipe(t *testing.T) { list := NewMasterRecipeList() // Test adding nil recipe if list.AddRecipe(nil) { t.Error("Adding nil recipe should fail") } // Test adding invalid recipe invalidRecipe := NewRecipe() if list.AddRecipe(invalidRecipe) { t.Error("Adding invalid recipe should fail") } // Test adding valid recipe validRecipe := NewRecipe() validRecipe.ID = 12345 validRecipe.Name = "Test Recipe" validRecipe.Level = 50 validRecipe.Tier = 5 // Need valid tier if !list.AddRecipe(validRecipe) { t.Error("Adding valid recipe should succeed") } // Test adding duplicate recipe duplicate := NewRecipe() duplicate.ID = 12345 duplicate.Name = "Duplicate" duplicate.Level = 60 if list.AddRecipe(duplicate) { t.Error("Adding duplicate recipe ID should fail") } } func TestMasterRecipeListGetRecipe(t *testing.T) { list := NewMasterRecipeList() // Test getting non-existent recipe recipe := list.GetRecipe(99999) if recipe != nil { t.Error("Getting non-existent recipe should return nil") } // Add a recipe testRecipe := NewRecipe() testRecipe.ID = 12345 testRecipe.Name = "Test Recipe" testRecipe.Level = 50 testRecipe.Tier = 5 // Need valid tier list.AddRecipe(testRecipe) // Test getting existing recipe retrieved := list.GetRecipe(12345) if retrieved == nil { t.Error("Getting existing recipe should not return nil") } if retrieved.ID != 12345 { t.Error("Retrieved recipe should have correct ID") } } func TestMasterRecipeListSize(t *testing.T) { list := NewMasterRecipeList() // Test empty list if list.Size() != 0 { t.Error("Empty list should have size 0") } // Add recipes for i := 1; i <= 3; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Test Recipe" recipe.Level = 50 recipe.Tier = 5 list.AddRecipe(recipe) } if list.Size() != 3 { t.Error("List with 3 recipes should have size 3") } } func TestMasterRecipeListClear(t *testing.T) { list := NewMasterRecipeList() // Add some recipes for i := 1; i <= 3; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Test Recipe" recipe.Level = 50 recipe.Tier = 5 list.AddRecipe(recipe) } // Clear the list list.ClearRecipes() if list.Size() != 0 { t.Error("Cleared list should have size 0") } if list.GetRecipe(1) != nil { t.Error("Recipe should not exist after clear") } } func TestMasterRecipeListGetRecipesByTier(t *testing.T) { list := NewMasterRecipeList() // Add recipes with different tiers for i := 1; i <= 5; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Test Recipe" recipe.Level = int8(i * 10) recipe.Tier = int8(i) list.AddRecipe(recipe) } // Get recipes for tier 3 tier3Recipes := list.GetRecipesByTier(3) if len(tier3Recipes) != 1 { t.Errorf("Expected 1 recipe for tier 3, got %d", len(tier3Recipes)) } if tier3Recipes[0].Tier != 3 { t.Error("Recipe should have tier 3") } // Get recipes for non-existent tier emptyTier := list.GetRecipesByTier(99) if len(emptyTier) != 0 { t.Error("Non-existent tier should return empty slice") } } func TestMasterRecipeListGetRecipesBySkill(t *testing.T) { list := NewMasterRecipeList() // Add recipes with different skills skillRecipe1 := NewRecipe() skillRecipe1.ID = 1 skillRecipe1.Name = "Skill Recipe 1" skillRecipe1.Level = 50 skillRecipe1.Tier = 5 skillRecipe1.Skill = 100 list.AddRecipe(skillRecipe1) skillRecipe2 := NewRecipe() skillRecipe2.ID = 2 skillRecipe2.Name = "Skill Recipe 2" skillRecipe2.Level = 60 skillRecipe2.Tier = 6 skillRecipe2.Skill = 100 list.AddRecipe(skillRecipe2) differentSkillRecipe := NewRecipe() differentSkillRecipe.ID = 3 differentSkillRecipe.Name = "Different Skill Recipe" differentSkillRecipe.Level = 50 differentSkillRecipe.Tier = 5 differentSkillRecipe.Skill = 200 list.AddRecipe(differentSkillRecipe) // Get recipes for skill 100 skill100Recipes := list.GetRecipesBySkill(100) if len(skill100Recipes) != 2 { t.Errorf("Expected 2 recipes for skill 100, got %d", len(skill100Recipes)) } // Get recipes for skill 200 skill200Recipes := list.GetRecipesBySkill(200) if len(skill200Recipes) != 1 { t.Errorf("Expected 1 recipe for skill 200, got %d", len(skill200Recipes)) } } // Test PlayerRecipeList operations func TestNewPlayerRecipeList(t *testing.T) { playerList := NewPlayerRecipeList() if playerList == nil { t.Error("NewPlayerRecipeList should not return nil") } if playerList.recipes == nil { t.Error("Recipes map should be initialized") } } func TestPlayerRecipeListAddRecipe(t *testing.T) { playerList := NewPlayerRecipeList() // Test adding nil recipe if playerList.AddRecipe(nil) { t.Error("Adding nil recipe should fail") } // Test adding valid recipe recipe := NewRecipe() recipe.ID = 12345 recipe.Name = "Test Recipe" recipe.Level = 50 recipe.Tier = 5 if !playerList.AddRecipe(recipe) { t.Error("Adding valid recipe should succeed") } // Test adding duplicate recipe duplicate := NewRecipe() duplicate.ID = 12345 duplicate.Name = "Duplicate" duplicate.Level = 60 if playerList.AddRecipe(duplicate) { t.Error("Adding duplicate recipe should fail") } } func TestPlayerRecipeListGetRecipe(t *testing.T) { playerList := NewPlayerRecipeList() // Test checking non-existent recipe if playerList.GetRecipe(99999) != nil { t.Error("Player should not have non-existent recipe") } // Add a recipe recipe := NewRecipe() recipe.ID = 12345 recipe.Name = "Test Recipe" recipe.Level = 50 recipe.Tier = 5 playerList.AddRecipe(recipe) // Test checking existing recipe if playerList.GetRecipe(12345) == nil { t.Error("Player should have added recipe") } } func TestPlayerRecipeListSize(t *testing.T) { playerList := NewPlayerRecipeList() // Test empty list if playerList.Size() != 0 { t.Error("Empty player recipe list should have size 0") } // Add recipes for i := 1; i <= 3; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Test Recipe" recipe.Level = 50 recipe.Tier = 5 playerList.AddRecipe(recipe) } if playerList.Size() != 3 { t.Error("Player recipe list with 3 recipes should have size 3") } } // Test Recipe Book List operations func TestNewMasterRecipeBookList(t *testing.T) { bookList := NewMasterRecipeBookList() if bookList == nil { t.Error("NewMasterRecipeBookList should not return nil") } } func TestMasterRecipeBookListOperations(t *testing.T) { bookList := NewMasterRecipeBookList() // Test adding recipe book recipeBook := NewRecipe() recipeBook.ID = 5001 recipeBook.BookID = 5001 // Need BookID for recipe books recipeBook.Name = "Test Recipe Book" recipeBook.Level = 1 recipeBook.Tier = 1 if !bookList.AddRecipeBook(recipeBook) { t.Error("Adding valid recipe book should succeed") } // Test getting recipe book retrieved := bookList.GetRecipeBook(5001) if retrieved == nil { t.Error("Should retrieve added recipe book") } if retrieved.ID != 5001 { t.Error("Retrieved recipe book should have correct ID") } // Test size if bookList.Size() != 1 { t.Error("Recipe book list should have size 1") } } func TestNewPlayerRecipeBookList(t *testing.T) { playerBookList := NewPlayerRecipeBookList() if playerBookList == nil { t.Error("NewPlayerRecipeBookList should not return nil") } } // Test RecipeManager operations func TestNewRecipeManager(t *testing.T) { manager := NewRecipeManager() if manager == nil { t.Error("NewRecipeManager should not return nil") } if manager.masterRecipeList == nil { t.Error("Master recipe list should be initialized") } if manager.masterRecipeBookList == nil { t.Error("Master recipe book list should be initialized") } if manager.loadedRecipes == nil { t.Error("Loaded recipes map should be initialized") } if manager.loadedRecipeBooks == nil { t.Error("Loaded recipe books map should be initialized") } } func TestRecipeManagerGetters(t *testing.T) { manager := NewRecipeManager() // Test getting master lists masterList := manager.GetMasterRecipeList() if masterList == nil { t.Error("Should return master recipe list") } masterBookList := manager.GetMasterRecipeBookList() if masterBookList == nil { t.Error("Should return master recipe book list") } // Test getting non-existent recipe recipe := manager.GetRecipe(99999) if recipe != nil { t.Error("Non-existent recipe should return nil") } // Test getting non-existent recipe book book := manager.GetRecipeBook(99999) if book != nil { t.Error("Non-existent recipe book should return nil") } } func TestRecipeManagerStatistics(t *testing.T) { manager := NewRecipeManager() // Test getting statistics when enabled manager.GetStatistics() // Call method without assignment to avoid lock copy // Test disabling statistics manager.SetStatisticsEnabled(false) manager.GetStatistics() // Call method without assignment to avoid lock copy // Test enabling statistics manager.SetStatisticsEnabled(true) } func TestRecipeManagerValidation(t *testing.T) { manager := NewRecipeManager() // Test validation of empty manager issues := manager.Validate() if len(issues) > 0 { // Manager validation might find issues, that's fine } } func TestRecipeManagerSize(t *testing.T) { manager := NewRecipeManager() // Test size of empty manager recipes, books := manager.Size() if recipes != 0 { t.Error("Empty manager should have 0 recipes") } if books != 0 { t.Error("Empty manager should have 0 recipe books") } } // Test RecipeComponent operations func TestRecipeComponent(t *testing.T) { component := &RecipeComponent{ ItemID: 1001, Slot: 0, } if component.ItemID != 1001 { t.Error("Component item ID should be set correctly") } if component.Slot != 0 { t.Error("Component slot should be set correctly") } } // Test RecipeProducts operations func TestRecipeProducts(t *testing.T) { products := &RecipeProducts{ ProductID: 2001, ProductQty: 1, ByproductID: 2002, ByproductQty: 2, } if products.ProductID != 2001 { t.Error("Product ID should be set correctly") } if products.ProductQty != 1 { t.Error("Product quantity should be set correctly") } if products.ByproductID != 2002 { t.Error("Byproduct ID should be set correctly") } if products.ByproductQty != 2 { t.Error("Byproduct quantity should be set correctly") } } // Test Statistics operations func TestNewStatistics(t *testing.T) { stats := NewStatistics() if stats == nil { t.Error("NewStatistics should not return nil") } if stats.RecipesByTier == nil { t.Error("RecipesByTier map should be initialized") } if stats.RecipesBySkill == nil { t.Error("RecipesBySkill map should be initialized") } } func TestStatisticsOperations(t *testing.T) { stats := NewStatistics() // Test incrementing lookups initialLookups := stats.RecipeLookups stats.IncrementRecipeLookups() if stats.RecipeLookups != initialLookups+1 { t.Error("Recipe lookups should be incremented") } // Test incrementing recipe book lookups initialBookLookups := stats.RecipeBookLookups stats.IncrementRecipeBookLookups() if stats.RecipeBookLookups != initialBookLookups+1 { t.Error("Recipe book lookups should be incremented") } // Test incrementing player recipe loads initialLoads := stats.PlayerRecipeLoads stats.IncrementPlayerRecipeLoads() if stats.PlayerRecipeLoads != initialLoads+1 { t.Error("Player recipe loads should be incremented") } // Test incrementing component queries initialQueries := stats.ComponentQueries stats.IncrementComponentQueries() if stats.ComponentQueries != initialQueries+1 { t.Error("Component queries should be incremented") } // Test getting snapshot snapshot := stats.GetSnapshot() if snapshot.RecipeLookups != stats.RecipeLookups { t.Error("Snapshot should match current stats") } } // Test error constants func TestErrorConstants(t *testing.T) { if ErrRecipeNotFound == nil { t.Error("ErrRecipeNotFound should be defined") } if ErrRecipeBookNotFound == nil { t.Error("ErrRecipeBookNotFound should be defined") } if ErrInvalidRecipeID == nil { t.Error("ErrInvalidRecipeID should be defined") } if ErrDuplicateRecipe == nil { t.Error("ErrDuplicateRecipe should be defined") } } // Test constants func TestConstants(t *testing.T) { // Test slot constants if SlotPrimary != 0 { t.Error("SlotPrimary should be 0") } if SlotBuild1 != 1 { t.Error("SlotBuild1 should be 1") } if SlotFuel != 5 { t.Error("SlotFuel should be 5") } // Test stage constants if Stage0 != 0 { t.Error("Stage0 should be 0") } if Stage4 != 4 { t.Error("Stage4 should be 4") } // Test validation constants if MinRecipeID != 1 { t.Error("MinRecipeID should be 1") } if MaxRecipeLevel != 100 { t.Error("MaxRecipeLevel should be 100") } } // Edge case tests func TestRecipeEdgeCases(t *testing.T) { recipe := NewRecipe() // Test accessing non-existent component slots components := recipe.GetComponentsBySlot(99) if len(components) != 0 { t.Error("Non-existent slot should return empty slice") } // Test accessing non-existent product stages products := recipe.GetProductsForStage(99) if products != nil { t.Error("Non-existent stage should return nil") } // Test recipe with maximum values recipe.ID = MaxRecipeID recipe.Level = MaxRecipeLevel recipe.Tier = MaxTier recipe.Name = "Max Recipe" if !recipe.IsValid() { t.Error("Recipe with maximum valid values should be valid") } } func TestMasterRecipeListEdgeCases(t *testing.T) { list := NewMasterRecipeList() // Test operations on empty list emptyTier := list.GetRecipesByTier(1) if len(emptyTier) != 0 { t.Error("Empty list should return empty slice for tier query") } emptySkill := list.GetRecipesBySkill(100) if len(emptySkill) != 0 { t.Error("Empty list should return empty slice for skill query") } // Test clearing already empty list list.ClearRecipes() if list.Size() != 0 { t.Error("Clearing empty list should keep size 0") } } // Benchmark tests func BenchmarkNewRecipe(b *testing.B) { for i := 0; i < b.N; i++ { NewRecipe() } } func BenchmarkRecipeCopy(b *testing.B) { source := NewRecipe() source.ID = 12345 source.Name = "Benchmark Recipe" source.Level = 50 source.Tier = 5 source.Components[0] = []int32{1001, 1002, 1003} b.ResetTimer() for i := 0; i < b.N; i++ { NewRecipeFromRecipe(source) } } func BenchmarkMasterRecipeListAdd(b *testing.B) { list := NewMasterRecipeList() b.ResetTimer() for i := 0; i < b.N; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Benchmark Recipe" recipe.Level = 50 recipe.Tier = 5 list.AddRecipe(recipe) } } func BenchmarkMasterRecipeListGet(b *testing.B) { list := NewMasterRecipeList() // Add test recipes for i := 1; i <= 1000; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Benchmark Recipe" recipe.Level = 50 recipe.Tier = 5 list.AddRecipe(recipe) } b.ResetTimer() for i := 0; i < b.N; i++ { list.GetRecipe(int32((i % 1000) + 1)) } } func BenchmarkPlayerRecipeListOperations(b *testing.B) { playerList := NewPlayerRecipeList() b.ResetTimer() for i := 0; i < b.N; i++ { recipe := NewRecipe() recipe.ID = int32(i) recipe.Name = "Benchmark Recipe" recipe.Level = 50 recipe.Tier = 5 playerList.AddRecipe(recipe) playerList.GetRecipe(int32(i)) } }