Paket C: Demo-Modus setzt Datenbank bei jedem Start zurück und befüllt sie neu

This commit is contained in:
sandrodach committed 2026-10-28 23:04:43 +01:00
1 parent 71fa53ead3
commit 9c6598cb85
4 files changed
+137 -9

No files matched your search

+6 -6
View File
@@ -37,18 +37,18 @@ func main() {
}
defer store.Close()
// Seed SMTP defaults from environment variables into DB if not yet configured in DB
store.SeedSMTPDefaultsFromEnv(cfg.SMTPHost, cfg.SMTPPort, cfg.SMTPUser, cfg.SMTPPass, cfg.SMTPFrom, cfg.SMTPTLS, cfg.SMTPSkipVerify)
if cfg.DemoMode {
slog.Info("🧪 Demo-Modus aktiv (MINDA_DEMO=true): Richte Demo-Benutzer admin/admin und user/user ein...")
if err := store.EnsureDemoAccounts(); err != nil {
slog.Warn("⚠️ Fehler beim Anlegen der Demo-Accounts", "error", err)
slog.Info("🧪 Demo-Modus aktiv: Setze Datenbank zurück und richte Demo-Accounts ein...")
if err := store.ResetAndSeedDemo(); err != nil {
slog.Warn("⚠️ Fehler beim Einrichten des Demo-Modus", "error", err)
} else {
slog.Info("✅ Demo-Accounts bereit: admin / admin (Admin-Rolle) und user / user (Standard-Rolle)")
}
}
// Seed SMTP defaults from environment variables into DB if not yet configured in DB
store.SeedSMTPDefaultsFromEnv(cfg.SMTPHost, cfg.SMTPPort, cfg.SMTPUser, cfg.SMTPPass, cfg.SMTPFrom, cfg.SMTPTLS, cfg.SMTPSkipVerify)
wsHub := ws.NewHub()
staticFS, err := fs.Sub(embeddedDist, "dist")
+21
View File
@@ -0,0 +1,21 @@
package storage
import (
"fmt"
"log/slog"
)
// ResetAndSeedDemo drops all tables, recreates the schema, and seeds demo accounts and demo data.
func (s *Store) ResetAndSeedDemo() error {
if err := s.dropAllTables(); err != nil {
return fmt.Errorf("reset demo: drop all tables: %w", err)
}
if err := s.migrate(); err != nil {
return fmt.Errorf("reset demo: migrate schema: %w", err)
}
if err := s.EnsureDemoAccounts(); err != nil {
return fmt.Errorf("reset demo: ensure demo accounts: %w", err)
}
slog.Info("🧹 Demo-Modus: Datenbank für Testumgebung zurückgesetzt und neu befüllt")
return nil
}
+104
View File
@@ -0,0 +1,104 @@
package storage
import (
"path/filepath"
"testing"
"github.com/sandrodach/mindsift/internal/auth"
)
func TestResetAndSeedDemo(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "demo_test.db")
store, err := NewStore(dbPath)
if err != nil {
t.Fatalf("NewStore failed: %v", err)
}
defer store.Close()
// Create custom user and data
hash, _ := auth.HashPassword("secret")
customUser, err := store.CreateUserWithUsername("custom@test.local", "customuser", hash, "Custom", "member")
if err != nil {
t.Fatalf("CreateUser failed: %v", err)
}
_, err = store.CreateThought(customUser.ID, "A personal secret note", "normal", "manual", "")
if err != nil {
t.Fatalf("CreateThought failed: %v", err)
}
// Call ResetAndSeedDemo
if err := store.ResetAndSeedDemo(); err != nil {
t.Fatalf("ResetAndSeedDemo failed: %v", err)
}
// Custom user must be wiped
u, err := store.GetUserByID(customUser.ID)
if err == nil && u != nil {
t.Fatalf("expected custom user to be wiped, but still found: %v", u)
}
// Demo accounts admin and user must exist
admin, err := store.GetUserByLogin("admin")
if err != nil || admin == nil {
t.Fatalf("expected demo admin to exist after reset: %v", err)
}
user, err := store.GetUserByLogin("user")
if err != nil || user == nil {
t.Fatalf("expected demo user to exist after reset: %v", err)
}
// Resetting again should work cleanly (idempotence on repeated server starts)
if err := store.ResetAndSeedDemo(); err != nil {
t.Fatalf("second ResetAndSeedDemo failed: %v", err)
}
}
func TestWithoutDemoModeDataIsNotWiped(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "nodemo_test.db")
// 1. Initial start without demo mode
store1, err := NewStore(dbPath)
if err != nil {
t.Fatalf("first NewStore failed: %v", err)
}
hash, _ := auth.HashPassword("secret123")
user1, err := store1.CreateUserWithUsername("persistent@example.com", "persistent", hash, "Persistent", "member")
if err != nil {
t.Fatalf("CreateUserWithUsername failed: %v", err)
}
thought1, err := store1.CreateThought(user1.ID, "Important persistent note #keep", "high", "manual", "")
if err != nil {
t.Fatalf("CreateThought failed: %v", err)
}
// Close store simulating server shutdown
store1.Close()
// 2. Server restart without demo mode (standard NewStore)
store2, err := NewStore(dbPath)
if err != nil {
t.Fatalf("second NewStore failed: %v", err)
}
defer store2.Close()
// Verify existing data is preserved
foundUser, err := store2.GetUserByID(user1.ID)
if err != nil || foundUser == nil {
t.Fatalf("expected user %s to be preserved without demo mode, err: %v", user1.ID, err)
}
if foundUser.Username != "persistent" {
t.Fatalf("expected username 'persistent', got '%s'", foundUser.Username)
}
thoughts, err := store2.GetThoughts(user1.ID, "", 10)
if err != nil {
t.Fatalf("GetThoughts failed: %v", err)
}
if len(thoughts) != 1 || thoughts[0].ID != thought1.ID {
t.Fatalf("expected thought %s to persist, got: %v", thought1.ID, thoughts)
}
}
+6 -3
View File
@@ -70,6 +70,11 @@ func (s *Store) resetOutdatedSchema() error {
if version >= schemaVersion {
return nil
}
slog.Warn("Veraltetes Datenbankschema: alle Daten werden gelöscht und neu angelegt", "version", version, "neu", schemaVersion)
return s.dropAllTables()
}
func (s *Store) dropAllTables() error {
conn, err := s.db.Conn(context.Background())
if err != nil {
return err
@@ -88,9 +93,7 @@ func (s *Store) resetOutdatedSchema() error {
}
}
_ = rows.Close()
if len(tables) > 0 {
slog.Warn("Veraltetes Datenbankschema: alle Daten werden gelöscht und neu angelegt", "version", version, "neu", schemaVersion)
}
if _, err := conn.ExecContext(ctx, `PRAGMA foreign_keys = OFF`); err != nil {
return err
}