From 84158683cd069f7f3b1a00634de9c4907a19409f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 10 Aug 2025 21:01:18 +0200 Subject: [PATCH] Set Test environment in tests --- backend/.env.example | 8 +- backend/.env.production | 4 +- backend/TESTING.md | 152 ++++++++++++++++++++++++++++ backend/character/character_test.go | 14 +++ backend/character/handlers_test.go | 11 ++ backend/config/config.go | 17 ++-- backend/config/config_test.go | 18 ++++ backend/database/config.go | 57 +++++++++-- backend/logger/logger_test.go | 22 ++++ backend/testutils/testutils.go | 61 +++++++++++ 10 files changed, 346 insertions(+), 18 deletions(-) create mode 100644 backend/TESTING.md create mode 100644 backend/testutils/testutils.go diff --git a/backend/.env.example b/backend/.env.example index 233b7c2..189b541 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -26,7 +26,13 @@ LOG_LEVEL=DEBUG # ====================== # Datenbank Konfiguration # ====================== -# DATABASE_URL=postgresql://user:password@localhost:5432/bamort_db +# Unterstützte Typen: mysql, sqlite +DATABASE_TYPE=mysql +# Beispiel-URLs für verschiedene Datenbank-Typen: +# MySQL: DATABASE_URL=user:password@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=True&loc=Local +# PostgreSQL: DATABASE_URL=postgresql://user:password@localhost:5432/database +# SQLite: DATABASE_URL=./database.db +DATABASE_URL= # ====================== # Beispiel-Konfigurationen diff --git a/backend/.env.production b/backend/.env.production index 6207be1..867dc7d 100644 --- a/backend/.env.production +++ b/backend/.env.production @@ -23,4 +23,6 @@ LOG_LEVEL=INFO # ====================== # Datenbank Konfiguration # ====================== -# DATABASE_URL=postgresql://user:password@prod-host:5432/bamort_prod_db +# Unterstützte Typen: mysql, sqlite +DATABASE_TYPE=mysql +# DATABASE_URL=user:password@tcp(prod-host:3306)/bamort_prod?charset=utf8mb4&parseTime=True&loc=Local diff --git a/backend/TESTING.md b/backend/TESTING.md new file mode 100644 index 0000000..b535835 --- /dev/null +++ b/backend/TESTING.md @@ -0,0 +1,152 @@ +# Test Environment Configuration + +## Überblick + +Alle Tests in diesem Projekt sind so konfiguriert, dass sie `ENVIRONMENT=test` verwenden, um sicherzustellen, dass: + +1. Die richtige Datenbank-Konfiguration verwendet wird (Test-DB statt Production-DB) +2. Logger-Ausgaben reduziert sind +3. Tests isoliert von der Produktionsumgebung laufen + +## Test-Umgebung einrichten + +### Option 1: Lokale setupTestEnvironment Funktion (empfohlen) + +Jeder Test sollte eine lokale `setupTestEnvironment` Funktion verwenden: + +```go +package mypackage + +import ( + "os" + "testing" +) + +// setupTestEnvironment setzt ENVIRONMENT=test für Tests +func setupTestEnvironment(t *testing.T) { + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) +} + +func TestMyFunction(t *testing.T) { + setupTestEnvironment(t) + + // Ihr Test-Code hier... +} +``` + +### Option 2: testutils Package (für komplexere Setups) + +Für komplexere Test-Setups verwenden Sie das `testutils` Package: + +```go +package mypackage + +import ( + "bamort/testutils" + "testing" +) + +func TestMyFunction(t *testing.T) { + testutils.SetupTestEnvironment(t) + + // Oder für spezifische Konfiguration: + testutils.SetupTestEnvironmentWithConfig(t, map[string]string{ + "DEBUG": "false", + "LOG_LEVEL": "ERROR", + }) + + // Ihr Test-Code hier... +} +``` + +## Aktualisierte Test-Dateien + +Folgende Test-Dateien wurden bereits aktualisiert: + +- ✅ `config/config_test.go` - Alle Tests setzen ENVIRONMENT=test +- ✅ `logger/logger_test.go` - Alle Tests setzen ENVIRONMENT=test +- ✅ `character/character_test.go` - setupTestEnvironment hinzugefügt +- ✅ `character/handlers_test.go` - Test-Umgebung konfiguriert + +## Database-Tests + +Die `database.ConnectDatabase()` Funktion erkennt automatisch `ENVIRONMENT=test` und verwendet die Test-Datenbank: + +```go +func ConnectDatabase() *gorm.DB { + cfg := config.LoadConfig() + + if cfg.Environment == "test" { + logger.Debug("Test-Umgebung erkannt, verwende Test-Datenbank") + SetupTestDB() + } else { + logger.Debug("Verwende konfigurierte Datenbank (%s)", cfg.DatabaseType) + return ConnectDatabaseOrig() + } + + return DB +} +``` + +## Best Practices + +1. **Immer setupTestEnvironment() aufrufen**: Jeder Test sollte die Umgebung konfigurieren +2. **Cleanup automatisch**: Verwenden Sie `t.Cleanup()` um ursprüngliche Werte wiederherzustellen +3. **Test-Isolation**: Tests sollten sich nicht gegenseitig beeinflussen +4. **Keine Produktionsdaten**: Tests verwenden immer Test-Datenbank + +## Verifikation + +Um zu prüfen, ob alle Tests korrekt konfiguriert sind: + +```bash +# Script ausführen +./verify_test_environment.sh + +# Einzelne Packages testen +go test ./config -v +go test ./logger -v +go test ./character -v +``` + +## Migration bestehender Tests + +Für bestehende Tests, die noch nicht aktualisiert wurden: + +1. Import für `os` hinzufügen +2. `setupTestEnvironment` Funktion hinzufügen +3. `setupTestEnvironment(t)` am Anfang jeder Test-Funktion aufrufen + +Beispiel: + +```go +// Vor der Migration +func TestMyFunction(t *testing.T) { + // Test-Code... +} + +// Nach der Migration +func TestMyFunction(t *testing.T) { + setupTestEnvironment(t) + // Test-Code... +} +``` + +## Troubleshooting + +**Problem**: Test verwendet Produktions-Datenbank +**Lösung**: `setupTestEnvironment(t)` am Anfang des Tests aufrufen + +**Problem**: Umgebungsvariablen beeinflussen sich zwischen Tests +**Lösung**: `t.Cleanup()` verwenden für automatisches Aufräumen + +**Problem**: Import-Zyklen bei testutils +**Lösung**: Lokale `setupTestEnvironment` Funktion in jedem Package verwenden diff --git a/backend/character/character_test.go b/backend/character/character_test.go index d8388df..03a8914 100644 --- a/backend/character/character_test.go +++ b/backend/character/character_test.go @@ -12,6 +12,19 @@ import ( "github.com/stretchr/testify/assert" ) +// setupTestEnvironment setzt ENVIRONMENT=test für Tests +func setupTestEnvironment(t *testing.T) { + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) +} + // ReadImageAsBase64 reads an image file and returns it as a Base64 string // with the prefix "data:mimeType;base64," func ReadImageAsBase64(filePath string) (string, error) { @@ -746,6 +759,7 @@ func charTests(t *testing.T, char *models.Char) { } func TestCreateChar(t *testing.T) { + setupTestEnvironment(t) database.SetupTestDB() err := models.MigrateStructure() diff --git a/backend/character/handlers_test.go b/backend/character/handlers_test.go index f1c5991..9186c65 100644 --- a/backend/character/handlers_test.go +++ b/backend/character/handlers_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "os" "testing" "bamort/database" @@ -15,6 +16,16 @@ import ( ) func TestImproveSkillHandler(t *testing.T) { + // Setup test environment + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) // Setup test database database.SetupTestDB(true, true) defer database.ResetTestDB() diff --git a/backend/config/config.go b/backend/config/config.go index f1abdc6..65dca25 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -13,7 +13,8 @@ type Config struct { ServerPort string // Database Konfiguration - DatabaseURL string + DatabaseURL string + DatabaseType string // Logging Konfiguration DebugMode bool @@ -26,11 +27,12 @@ type Config struct { // defaultConfig gibt die Standard-Konfiguration zurück func defaultConfig() *Config { return &Config{ - ServerPort: "8180", - DatabaseURL: "", - DebugMode: false, - LogLevel: "INFO", - Environment: "production", + ServerPort: "8180", + DatabaseURL: "", + DatabaseType: "mysql", + DebugMode: false, + LogLevel: "INFO", + Environment: "production", } } @@ -53,6 +55,9 @@ func LoadConfig() *Config { if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" { config.DatabaseURL = dbURL } + if dbType := os.Getenv("DATABASE_TYPE"); dbType != "" { + config.DatabaseType = strings.ToLower(dbType) + } // Debug Mode if debug := os.Getenv("DEBUG"); debug != "" { diff --git a/backend/config/config_test.go b/backend/config/config_test.go index 73f3f0b..b01abf7 100644 --- a/backend/config/config_test.go +++ b/backend/config/config_test.go @@ -5,7 +5,21 @@ import ( "testing" ) +// setupTestEnvironment setzt ENVIRONMENT=test für Tests und stellt es nach dem Test wieder her +func setupTestEnvironment(t *testing.T) { + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) +} + func TestLoadEnvFile(t *testing.T) { + setupTestEnvironment(t) // Test-Datei erstellen envContent := `# Test .env file DEBUG=true @@ -97,6 +111,8 @@ QUOTED_VALUE='single quotes' } func TestEnvVariablesPrecedence(t *testing.T) { + setupTestEnvironment(t) + // Test, dass bereits gesetzte Umgebungsvariablen Vorrang haben envContent := `DEBUG=false LOG_LEVEL=ERROR` @@ -130,6 +146,8 @@ LOG_LEVEL=ERROR` } func TestLoadConfigWithEnvFile(t *testing.T) { + setupTestEnvironment(t) + // Test-Konfiguration mit .env-Datei envContent := `ENVIRONMENT=development DEBUG=true diff --git a/backend/database/config.go b/backend/database/config.go index 973479d..eb7fd96 100644 --- a/backend/database/config.go +++ b/backend/database/config.go @@ -1,6 +1,8 @@ package database import ( + "bamort/config" + "bamort/logger" "database/sql/driver" "encoding/json" "errors" @@ -10,6 +12,7 @@ import ( "log" "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -37,22 +40,56 @@ var ( ) func ConnectDatabase() *gorm.DB { - SetupTestDB() - /* - db, err := gorm.Open(sqlite.Open(PreparedTestDB), &gorm.Config{}) - if err != nil { - log.Fatal("Failed to connect to database:", err) - } - DB = db - */ + // Konfiguration laden um zu entscheiden, welche Datenbank verwendet werden soll + cfg := config.LoadConfig() + + // In Test-Umgebung verwende Test-DB, sonst die konfigurierte Datenbank + if cfg.Environment == "test" { + logger.Debug("Test-Umgebung erkannt, verwende Test-Datenbank") + SetupTestDB() + } else { + logger.Debug("Verwende konfigurierte Datenbank (%s)", cfg.DatabaseType) + return ConnectDatabaseOrig() + } + return DB } func ConnectDatabaseOrig() *gorm.DB { - dsn := "bamort:bG4)efozrc@tcp(192.168.0.5:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local" - database, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + // Konfiguration laden + cfg := config.LoadConfig() + + logger.Debug("Datenbank-Konfiguration geladen: Type=%s, URL=%s", cfg.DatabaseType, cfg.DatabaseURL) + + // 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" + logger.Warn("Keine DATABASE_URL konfiguriert, verwende Standard-MySQL-Konfiguration") + cfg.DatabaseType = "mysql" + } + + var database *gorm.DB + var err error + + // Datenbanktyp-spezifische Verbindung + switch cfg.DatabaseType { + case "mysql": + logger.Debug("Verbinde mit MySQL-Datenbank...") + database, err = gorm.Open(mysql.Open(dbURL), &gorm.Config{}) + case "sqlite": + logger.Debug("Verbinde mit SQLite-Datenbank...") + database, err = gorm.Open(sqlite.Open(dbURL), &gorm.Config{}) + default: + logger.Error("Nicht unterstützter Datenbanktyp: %s. Verwende MySQL als Fallback.", cfg.DatabaseType) + database, err = gorm.Open(mysql.Open(dbURL), &gorm.Config{}) + } + if err != nil { + logger.Error("Fehler beim Verbinden mit der Datenbank (%s): %s", cfg.DatabaseType, err.Error()) log.Fatal("Failed to connect to database:", err) } + + logger.Info("Erfolgreich mit %s-Datenbank verbunden", cfg.DatabaseType) DB = database return DB } diff --git a/backend/logger/logger_test.go b/backend/logger/logger_test.go index 1505e75..e5ba0b3 100644 --- a/backend/logger/logger_test.go +++ b/backend/logger/logger_test.go @@ -5,7 +5,21 @@ import ( "testing" ) +// setupTestEnvironment setzt ENVIRONMENT=test für Tests +func setupTestEnvironment(t *testing.T) { + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) +} + func TestLogLevels(t *testing.T) { + setupTestEnvironment(t) // Test String-Representation der Log-Levels tests := []struct { level LogLevel @@ -25,6 +39,8 @@ func TestLogLevels(t *testing.T) { } func TestDebugModeFromEnv(t *testing.T) { + setupTestEnvironment(t) + // Test verschiedene Umgebungsvariablen-Werte tests := []struct { envValue string @@ -55,6 +71,8 @@ func TestDebugModeFromEnv(t *testing.T) { } func TestMinLogLevelFromEnv(t *testing.T) { + setupTestEnvironment(t) + // Test verschiedene LOG_LEVEL Werte tests := []struct { envValue string @@ -97,6 +115,8 @@ func TestMinLogLevelFromEnv(t *testing.T) { } func TestSetDebugMode(t *testing.T) { + setupTestEnvironment(t) + // Test Debug-Modus aktivieren SetDebugMode(true) if !IsDebugEnabled() { @@ -111,6 +131,8 @@ func TestSetDebugMode(t *testing.T) { } func TestSetMinLogLevel(t *testing.T) { + setupTestEnvironment(t) + // Test verschiedene Log-Level setzen levels := []LogLevel{DEBUG, INFO, WARN, ERROR} diff --git a/backend/testutils/testutils.go b/backend/testutils/testutils.go new file mode 100644 index 0000000..203d908 --- /dev/null +++ b/backend/testutils/testutils.go @@ -0,0 +1,61 @@ +package testutils + +import ( + "os" + "testing" +) + +// SetupTestEnvironment konfiguriert die Umgebung für Tests +// Diese Funktion setzt ENVIRONMENT=test und stellt sicher, dass nach dem Test +// die ursprüngliche Umgebung wiederhergestellt wird +func SetupTestEnvironment(t *testing.T) { + // Sicherstellen, dass ENVIRONMENT auf "test" gesetzt ist + originalEnv := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + + // Cleanup-Funktion registrieren + t.Cleanup(func() { + if originalEnv != "" { + os.Setenv("ENVIRONMENT", originalEnv) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) +} + +// SetupTestEnvironmentWithConfig konfiguriert die Test-Umgebung mit spezifischen Werten +func SetupTestEnvironmentWithConfig(t *testing.T, envVars map[string]string) { + // Ursprüngliche Werte sichern + originalVars := make(map[string]string) + for key := range envVars { + originalVars[key] = os.Getenv(key) + } + + // Sicherstellen, dass ENVIRONMENT auf "test" gesetzt ist + envVars["ENVIRONMENT"] = "test" + + // Test-Umgebungsvariablen setzen + for key, value := range envVars { + os.Setenv(key, value) + } + + // Cleanup-Funktion registrieren + t.Cleanup(func() { + for key, originalValue := range originalVars { + if originalValue != "" { + os.Setenv(key, originalValue) + } else { + os.Unsetenv(key) + } + } + }) +} + +// EnsureTestEnvironment ist eine einfache Prüfung, ob die Test-Umgebung korrekt ist +// Kann in Tests verwendet werden um sicherzustellen, dass ENVIRONMENT=test gesetzt ist +func EnsureTestEnvironment(t *testing.T) { + if os.Getenv("ENVIRONMENT") != "test" { + t.Errorf("ENVIRONMENT sollte 'test' sein, ist aber '%s'. Vergessen Sie SetupTestEnvironment() aufzurufen?", + os.Getenv("ENVIRONMENT")) + } +}