Paket C: Demo-Modus setzt Datenbank bei jedem Start zurück und befüllt sie neu
This commit is contained in:
1 parent
71fa53ead3
commit
9c6598cb85
4 files changed
+137
-9
No files matched your search
+6
-6
@@ -37,18 +37,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer store.Close()
|
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 {
|
if cfg.DemoMode {
|
||||||
slog.Info("🧪 Demo-Modus aktiv (MINDA_DEMO=true): Richte Demo-Benutzer admin/admin und user/user ein...")
|
slog.Info("🧪 Demo-Modus aktiv: Setze Datenbank zurück und richte Demo-Accounts ein...")
|
||||||
if err := store.EnsureDemoAccounts(); err != nil {
|
if err := store.ResetAndSeedDemo(); err != nil {
|
||||||
slog.Warn("⚠️ Fehler beim Anlegen der Demo-Accounts", "error", err)
|
slog.Warn("⚠️ Fehler beim Einrichten des Demo-Modus", "error", err)
|
||||||
} else {
|
} else {
|
||||||
slog.Info("✅ Demo-Accounts bereit: admin / admin (Admin-Rolle) und user / user (Standard-Rolle)")
|
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()
|
wsHub := ws.NewHub()
|
||||||
|
|
||||||
staticFS, err := fs.Sub(embeddedDist, "dist")
|
staticFS, err := fs.Sub(embeddedDist, "dist")
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,11 @@ func (s *Store) resetOutdatedSchema() error {
|
|||||||
if version >= schemaVersion {
|
if version >= schemaVersion {
|
||||||
return nil
|
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())
|
conn, err := s.db.Conn(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -88,9 +93,7 @@ func (s *Store) resetOutdatedSchema() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = rows.Close()
|
_ = 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 {
|
if _, err := conn.ExecContext(ctx, `PRAGMA foreign_keys = OFF`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user