From a547d850a246c89e5b2fcf7d1e040a814906e88d Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 2 Jan 2026 12:07:28 +0100 Subject: [PATCH] TestCopyLiveDatabaseToFile now runs after connection to local MySQL database is working for go tests --- backend/character/handlers.go | 14 +++++++------- backend/character/handlers_test.go | 1 + backend/config/config.go | 4 ++++ backend/database/config.go | 2 +- backend/maintenance/api_maint_test.go | 12 ++++++++---- backend/maintenance/copy_db_test.go | 10 ++++++++-- backend/maintenance/handlers.go | 24 ++++++++++++++++++------ backend/models/model_character.go | 6 +++--- backend/user/comprehensive_test.go | 6 ++++++ docker/docker-compose.dev.yml | 4 ++-- 10 files changed, 58 insertions(+), 25 deletions(-) diff --git a/backend/character/handlers.go b/backend/character/handlers.go index 5ba3297..553eb3f 100644 --- a/backend/character/handlers.go +++ b/backend/character/handlers.go @@ -193,10 +193,10 @@ func splitSkills(object []models.SkFertigkeit) ([]models.SkFertigkeit, []models. type ExperienceAndWealthResponse struct { ExperiencePoints int `json:"experience_points"` Wealth struct { - Goldstücke int `json:"gold_coins"` // GS - Silberstücke int `json:"silver_coins"` // SS - Kupferstücke int `json:"copper_coins"` // KS - TotalInGS int `json:"total_in_ss"` // Gesamt in Silberstücken + Goldstuecke int `json:"gold_coins"` // GS + Silberstuecke int `json:"silver_coins"` // SS + Kupferstuecke int `json:"copper_coins"` // KS + TotalInGS int `json:"total_in_ss"` // Gesamt in Silberstücken } `json:"wealth"` } @@ -225,9 +225,9 @@ func GetCharacterExperienceAndWealth(c *gin.Context) { response := ExperienceAndWealthResponse{ ExperiencePoints: character.Erfahrungsschatz.EP, } - response.Wealth.Goldstücke = gs - response.Wealth.Silberstücke = ss - response.Wealth.Kupferstücke = ks + response.Wealth.Goldstuecke = gs + response.Wealth.Silberstuecke = ss + response.Wealth.Kupferstuecke = ks response.Wealth.TotalInGS = totalInSS c.JSON(http.StatusOK, response) diff --git a/backend/character/handlers_test.go b/backend/character/handlers_test.go index eb108bc..b4db29e 100644 --- a/backend/character/handlers_test.go +++ b/backend/character/handlers_test.go @@ -599,6 +599,7 @@ func TestFinalizeCharacterCreation(t *testing.T) { UserID: 1, Username: "bebe", Email: "frank@wuenscheonline.de", + Role: "admin", } err = database.DB.Create(&testUser).Error assert.NoError(t, err) diff --git a/backend/config/config.go b/backend/config/config.go index 3705cfc..1e44d30 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -145,6 +145,10 @@ func LoadConfig() *Config { // loadEnvFile lädt eine .env-Datei falls vorhanden func loadEnvFile() { envFiles := []string{".env", ".env.local"} + configFile := os.Getenv("CONFIG_FILE") + if configFile != "" { + envFiles = append(envFiles, configFile) + } for _, envFile := range envFiles { if _, err := os.Stat(envFile); err == nil { diff --git a/backend/database/config.go b/backend/database/config.go index a9fa523..87d32ab 100644 --- a/backend/database/config.go +++ b/backend/database/config.go @@ -70,7 +70,7 @@ func ConnectDatabaseOrig() *gorm.DB { // Falls keine URL konfiguriert ist, verwende Standard-MySQL-Konfiguration als Fallback dbURL := cfg.DatabaseURL if dbURL == "" { - dbURL = "bamort:bG4)efozrc@tcp(192.168.0.5:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local" + dbURL = "bamort:bG4)efozrc@tcp(localhost:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local" logger.Warn("Keine DATABASE_URL konfiguriert, verwende Standard-MySQL-Konfiguration") cfg.DatabaseType = "mysql" } diff --git a/backend/maintenance/api_maint_test.go b/backend/maintenance/api_maint_test.go index 7cb73fb..6a80b80 100644 --- a/backend/maintenance/api_maint_test.go +++ b/backend/maintenance/api_maint_test.go @@ -43,7 +43,9 @@ func TestMaintSetupCheck(t *testing.T) { } func TestGetMasterData(t *testing.T) { - database.SetupTestDB() //(false) + // Ensure fresh database connection + database.DB = nil + database.SetupTestDB() // Initialize a Gin router r := gin.Default() router.SetupGin(r) @@ -56,7 +58,9 @@ func TestGetMasterData(t *testing.T) { c.JSON(http.StatusOK, gin.H{"status": "Test OK"}) }) u := user.User{} - u.FirstId(1) + err := u.FirstId(1) + require.NoError(t, err, "Failed to load user with ID 1") + require.Equal(t, "admin", u.Role, "User 1 should be admin") // Create a test HTTP request req, _ := http.NewRequest("GET", "/api/maintenance", nil) @@ -82,8 +86,8 @@ func TestGetMasterData(t *testing.T) { Weapons []models.Weapon `json:"weapons"` } var dta dtaStruct - err := json.Unmarshal(respRecorder.Body.Bytes(), &dta) - assert.NoError(t, err) + errUnmarshal := json.Unmarshal(respRecorder.Body.Bytes(), &dta) + assert.NoError(t, errUnmarshal) } func TestGetMDSkillCategories(t *testing.T) { diff --git a/backend/maintenance/copy_db_test.go b/backend/maintenance/copy_db_test.go index 59fe569..7166435 100644 --- a/backend/maintenance/copy_db_test.go +++ b/backend/maintenance/copy_db_test.go @@ -1,6 +1,7 @@ package maintenance import ( + "bamort/config" "bamort/database" "bamort/models" "bamort/user" @@ -20,8 +21,13 @@ func TestCopyLiveDatabaseToFile(t *testing.T) { // Setup tempDir := t.TempDir() targetFile := filepath.Join(tempDir, "empty_backup.db") + envpath, _ := filepath.Abs("../.env.test") + os.Setenv("CONFIG_FILE", envpath) - // Create empty live database (only migrate structures, no data) + config.Cfg = config.LoadConfig() + + // Reset database connection to ensure we use environment config + database.DB = nil database.ConnectDatabase() liveDB := database.DB require.NotNil(t, liveDB, "Live database should be connected") @@ -51,7 +57,7 @@ func TestCopyLiveDatabaseToFile(t *testing.T) { var userCount int64 err = targetDB.Model(&user.User{}).Count(&userCount).Error require.NoError(t, err, "Should be able to count users") - assert.Equal(t, int64(2), userCount, "User table should be empty") + assert.GreaterOrEqual(t, userCount, int64(2), "User table should have more that 2 users") // Copy target file to database.PreparedTestDB for permanent storage // Close the database connection before copying diff --git a/backend/maintenance/handlers.go b/backend/maintenance/handlers.go index 64a4c9b..d66ddca 100644 --- a/backend/maintenance/handlers.go +++ b/backend/maintenance/handlers.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "path/filepath" + "reflect" "strings" "github.com/gin-gonic/gin" @@ -299,19 +300,30 @@ func copyTableData(sourceDB, targetDB *gorm.DB, model interface{}) error { batchSize := 100 totalBatches := (int(count) + batchSize - 1) / batchSize + // Get the element type for creating slice of records + modelType := reflect.TypeOf(model).Elem() + for offset := 0; offset < int(count); offset += batchSize { batchNum := (offset / batchSize) + 1 logger.Debug("Kopiere Batch %d/%d für %s (Offset: %d, Limit: %d)", batchNum, totalBatches, tableName, offset, batchSize) - var records []map[string]interface{} + // Create a slice of the model type using reflection + sliceType := reflect.SliceOf(modelType) + recordsValue := reflect.MakeSlice(sliceType, 0, batchSize) + recordsPtr := reflect.New(sliceType) + recordsPtr.Elem().Set(recordsValue) - // Batch aus MariaDB lesen - if err := sourceDB.Model(model).Offset(offset).Limit(batchSize).Find(&records).Error; err != nil { + // Batch aus MariaDB lesen (use proper struct type instead of map) + if err := sourceDB.Model(model).Offset(offset).Limit(batchSize).Find(recordsPtr.Interface()).Error; err != nil { logger.Error("Fehler beim Lesen von Batch %d für %s: %s", batchNum, tableName, err.Error()) return err } - if len(records) == 0 { + // Get the actual records from reflection + records := recordsPtr.Elem().Interface() + recordsLen := recordsPtr.Elem().Len() + + if recordsLen == 0 { logger.Debug("Keine weiteren Datensätze für %s", tableName) break } @@ -320,12 +332,12 @@ func copyTableData(sourceDB, targetDB *gorm.DB, model interface{}) error { // Verwende Clauses.OnConflict um bestehende Datensätze zu ersetzen if err := targetDB.Model(model).Clauses(clause.OnConflict{ UpdateAll: true, - }).Create(&records).Error; err != nil { + }).Create(records).Error; err != nil { logger.Error("Fehler beim Einfügen von Batch %d für %s: %s", batchNum, tableName, err.Error()) return err } - logger.Debug("Batch %d/%d für %s erfolgreich kopiert (%d Datensätze)", batchNum, totalBatches, tableName, len(records)) + logger.Debug("Batch %d/%d für %s erfolgreich kopiert (%d Datensätze)", batchNum, totalBatches, tableName, recordsLen) } logger.Info("Tabelle %s erfolgreich kopiert (%d Datensätze total)", tableName, count) diff --git a/backend/models/model_character.go b/backend/models/model_character.go index c18b699..9fb7f7d 100644 --- a/backend/models/model_character.go +++ b/backend/models/model_character.go @@ -69,9 +69,9 @@ type Bennies struct { type Vermoegen struct { BamortCharTrait - Goldstuecke int `json:"goldstücke"` // GS - Silberstuecke int `json:"silberstücke"` // SS - Kupferstuecke int `json:"kupferstücke"` // KS + Goldstuecke int `gorm:"column:goldstuecke" json:"goldstücke"` // GS + Silberstuecke int `gorm:"column:silberstuecke" json:"silberstücke"` // SS + Kupferstuecke int `gorm:"column:kupferstuecke" json:"kupferstücke"` // KS } type Char struct { diff --git a/backend/user/comprehensive_test.go b/backend/user/comprehensive_test.go index 01adf14..24173d5 100644 --- a/backend/user/comprehensive_test.go +++ b/backend/user/comprehensive_test.go @@ -565,6 +565,7 @@ func TestRegisterUser(t *testing.T) { Username: "bebe", PasswordHash: "osiris", Email: "frank@wuenscheonline.de", + Role: "admin", } hashedPassword := md5.Sum([]byte(usr.PasswordHash)) @@ -576,6 +577,7 @@ func TestRegisterUser(t *testing.T) { Username: "bubnu", PasswordHash: "osiris", Email: "spacer@wuenscheonline.de", + Role: "standard", } hashedPassword = md5.Sum([]byte(usr2.PasswordHash)) usr2.PasswordHash = hex.EncodeToString(hashedPassword[:]) @@ -591,6 +593,7 @@ func TestLoginUser(t *testing.T) { Username: "logintest", PasswordHash: "osiris", Email: "login@test.com", + Role: "standard", } hashedPassword := md5.Sum([]byte(usr.PasswordHash)) usr.PasswordHash = hex.EncodeToString(hashedPassword[:]) @@ -686,6 +689,7 @@ func TestUser_EdgeCases(t *testing.T) { Username: "", PasswordHash: "", Email: "", + Role: "standard", } err := user.Save() assert.NoError(t, err, "Should save user with empty strings") @@ -704,6 +708,7 @@ func TestUser_EdgeCases(t *testing.T) { Username: "longuser", PasswordHash: longString, Email: "long@example.com", + Role: "standard", } err = user2.Create() assert.NoError(t, err, "Should create user with long password hash") @@ -717,6 +722,7 @@ func TestUser_ConcurrentAccess(t *testing.T) { Username: "concurrentuser", PasswordHash: "hash", Email: "concurrent@example.com", + Role: "standard", } err := user.Create() require.NoError(t, err, "Should be able to create test user") diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 1a2d930..5518eba 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -48,8 +48,8 @@ services: image: mariadb:11.4 container_name: bamort-mariadb-dev restart: unless-stopped - #ports: - # - "3306:3306" + ports: + - "3306:3306" environment: MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-secure_root_password} MARIADB_DATABASE: ${MARIADB_DATABASE:-bamort}