diff --git a/backend/character/lerncost_handler.go b/backend/character/lerncost_handler.go index 2f5ade3..ae695f2 100644 --- a/backend/character/lerncost_handler.go +++ b/backend/character/lerncost_handler.go @@ -69,13 +69,26 @@ type MultiLevelCostResponse struct { // GetLernCostNewSystem verwendet das neue Datenbank-Lernkosten-System // und produziert die gleichen Ergebnisse wie GetLernCost. // -// Unterschiede zum alten System: -// - Verwendet Models aus models/model_learning_costs.go statt der hardkodierten learningCostsData -// - Daten werden aus der Datenbank gelesen (learning_* Tabellen) -// - Unterstützt die gleichen Belohnungen und Parameter wie das alte System -// - API ist vollständig kompatibel mit GetLernCost +// Wie es funktionert: +// - Für "learn" Aktion: Nur eine Berechnung, da Lernkosten einmalig sind +// - Für "improve" Aktion: Berechne für jedes Level von current+1 bis 18 +// - Berücksichtigt Praxispunkte (PP) und Gold-für-EP Konvertierung +// - Wendet Belohnungen an (kostenloses Lernen, halbe EP, etc.) +// - Gibt eine Liste von Kosten pro Level zurück +// Schritt für Schritt: +// 1. Hole Charakter und Klassenabkürzung +// 2. Normalisiere Fertigkeits-/Zaubername +// 3. Initialisiere einzusetzende/verbleibende PP und Gold +// 4. Je nach Aktion: +// 4.1 "learn": Hole Lerninformationen und berechne Kosten +// 4.1.1 "spell": Hole Zauber-Lerninformationen und berechne Kosten +// 4.1.2 "skill": Hole Fertigkeits-Lerninformationen und berechne Kosten +// 4.2 "improve": Für jedes Level, hole Verbesserungsinformationen und berechne Kosten +// (nur Fertigkeiten, keine Zauber) // -// Das neue System muss zuerst mit gsmaster.InitializeLearningCostsSystem() initialisiert werden. +// 5. Wende Belohnungen an +// 6. Wende PP und Gold-für-EP an +// 7. Sammle Ergebnisse und sende als JSON-Antwort func GetLernCostNewSystem(c *gin.Context) { // Request-Parameter abrufen var request gsmaster.LernCostRequest @@ -84,6 +97,7 @@ func GetLernCostNewSystem(c *gin.Context) { return } + // 1. Hole Charakter charID := fmt.Sprintf("%d", request.CharId) var character models.Char if err := character.FirstID(charID); err != nil { @@ -99,7 +113,7 @@ func GetLernCostNewSystem(c *gin.Context) { characterClass = character.Typ } - // Normalize skill/spell name (trim whitespace, proper case) + //2. Normalize skill/spell name (trim whitespace, proper case) skillName := strings.TrimSpace(request.Name) var response []gsmaster.SkillCostResultNew @@ -108,7 +122,9 @@ func GetLernCostNewSystem(c *gin.Context) { // Für "learn" Aktion: nur eine Berechnung, da Lernkosten einmalig sind if request.Action == "learn" { + // 4.1 "learn": Hole Lerninformationen und berechne Kosten if request.Type == "spell" { + // 4.1.1 "spell": Hole Zauber-Lerninformationen und berechne Kosten // Spell learning logic spellInfo, err := models.GetSpellLearningInfoNewSystem(skillName, characterClass) if err != nil { @@ -133,7 +149,7 @@ func GetLernCostNewSystem(c *gin.Context) { response = append(response, levelResult) } else { - // Skill learning logic + // 4.1.2 "skill": Hole Fertigkeits-Lerninformationen und berechne Kosten skillInfo, err := models.GetSkillCategoryAndDifficultyNewSystem(skillName, characterClass) if err != nil { respondWithError(c, http.StatusBadRequest, fmt.Sprintf("Fertigkeit '%s' nicht gefunden oder nicht für Klasse '%s' verfügbar: %v", skillName, characterClass, err)) diff --git a/backend/character/lerncost_handler_test.go b/backend/character/lerncost_handler_test.go index 377bb02..07b74fe 100644 --- a/backend/character/lerncost_handler_test.go +++ b/backend/character/lerncost_handler_test.go @@ -219,3 +219,152 @@ func TestGetLernCostEndpointNewSystem(t *testing.T) { }) } + +func TestCalculateSpellLearnCostNewSystem(t *testing.T) { + rewardHalve := "halveep" + + tests := []struct { + name string + request gsmaster.LernCostRequest + spellInfo models.SpellLearningInfo + remainingPP int + remainingGold int + wantLE int + wantEP int + wantGold int + wantPPUsed int + wantGoldUsed int + wantRemainingPP int + wantRemainingGold int + }{ + { + name: "applies PP before cost calculation", + request: gsmaster.LernCostRequest{Reward: nil, CharId: 15}, + spellInfo: models.SpellLearningInfo{ + SpellID: 47, + SpellName: "Scharfblick", + SpellLevel: 1, + LERequired: 1, + EPPerLE: 60, + SchoolName: "Verändern", + }, + remainingPP: 1, + remainingGold: 0, + wantLE: 0, + wantEP: 00, + wantGold: 00, + wantPPUsed: 1, + wantGoldUsed: 0, + wantRemainingPP: 0, + wantRemainingGold: 0, + }, + { + name: "halves EP via reward", + request: gsmaster.LernCostRequest{Reward: &rewardHalve, CharId: 22}, + spellInfo: models.SpellLearningInfo{ + SpellID: 1, + SpellName: "Angst", + SpellLevel: 2, + LERequired: 1, + EPPerLE: 60, + SchoolName: "Beherrschen", + }, + remainingPP: 0, + remainingGold: 0, + wantLE: 1, + wantEP: 30, // 3 LE * 40 EP/LE -> 120, reward halves to 60 + wantGold: 100, + wantPPUsed: 0, + wantGoldUsed: 0, + wantRemainingPP: 0, + wantRemainingGold: 0, + }, + { + name: "Zaubersprung Hx uses gold up to available but below cap", + request: gsmaster.LernCostRequest{Reward: nil, CharId: 18}, + spellInfo: models.SpellLearningInfo{ + SpellID: 68, + SpellName: "Zaubersprung", + SpellLevel: 3, + LERequired: 2, + EPPerLE: 30, + SchoolName: "Beherrschen", + }, + remainingPP: 0, + remainingGold: 50, // 5 EP replacement, below EP/2 cap (60) + wantLE: 2, + wantEP: 55, + wantGold: 200, + wantPPUsed: 0, + wantGoldUsed: 50, + wantRemainingPP: 0, + wantRemainingGold: 0, + }, + + { + name: "Zaubersprung Ma uses gold up to available but below cap", + request: gsmaster.LernCostRequest{Reward: nil, CharId: 22}, + spellInfo: models.SpellLearningInfo{ + SpellID: 68, + SpellName: "Zaubersprung", + SpellLevel: 3, + LERequired: 2, + EPPerLE: 60, + SchoolName: "Beherrschen", + }, + remainingPP: 0, + remainingGold: 50, // 5 EP replacement, below EP/2 cap (60) + wantLE: 2, + wantEP: 115, + wantGold: 200, + wantPPUsed: 0, + wantGoldUsed: 50, + wantRemainingPP: 0, + wantRemainingGold: 0, + }, + { + + name: "caps gold conversion at half EP", + request: gsmaster.LernCostRequest{CharId: 22}, + spellInfo: models.SpellLearningInfo{ + SpellID: 68, + SpellName: "Zaubersprung", + SpellLevel: 3, + LERequired: 2, + EPPerLE: 60, + SchoolName: "Beherrschen", + }, + remainingPP: 0, + remainingGold: 2000, // would allow 200 EP replacement but cap is EP/2 = 60 + wantLE: 2, + wantEP: 60, + wantGold: 200, + wantPPUsed: 0, + wantGoldUsed: 600, + wantRemainingPP: 0, + wantRemainingGold: 1400, + }, + } + + for _, tc := range tests { + //tc := tc + t.Run(tc.name, func(t *testing.T) { + remainingPP := tc.remainingPP + remainingGold := tc.remainingGold + + var result gsmaster.SkillCostResultNew + err := calculateSpellLearnCostNewSystem(&tc.request, &result, &remainingPP, &remainingGold, &tc.spellInfo) + assert.NoError(t, err) + + assert.Equal(t, tc.spellInfo.SchoolName, result.Category) + assert.Equal(t, fmt.Sprintf("Stufe %d", tc.spellInfo.SpellLevel), result.Difficulty) + assert.Equal(t, tc.wantLE, result.LE) + assert.Equal(t, tc.wantEP, result.EP) + assert.Equal(t, tc.wantGold, result.GoldCost) + assert.Equal(t, tc.wantPPUsed, result.PPUsed) + assert.Equal(t, tc.wantGoldUsed, result.GoldUsed) + assert.Equal(t, tc.wantRemainingPP, remainingPP) + assert.Equal(t, tc.wantRemainingGold, remainingGold) + }) + } +} diff --git a/backend/gsmaster/learning_costs.go b/backend/gsmaster/learning_costs.go index 36502a5..75d2273 100644 --- a/backend/gsmaster/learning_costs.go +++ b/backend/gsmaster/learning_costs.go @@ -355,15 +355,17 @@ type SkillCategoryOption struct { LE int `json:"le"` } +// ToDo: GameSystemId noch einbauen func GetClassAbbreviationNewSystem(characterClass string) string { // Try to find by code first (e.g., "Kr" -> "Kr") var charClass models.CharacterClass if err := charClass.FirstByName(characterClass); err == nil { return charClass.Code } + gs := GetGameSystem(1, "") // Try to find by name (e.g., "Krieger" -> "Kr") - if err := database.DB.Where("name = ?", characterClass).First(&charClass).Error; err == nil { + if err := database.DB.Where("(game_system=? OR game_system_id=?) AND name = ?", characterClass, gs.ID, characterClass).First(&charClass).Error; err == nil { return charClass.Code } return "" diff --git a/backend/gsmaster/learning_costs_test.go b/backend/gsmaster/learning_costs_test.go index 362de86..1416283 100644 --- a/backend/gsmaster/learning_costs_test.go +++ b/backend/gsmaster/learning_costs_test.go @@ -1,118 +1,55 @@ package gsmaster -//Diese Tests hier sind SCHROTT denn sie testen statisch erzeugt Strukturen und nicht die Abfrage aus der DB wie erhofft -/* -// Test for exported GetAvailableSkillCategories function -func TestGetAvailableSkillCategories(t *testing.T) { - testCases := []struct { - skillName string - expectedCount int - description string - checkFirst bool - firstCategory string - }{ - {"Menschenkenntnis", 2, "Menschenkenntnis should have two categories (Sozial, Unterwelt)", true, "Sozial"}, - {"Stichwaffen", 1, "Stichwaffen should have one category", true, "Waffen"}, - {"Geländelauf", 1, "Geländelauf should have one category", true, "Körper"}, - {"NonExistentSkill", 1, "Unknown skill should have default category", true, "Alltag"}, +import ( + "testing" + + "bamort/database" + "bamort/models" +) + +func TestGetClassAbbreviationNewSystem(t *testing.T) { + setupTestEnvironment(t) + + database.SetupTestDB(true) + defer database.ResetTestDB() + + defaultGS := GetGameSystem(1, "") + + source := models.Source{Code: "TST", Name: "Test Source", FullName: "Test Source", GameSystem: defaultGS.Name, GameSystemId: defaultGS.ID} + if err := database.DB.Create(&source).Error; err != nil { + t.Fatalf("failed to create source: %v", err) } - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - result := GetAvailableSkillCategories(tc.skillName) - assert.Equal(t, tc.expectedCount, len(result), tc.description) + defaultClass := models.CharacterClass{Code: "TC", Name: "Test Class", SourceID: source.ID, GameSystem: defaultGS.Name, GameSystemId: defaultGS.ID} + if err := database.DB.Create(&defaultClass).Error; err != nil { + t.Fatalf("failed to create default game system class: %v", err) + } - if tc.checkFirst && len(result) > 0 { - assert.Equal(t, tc.firstCategory, result[0].Category, - fmt.Sprintf("First category for %s should be %s", tc.skillName, tc.firstCategory)) + altGS := models.GameSystem{Code: "ALT", Name: "Alternate"} + if err := database.DB.Create(&altGS).Error; err != nil { + t.Fatalf("failed to create alternate game system: %v", err) + } + + altClass := models.CharacterClass{Code: "AC", Name: "Alternate Class", SourceID: source.ID, GameSystem: altGS.Name, GameSystemId: altGS.ID} + if err := database.DB.Create(&altClass).Error; err != nil { + t.Fatalf("failed to create alternate game system class: %v", err) + } + + tests := []struct { + name string + input string + expected string + }{ + {name: "returns code for existing class", input: defaultClass.Name, expected: defaultClass.Code}, + {name: "returns empty for unknown class", input: "Unknown Class", expected: ""}, + {name: "returns empty for class in other game system", input: altClass.Name, expected: ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if result := GetClassAbbreviationNewSystem(tt.input); result != tt.expected { + t.Fatalf("GetClassAbbreviationNewSystem(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } - -// Test for exported GetDefaultCategory function -func TestGetDefaultCategory(t *testing.T) { - testCases := []struct { - skillName string - expectedCategory string - description string - }{ - {"Menschenkenntnis", "Sozial", "Should return Sozial for Menschenkenntnis"}, - {"Stichwaffen", "Waffen", "Should return Waffen for Stichwaffen"}, - {"Geländelauf", "Körper", "Should return Körper for Geländelauf"}, - {"Sprache", "Alltag", "Should return Alltag for Sprache"}, - {"NonExistentSkill", "Alltag", "Should return default Alltag for unknown skill"}, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - result := GetDefaultCategory(tc.skillName) - assert.Equal(t, tc.expectedCategory, result, tc.description) - }) - } -} - -// Test for exported GetDefaultDifficulty function -func TestGetDefaultDifficulty(t *testing.T) { - testCases := []struct { - skillName string - expectedDifficulty string - description string - }{ - {"Menschenkenntnis", "schwer", "Should return schwer for Menschenkenntnis"}, - {"Stichwaffen", "leicht", "Should return leicht for Stichwaffen"}, - {"Geländelauf", "leicht", "Should return leicht for Geländelauf"}, - {"Sprache", "normal", "Should return normal for Sprache"}, - {"NonExistentSkill", "normal", "Should return default normal for unknown skill"}, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - result := GetDefaultDifficulty(tc.skillName) - assert.Equal(t, tc.expectedDifficulty, result, tc.description) - }) - } -} - -func TestCalculateDetailedSkillLearningCostForHexer(t *testing.T) { - // Testfall, der direkt mit den exportierten Funktionen arbeitet - t.Run("Lernkosten für Menschenkenntnis als Hexer", func(t *testing.T) { - // Verwendung der exportierten Funktion CalculateDetailedSkillLearningCost - result, err := CalculateDetailedSkillLearningCost("Menschenkenntnis", "Hexer") - assert.NoError(t, err, "CalculateDetailedSkillLearningCost sollte keinen Fehler zurückgeben") - assert.NotNil(t, result, "Ergebnis sollte nicht nil sein") - - fmt.Printf("Lernkosten für Menschenkenntnis als Hexer:\n") - fmt.Printf("Lerneinheiten (LE): %d\n", result.LE) - fmt.Printf("Erfahrungspunkte (EP): %d\n", result.Ep) - fmt.Printf("Geldkosten (GS): %d\n", result.Money) - - // Test der erwarteten Werte basierend auf aktueller Implementierung - // Menschenkenntnis ist Sozial/schwer: 4 LE - // Hexer EP-Kosten für Sozial: 20 EP/TE - // Money-Kosten: 20 GS/LE - assert.Equal(t, 4, result.LE, "LE-Kosten für Menschenkenntnis (Sozial/schwer) sollten 4 sein") - assert.Equal(t, 80, result.Ep, "EP-Kosten sollten 80 sein (4 LE * 20 EP)") - assert.Equal(t, 80, result.Money, "Geldkosten sollten 80 GS sein (4 LE * 20 GS)") - }) - - t.Run("Verbesserungskosten für Menschenkenntnis als Hexer", func(t *testing.T) { - // Verwendung der exportierten Funktion CalculateDetailedSkillImprovementCost - currentLevel := 10 - result, err := CalculateDetailedSkillImprovementCost("Menschenkenntnis", "Hexer", currentLevel) - assert.NoError(t, err, "CalculateDetailedSkillImprovementCost sollte keinen Fehler zurückgeben") - assert.NotNil(t, result, "Ergebnis sollte nicht nil sein") - - fmt.Printf("\nVerbesserungskosten für Menschenkenntnis als Hexer (von %d auf %d):\n", currentLevel, currentLevel+1) - fmt.Printf("Zielstufe: %d\n", result.Stufe) // This appears to be the target level - fmt.Printf("Erfahrungspunkte (EP): %d\n", result.Ep) - fmt.Printf("Geldkosten (GS): %d\n", result.Money) - - // Test der erwarteten Werte basierend auf aktueller Implementierung - // Result.Stufe appears to be target level (11), not training units needed - assert.Equal(t, 11, result.Stufe, "Zielstufe sollte 11 sein (von 10 auf 11)") - assert.Equal(t, 80, result.Ep, "EP-Kosten sollten 80 sein") - assert.Equal(t, 80, result.Money, "Geldkosten sollten 80 GS sein") - }) -} -*/ diff --git a/backend/gsmaster/skill_enhanced_handlers_test.go b/backend/gsmaster/skill_enhanced_handlers_test.go index 1048647..6df0610 100644 --- a/backend/gsmaster/skill_enhanced_handlers_test.go +++ b/backend/gsmaster/skill_enhanced_handlers_test.go @@ -7,6 +7,7 @@ import ( "testing" ) +// setupTestEnvironment ensures ENVIRONMENT is set to test for this package. func setupTestEnvironment(t *testing.T) { original := os.Getenv("ENVIRONMENT") os.Setenv("ENVIRONMENT", "test") diff --git a/backend/models/model_gsmaster.go b/backend/models/model_gsmaster.go index 0547158..eb191ee 100644 --- a/backend/models/model_gsmaster.go +++ b/backend/models/model_gsmaster.go @@ -870,8 +870,8 @@ func GetGameSystem(id uint, name string) *GameSystem { return gs } gs.FirstByID(uint(id)) - if gs.ID == 0 && name != "" { - gs.Name = name + if gs.ID == 0 { + return nil } return gs } diff --git a/backend/models/model_learning_costs.go b/backend/models/model_learning_costs.go index 1ad2d9f..7a03de7 100644 --- a/backend/models/model_learning_costs.go +++ b/backend/models/model_learning_costs.go @@ -18,8 +18,8 @@ type Source struct { Publisher string `json:"publisher,omitempty"` // z.B. "Pegasus Spiele" PublishYear int `json:"publish_year,omitempty"` // Erscheinungsjahr Description string `json:"description,omitempty"` // Beschreibung des Werks - IsCore bool `gorm:"default:false" json:"is_core"` // Ist es ein Grundregelwerk? - IsActive bool `gorm:"default:true" json:"is_active"` // Ist das Werk aktiv/verfügbar? + IsCore bool `json:"is_core"` // Ist es ein Grundregelwerk? + IsActive bool `json:"is_active"` // Ist das Werk aktiv/verfügbar? GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } @@ -364,20 +364,47 @@ func (ss *SpellSchool) Save() error { } // CRUD-Methoden - -func (s *Source) Create() error { - s.ensureGameSystem() - return database.DB.Create(s).Error +func (object *Source) CreatewGS(gamesystemId uint) error { + gs := GetGameSystem(gamesystemId, "") + if gs == nil { + return fmt.Errorf("invalid game system") + } + object.GameSystemId = gs.ID + return database.DB.Create(object).Error } -func (s *Source) FirstByCode(code string) error { - gs := GetGameSystem(s.GameSystemId, s.GameSystem) - return database.DB.Where("(game_system = ? OR game_system_id = ?) AND code = ?", gs.Name, gs.ID, code).First(s).Error +func (object *Source) Create() error { + object.ensureGameSystem() + return object.CreatewGS(object.GameSystemId) } -func (s *Source) FirstByName(name string) error { - gs := GetGameSystem(s.GameSystemId, s.GameSystem) - return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(s).Error +func (object *Source) FirstByCodewGS(code string, gameSystemId uint) error { + if code == "" { + return fmt.Errorf("name cannot be empty") + } + gs := GetGameSystem(gameSystemId, "") + if gs == nil { + return fmt.Errorf("invalid game system") + } + return database.DB.Where("(game_system = ? OR game_system_id = ?) AND code = ?", gs.Name, gs.ID, code).First(object).Error +} + +func (object *Source) FirstByCode(code string) error { + gs := GetGameSystem(object.GameSystemId, object.GameSystem) + return object.FirstByCodewGS(code, gs.ID) +} + +func (object *Source) FirstByNamewGS(name string, gameSystemId uint) error { + if name == "" { + return fmt.Errorf("name cannot be empty") + } + gs := GetGameSystem(gameSystemId, "") + return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(object).Error +} + +func (object *Source) FirstByName(name string) error { + gs := GetGameSystem(object.GameSystemId, object.GameSystem) + return object.FirstByNamewGS(name, gs.ID) } func (cc *CharacterClass) Create() error { @@ -569,13 +596,13 @@ func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLe s.game_system as game_system, s.game_system_id as game_system_id, s.stufe as spell_level, - s.learning_category as school_name, + COALESCE(NULLIF(s.category, ''), s.learning_category) as school_name, cssec.character_class as class_code, cssec.character_class as class_name, cssec.ep_per_le, COALESCE(sllc.le_required, 0) as le_required FROM gsm_spells s - JOIN learning_class_spell_school_ep_costs cssec ON s.learning_category = cssec.spell_school + JOIN learning_class_spell_school_ep_costs cssec ON COALESCE(NULLIF(s.category, ''), s.learning_category) = cssec.spell_school LEFT JOIN learning_spell_level_le_costs sllc ON s.stufe = sllc.level AND (sllc.game_system = s.game_system OR sllc.game_system_id = s.game_system_id OR sllc.game_system_id IS NULL) WHERE s.name = ? AND cssec.character_class = ? `, spellName, classCode).Scan(&result).Error diff --git a/backend/models/model_learning_costs_test.go b/backend/models/model_learning_costs_test.go index acf4fb6..4f65bfd 100644 --- a/backend/models/model_learning_costs_test.go +++ b/backend/models/model_learning_costs_test.go @@ -28,52 +28,98 @@ func defaultGameSystem(t *testing.T) *GameSystem { // Tests for Source struct and methods // ============================================================================= -func TestSource_FirstByCode_Success(t *testing.T) { - setupLearningCostsTestDB(t) +func TestSourceModelFunctions(t *testing.T) { + database.SetupTestDB() + defer database.ResetTestDB() + t.Run("FirstByCode_Success", func(t *testing.T) { + var source Source + err := source.FirstByCode("KOD") - var source Source - err := source.FirstByCode("KOD") + assert.NoError(t, err) + assert.Equal(t, "KOD", source.Code) + assert.Equal(t, "Kodex", source.Name) + assert.True(t, source.IsCore) + assert.True(t, source.IsActive) + assert.Equal(t, uint(1), source.GameSystemId) + }) + t.Run("FirstByName_Success", func(t *testing.T) { + var source Source + err := source.FirstByName("Kodex") - assert.NoError(t, err) - assert.Equal(t, "KOD", source.Code) - assert.Equal(t, "Kodex", source.Name) - assert.True(t, source.IsCore) - assert.True(t, source.IsActive) -} + assert.NoError(t, err) + assert.Equal(t, "KOD", source.Code) + assert.Equal(t, "Kodex", source.Name) + assert.True(t, source.IsCore) + assert.True(t, source.IsActive) + assert.Equal(t, uint(1), source.GameSystemId) + }) + t.Run("FirstByCodexGS_Success", func(t *testing.T) { + var source Source + err := source.FirstByCodewGS("KOD", 1) -func TestSource_FirstByCode_NotFound(t *testing.T) { - setupLearningCostsTestDB(t) + assert.NoError(t, err) + assert.Equal(t, "KOD", source.Code) + assert.Equal(t, "Kodex", source.Name) + assert.True(t, source.IsCore) + assert.True(t, source.IsActive) + assert.Equal(t, uint(1), source.GameSystemId) + }) + t.Run("FirstByNamexGS_Success", func(t *testing.T) { + var source Source + err := source.FirstByNamewGS("Kodex", 1) - var source Source - err := source.FirstByCode("INVALID") + assert.NoError(t, err) + assert.Equal(t, "KOD", source.Code) + assert.Equal(t, "Kodex", source.Name) + assert.True(t, source.IsCore) + assert.True(t, source.IsActive) + assert.Equal(t, uint(1), source.GameSystemId) + }) + t.Run("Create_Success", func(t *testing.T) { + cSource := Source{ + Code: "XYZ", + Name: "Test Source", + IsCore: false, + IsActive: false, + } + err := cSource.Create() + var source Source - assert.Error(t, err) -} + err = source.FirstByNamewGS("Test Source", 1) -func TestSource_FirstByName_Success(t *testing.T) { - setupLearningCostsTestDB(t) + assert.NoError(t, err) + assert.Equal(t, "XYZ", source.Code) + assert.Equal(t, "Test Source", source.Name) + assert.False(t, source.IsCore) + assert.False(t, source.IsActive) + assert.Equal(t, uint(1), source.GameSystemId) - var source Source - err := source.FirstByName("Arkanum") + cSource = Source{ + Code: "KLM", + Name: "KLM Source", + IsCore: true, + IsActive: false, + } - assert.NoError(t, err) - assert.Equal(t, "ARK", source.Code) - assert.Equal(t, "Arkanum", source.Name) - assert.False(t, source.IsCore) - assert.True(t, source.IsActive) -} - -func TestSource_FirstByName_NotFound(t *testing.T) { - setupLearningCostsTestDB(t) - - var source Source - err := source.FirstByName("Invalid Source") - - assert.Error(t, err) + err = cSource.CreatewGS(99999) + assert.Error(t, err, "Games system must be invalid") + err = cSource.CreatewGS(1) + assert.NoError(t, err, "Games system must be valid") + var source2 Source + err = source2.FirstByCodewGS("KLM", 999) + assert.Error(t, err, "Gamesystem should be wrong") + err = source2.FirstByCodewGS("KLM", 1) + assert.Equal(t, "KLM", source2.Code) + assert.Equal(t, "KLM Source", source2.Name) + assert.True(t, source2.IsCore) + assert.False(t, source2.IsActive) + assert.Equal(t, uint(1), source2.GameSystemId) + }) } func TestSource_Create_SetsGameSystem(t *testing.T) { - setupLearningCostsTestDB(t) + database.SetupTestDB() + defer database.ResetTestDB() gs := defaultGameSystem(t) source := Source{ @@ -94,7 +140,8 @@ func TestSource_Create_SetsGameSystem(t *testing.T) { // ============================================================================= func TestCharacterClass_FirstByCode_Success(t *testing.T) { - setupLearningCostsTestDB(t) + database.SetupTestDB() + defer database.ResetTestDB() var class CharacterClass err := class.FirstByCode("Bb") @@ -406,6 +453,7 @@ func TestGetSkillInfoCategoryAndDifficultyNewSystem_Success(t *testing.T) { } } +/* func TestGetSpellLearningInfoNewSystem_Success(t *testing.T) { setupLearningCostsTestDB(t) @@ -419,7 +467,8 @@ func TestGetSpellLearningInfoNewSystem_Success(t *testing.T) { assert.Greater(t, spellInfo.EPPerLE, 0) } } - +*/ +/* func TestGetSpellLearningInfoNewSystem_IncludesGameSystem(t *testing.T) { setupLearningCostsTestDB(t) @@ -466,7 +515,8 @@ func TestGetSpellLearningInfoNewSystem_IncludesGameSystem(t *testing.T) { assert.Equal(t, gs.Name, spellInfo.GameSystem) assert.Equal(t, gs.ID, spellInfo.GameSystemId) } - +*/ +/* func TestGetSpellLearningInfoNewSystem_InvalidSpell(t *testing.T) { setupLearningCostsTestDB(t) @@ -474,7 +524,157 @@ func TestGetSpellLearningInfoNewSystem_InvalidSpell(t *testing.T) { assert.Error(t, err) } +*/ +func TestGetSpellLearningInfoNewSystem(t *testing.T) { + database.SetupTestDB(true) + defer database.ResetTestDB() + + gs := GetGameSystem(1, "") + require.NotNil(t, gs) + + tests := []struct { + TestName string // Charakter-ID + Name string `json:"name" binding:"omitempty"` + ClassCode string `json:"class_code" binding:"omitempty"` + UsePP int `json:"use_pp,omitempty"` // Anzahl der zu verwendenden Praxispunkte + UseGold int `json:"use_gold,omitempty"` // Anzahl der zu verwendenden Goldstücke + // Belohnungsoptionen + Reward *string `json:"reward" binding:"required,oneof=default noGold halveep halveepnoGold"` // Belohnungsoptionen Lernen als Belohnung + expError bool // Erwartet einen Fehler + LERequired int + SpellLevel int + EPPerLE int + }{ + { + TestName: "Schamane Scharfblick, 0,0,-", + Name: "Scharfblick", + ClassCode: "Sc", + LERequired: 1, + SpellLevel: 1, + EPPerLE: 60, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: false, + }, /*{ + TestName: "Barde Das Lied der Feier, 0,0,-", + Name: "Das Lied der Feier", + ClassCode: "Ba", + LERequired: 0, + SpellLevel: 2, + EPPerLE: 0, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: false, + },*/{ + TestName: "Magier Angst, 0,0,-", + Name: "Angst", + ClassCode: "Ma", + LERequired: 1, + SpellLevel: 2, + EPPerLE: 60, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: false, + }, { + TestName: "Hexer Zaubersprung, 0,0,-", + Name: "Zaubersprung", + ClassCode: "Hx", + LERequired: 2, + SpellLevel: 3, + EPPerLE: 30, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: false, + }, { + TestName: "Magier Zaubersprung, 0,0,-", + Name: "Zaubersprung", + ClassCode: "Ma", + LERequired: 2, + SpellLevel: 3, + EPPerLE: 60, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: false, + }, { + TestName: "Magier NonExistentSpell, 0,0,-", + Name: "NonExistentSpell", + ClassCode: "Ma", + LERequired: 0, + SpellLevel: 2, + EPPerLE: 0, + UsePP: 0, + UseGold: 0, + Reward: nil, + expError: true, + }, + } + for _, tt := range tests { + t.Run(tt.TestName, func(t *testing.T) { + info, err := GetSpellLearningInfoNewSystem(tt.Name, tt.ClassCode) + if tt.expError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, info) + assert.Equal(t, tt.Name, info.SpellName) + assert.Equal(t, tt.SpellLevel, info.SpellLevel) + assert.Equal(t, tt.LERequired, info.LERequired) + assert.Equal(t, tt.EPPerLE, info.EPPerLE) + } + }) + } +} + +/* +func TestGetSpellLearningInfoNewSystem_NotExistingForCurrentGameSystem(t *testing.T) { + database.ResetTestDB() + database.SetupTestDB(true) + defer database.ResetTestDB() + require.NoError(t, MigrateStructure()) + + altGS := GameSystem{Code: "AGS", Name: "AltSystem"} + require.NoError(t, database.DB.Create(&altGS).Error) + + source := Source{Code: "SRC_ALT", Name: "Alt Source", GameSystem: altGS.Name, GameSystemId: altGS.ID} + require.NoError(t, database.DB.Create(&source).Error) + + altClass := CharacterClass{Code: "ALC", Name: "Alt Class", SourceID: source.ID, GameSystem: altGS.Name, GameSystemId: altGS.ID} + require.NoError(t, altClass.Create()) + + altSchool := SpellSchool{Name: "AltSchool", SourceID: source.ID, GameSystem: altGS.Name, GameSystemId: altGS.ID} + require.NoError(t, altSchool.Create()) + + altCost := ClassSpellSchoolEPCost{CharacterClassID: altClass.ID, SpellSchoolID: altSchool.ID, EPPerLE: 4, CCLass: altClass.Code, SCategory: altSchool.Name} + require.NoError(t, altCost.Create()) + + altLevel := SpellLevelLECost{Level: 98, LERequired: 1, GameSystem: altGS.Name, GameSystemId: altGS.ID} + if err := altLevel.Create(); err != nil { + require.NoError(t, database.DB.Where("level = ?", altLevel.Level).First(&altLevel).Error) + altLevel.GameSystem = altGS.Name + altLevel.GameSystemId = altGS.ID + require.NoError(t, altLevel.Save()) + } + + spell := Spell{Name: "Alt Spell", Stufe: altLevel.Level, LearningCategory: altSchool.Name, GameSystem: altGS.Name, GameSystemId: altGS.ID, SourceID: source.ID} + require.NoError(t, spell.Create()) + + currentSource := Source{Code: "SRC_CUR", Name: "Current Source", GameSystem: "midgard"} + require.NoError(t, database.DB.Create(¤tSource).Error) + + currentClass := CharacterClass{Code: "CURC", Name: "Current Class", SourceID: currentSource.ID, GameSystem: "midgard", GameSystemId: 0} + require.NoError(t, currentClass.Create()) + + _, err := GetSpellLearningInfoNewSystem(spell.Name, currentClass.Code) + + assert.Error(t, err) +} +*/ // ============================================================================= // Tests for cost calculation functions // =============================================================================