Files
bamort/backend/maintenance/handlers_test.go
T

362 lines
11 KiB
Go

package maintenance
import (
"bamort/database"
"bamort/models"
"bamort/user"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// TestTableListCompleteness verifies that all database models are included in the table lists
func TestTableListCompleteness(t *testing.T) {
// Define all known database models that should be copied
expectedModels := map[string]bool{
// User models
"*user.User": true,
// Learning Costs System - Basis
"*models.Source": true,
"*models.CharacterClass": true,
"*models.SkillCategory": true,
"*models.SkillDifficulty": true,
"*models.SpellSchool": true,
// Learning Costs System - Dependent
"*models.ClassCategoryEPCost": true,
"*models.ClassSpellSchoolEPCost": true,
"*models.SpellLevelLECost": true,
"*models.SkillCategoryDifficulty": true,
"*models.WeaponSkillCategoryDifficulty": true,
"*models.SkillImprovementCost": true,
// GSMaster Base Data
"*models.Skill": true,
"*models.WeaponSkill": true,
"*models.Spell": true,
"*models.Equipment": true,
"*models.Weapon": true,
"*models.Container": true,
"*models.Transportation": true,
"*models.Believe": true,
// Characters (Base)
"*models.Char": true,
// Character Properties
"*models.Eigenschaft": true,
"*models.Lp": true,
"*models.Ap": true,
"*models.B": true,
"*models.Merkmale": true,
"*models.Erfahrungsschatz": true,
"*models.Bennies": true,
"*models.Vermoegen": true,
// Character Skills
"*models.SkFertigkeit": true,
"*models.SkWaffenfertigkeit": true,
"*models.SkAngeboreneFertigkeit": true,
"*models.SkZauber": true,
// Character Equipment
"*models.EqAusruestung": true,
"*models.EqWaffe": true,
"*models.EqContainer": true,
// Character Creation Sessions
"*models.CharacterCreationSession": true,
// Audit Logging
"*models.AuditLogEntry": true,
}
// Get the table list from copyMariaDBToSQLite (simulated)
tables := []interface{}{
&user.User{},
&models.Source{},
&models.CharacterClass{},
&models.SkillCategory{},
&models.SkillDifficulty{},
&models.SpellSchool{},
&models.ClassCategoryEPCost{},
&models.ClassSpellSchoolEPCost{},
&models.SpellLevelLECost{},
&models.SkillCategoryDifficulty{},
&models.WeaponSkillCategoryDifficulty{},
&models.SkillImprovementCost{},
&models.Skill{},
&models.WeaponSkill{},
&models.Spell{},
&models.Equipment{},
&models.Weapon{},
&models.Container{},
&models.Transportation{},
&models.Believe{},
&models.Char{},
&models.Eigenschaft{},
&models.Lp{},
&models.Ap{},
&models.B{},
&models.Merkmale{},
&models.Erfahrungsschatz{},
&models.Bennies{},
&models.Vermoegen{},
&models.SkFertigkeit{},
&models.SkWaffenfertigkeit{},
&models.SkAngeboreneFertigkeit{},
&models.SkZauber{},
&models.EqAusruestung{},
&models.EqWaffe{},
&models.EqContainer{},
&models.CharacterCreationSession{},
&models.AuditLogEntry{},
}
// Verify all expected models are in the table list
foundModels := make(map[string]bool)
for _, model := range tables {
modelType := getModelTypeName(model)
foundModels[modelType] = true
}
// Check for missing models
for expectedModel := range expectedModels {
if !foundModels[expectedModel] {
t.Errorf("Missing model in table list: %s", expectedModel)
}
}
// Check for unexpected models (not in expected list)
for foundModel := range foundModels {
if !expectedModels[foundModel] {
t.Logf("Warning: Unexpected model in table list (may be intentional): %s", foundModel)
}
}
t.Logf("Total models in table list: %d", len(tables))
t.Logf("Total expected models: %d", len(expectedModels))
}
// TestImprovableFieldTransfer tests that the Improvable field is correctly transferred from MariaDB to SQLite
func TestImprovableFieldTransfer(t *testing.T) {
setupTestEnvironment(t)
// Create source database (simulating MariaDB)
sourceDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
// Create target database (simulating SQLite)
targetDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
// Migrate structures
err = sourceDB.AutoMigrate(&models.Skill{})
require.NoError(t, err)
err = targetDB.AutoMigrate(&models.Skill{})
require.NoError(t, err)
// Insert test data with different Improvable values
// Use raw SQL to avoid GORM applying default values
testSkills := []struct {
ID uint
Name string
GameSystem string
GameSystemID uint
Improvable bool
InnateSkill bool
}{
{1, "Hören", "midgard", 1, false, true},
{2, "Alchimie", "midgard", 1, true, false},
{3, "Nachtsicht", "midgard", 1, false, true},
}
for _, skill := range testSkills {
err = sourceDB.Exec(`INSERT INTO gsm_skills (id, name, game_system, game_system_id, improvable, innate_skill, initialwert, basis_wert) VALUES (?, ?, ?, ?, ?, ?, 5, 0)`,
skill.ID, skill.Name, skill.GameSystem, skill.GameSystemID, skill.Improvable, skill.InnateSkill).Error
require.NoError(t, err)
}
// Verify source data
var sourceSkill models.Skill
err = sourceDB.First(&sourceSkill, 1).Error
require.NoError(t, err)
assert.Equal(t, "Hören", sourceSkill.Name)
t.Logf("DEBUG: Source skill Hören - Improvable: %v, InnateSkill: %v", sourceSkill.Improvable, sourceSkill.InnateSkill)
// Also check raw data from database
var rawImprovable int
err = sourceDB.Raw("SELECT improvable FROM gsm_skills WHERE id = 1").Scan(&rawImprovable).Error
require.NoError(t, err)
t.Logf("DEBUG: Raw SQL value for improvable: %d", rawImprovable)
assert.False(t, sourceSkill.Improvable, "Source skill should have Improvable=false")
// Copy data using copyTableData
err = copyTableData(sourceDB, targetDB, &models.Skill{})
require.NoError(t, err)
// Verify all skills in target database
var targetSkills []models.Skill
err = targetDB.Find(&targetSkills).Error
require.NoError(t, err)
require.Len(t, targetSkills, 3, "Should have 3 skills in target")
// Check each skill's Improvable field
expectedValues := map[uint]bool{
1: false, // Hören
2: true, // Alchimie
3: false, // Nachtsicht
}
for _, targetSkill := range targetSkills {
expectedImprovable := expectedValues[targetSkill.ID]
assert.Equal(t, expectedImprovable, targetSkill.Improvable,
"Skill %s (ID: %d) should have Improvable=%v, got %v",
targetSkill.Name, targetSkill.ID, expectedImprovable, targetSkill.Improvable)
}
// Specific checks
var hoeren models.Skill
err = targetDB.Where("name = ?", "Hören").First(&hoeren).Error
require.NoError(t, err)
assert.False(t, hoeren.Improvable, "Hören should have Improvable=false after transfer")
var alchimie models.Skill
err = targetDB.Where("name = ?", "Alchimie").First(&alchimie).Error
require.NoError(t, err)
assert.True(t, alchimie.Improvable, "Alchimie should have Improvable=true after transfer")
}
// TestImprovableFieldInPreparedTestDB verifies the prepared test database has correct Improvable values
func TestImprovableFieldInPreparedTestDB(t *testing.T) {
setupTestEnvironment(t)
// Ensure clean database state before setup
database.ResetTestDB()
// Use the prepared test database
database.SetupTestDB(true)
require.NotNil(t, database.DB)
// Ensure database cleanup after test
t.Cleanup(func() {
database.ResetTestDB()
})
// Check specific skills that should have Improvable=false (innate skills)
innateSkills := []string{"Hören", "Nachtsicht", "Riechen", "Sechster Sinn", "Sehen"}
for _, skillName := range innateSkills {
var skill models.Skill
err := database.DB.Where("name = ?", skillName).First(&skill).Error
if err == gorm.ErrRecordNotFound {
t.Logf("Skill %s not found in prepared test DB - skipping", skillName)
continue
}
require.NoError(t, err)
// These are innate skills and should not be improvable
assert.True(t, skill.InnateSkill, "Skill %s should be marked as InnateSkill", skillName)
// Note: Based on game rules, innate skills are typically not improvable
t.Logf("Skill: %s, Improvable: %v, InnateSkill: %v", skillName, skill.Improvable, skill.InnateSkill)
}
// Check a regular skill that should be improvable
var alchimie models.Skill
err := database.DB.Where("name = ?", "Alchimie").First(&alchimie).Error
if err != gorm.ErrRecordNotFound {
require.NoError(t, err)
assert.True(t, alchimie.Improvable, "Alchimie should be improvable")
assert.False(t, alchimie.InnateSkill, "Alchimie should not be an innate skill")
}
}
// getModelTypeName returns the type name of a model
func getModelTypeName(model interface{}) string {
switch model.(type) {
case *user.User:
return "*user.User"
case *models.Source:
return "*models.Source"
case *models.CharacterClass:
return "*models.CharacterClass"
case *models.SkillCategory:
return "*models.SkillCategory"
case *models.SkillDifficulty:
return "*models.SkillDifficulty"
case *models.SpellSchool:
return "*models.SpellSchool"
case *models.ClassCategoryEPCost:
return "*models.ClassCategoryEPCost"
case *models.ClassSpellSchoolEPCost:
return "*models.ClassSpellSchoolEPCost"
case *models.SpellLevelLECost:
return "*models.SpellLevelLECost"
case *models.SkillCategoryDifficulty:
return "*models.SkillCategoryDifficulty"
case *models.WeaponSkillCategoryDifficulty:
return "*models.WeaponSkillCategoryDifficulty"
case *models.SkillImprovementCost:
return "*models.SkillImprovementCost"
case *models.Skill:
return "*models.Skill"
case *models.WeaponSkill:
return "*models.WeaponSkill"
case *models.Spell:
return "*models.Spell"
case *models.Equipment:
return "*models.Equipment"
case *models.Weapon:
return "*models.Weapon"
case *models.Container:
return "*models.Container"
case *models.Transportation:
return "*models.Transportation"
case *models.Believe:
return "*models.Believe"
case *models.Char:
return "*models.Char"
case *models.Eigenschaft:
return "*models.Eigenschaft"
case *models.Lp:
return "*models.Lp"
case *models.Ap:
return "*models.Ap"
case *models.B:
return "*models.B"
case *models.Merkmale:
return "*models.Merkmale"
case *models.Erfahrungsschatz:
return "*models.Erfahrungsschatz"
case *models.Bennies:
return "*models.Bennies"
case *models.Vermoegen:
return "*models.Vermoegen"
case *models.SkFertigkeit:
return "*models.SkFertigkeit"
case *models.SkWaffenfertigkeit:
return "*models.SkWaffenfertigkeit"
case *models.SkAngeboreneFertigkeit:
return "*models.SkAngeboreneFertigkeit"
case *models.SkZauber:
return "*models.SkZauber"
case *models.EqAusruestung:
return "*models.EqAusruestung"
case *models.EqWaffe:
return "*models.EqWaffe"
case *models.EqContainer:
return "*models.EqContainer"
case *models.CharacterCreationSession:
return "*models.CharacterCreationSession"
case *models.AuditLogEntry:
return "*models.AuditLogEntry"
default:
return "UNKNOWN"
}
}