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" } }