package storage_test import ( "errors" "os" "path/filepath" "slices" "testing" "github.com/dariuszjaniak/warp/server/internal/storage" _ "github.com/dariuszjaniak/warp/server/internal/storage/drivers" ) // openTestDB opens each driver that can run without external services. The // postgres driver joins in when WARP_TEST_POSTGRES_DSN is set, which is how // CI runs the same tests against both engines. func openTestDB(t *testing.T, driver string) storage.Database { t.Helper() cfg := storage.Config{Driver: driver, MaxOpenConns: 4, MaxIdleConns: 2} switch driver { case storage.DriverSQLite: path := filepath.ToSlash(filepath.Join(t.TempDir(), "warp.db")) cfg.ConnectionString = "file:" + path + "?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)" case storage.DriverPostgres: dsn := os.Getenv("WARP_TEST_POSTGRES_DSN") if dsn == "" { t.Skip("set WARP_TEST_POSTGRES_DSN to test the postgres driver") } cfg.ConnectionString = dsn default: t.Fatalf("no test setup for driver %q", driver) } db, err := storage.Open(t.Context(), cfg, nil) if err != nil { t.Fatalf("Open(%s): %v", driver, err) } t.Cleanup(func() { _ = db.Close() }) return db } // testDrivers are run by every storage test. var testDrivers = []string{storage.DriverSQLite, storage.DriverPostgres} func TestDriversRegistered(t *testing.T) { got := storage.Drivers() for _, want := range []string{storage.DriverSQLite, storage.DriverPostgres, storage.DriverPocketBase} { if !slices.Contains(got, want) { t.Errorf("driver %q is not registered (have %v)", want, got) } } } func TestOpenUnknownDriver(t *testing.T) { _, err := storage.Open(t.Context(), storage.Config{Driver: "mysql"}, nil) if err == nil { t.Fatal("expected an error for an unknown driver") } } func TestPocketBaseIsNotImplemented(t *testing.T) { _, err := storage.Open(t.Context(), storage.Config{ Driver: storage.DriverPocketBase, ConnectionString: "./pb_data", }, nil) if !errors.Is(err, storage.ErrNotImplemented) { t.Fatalf("expected ErrNotImplemented, got %v", err) } } func TestMigrateAndSettings(t *testing.T) { for _, driver := range testDrivers { t.Run(driver, func(t *testing.T) { ctx := t.Context() db := openTestDB(t, driver) pending, err := db.PendingMigrations(ctx) if err != nil { t.Fatalf("PendingMigrations: %v", err) } if len(pending) == 0 { t.Skip("database already migrated by a previous run") } if err := db.Migrate(ctx); err != nil { t.Fatalf("Migrate: %v", err) } // Migrating twice must be a no-op, not an error: it runs on // every boot. if err := db.Migrate(ctx); err != nil { t.Fatalf("second Migrate: %v", err) } pending, err = db.PendingMigrations(ctx) if err != nil { t.Fatalf("PendingMigrations: %v", err) } if len(pending) != 0 { t.Errorf("after Migrate, still pending: %v", pending) } settings := db.Settings() if _, found, err := settings.Get(ctx, "federation.enabled"); err != nil { t.Fatalf("Get: %v", err) } else if found { t.Fatal("expected no stored value on a fresh database") } if err := settings.Set(ctx, "federation.enabled", "true"); err != nil { t.Fatalf("Set: %v", err) } if err := settings.Set(ctx, "federation.enabled", "false"); err != nil { t.Fatalf("Set (overwrite): %v", err) } value, found, err := settings.Get(ctx, "federation.enabled") if err != nil || !found { t.Fatalf("Get after Set: value=%q found=%v err=%v", value, found, err) } if value != "false" { t.Errorf("value = %q, want %q (the upsert must overwrite)", value, "false") } all, err := settings.GetAll(ctx) if err != nil { t.Fatalf("GetAll: %v", err) } if all["federation.enabled"] != "false" { t.Errorf("GetAll = %v", all) } if err := settings.Delete(ctx, "federation.enabled"); err != nil { t.Fatalf("Delete: %v", err) } if err := settings.Delete(ctx, "federation.enabled"); err != nil { t.Errorf("deleting an absent key must not fail: %v", err) } if _, found, _ := settings.Get(ctx, "federation.enabled"); found { t.Error("value survived Delete") } if err := db.Ping(ctx); err != nil { t.Errorf("Ping: %v", err) } if db.Driver() != driver { t.Errorf("Driver() = %q, want %q", db.Driver(), driver) } }) } }