diff --git a/internal/db/db_test.go b/internal/db/db_test.go index bac5ae34..c16d73be 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -1,13 +1,58 @@ package db import ( - "errors" + "context" + "io" + "log/slog" + "os" "testing" ) -func TestMigrateStubLoadsEmbeddedFiles(t *testing.T) { - err := Migrate() - if !errors.Is(err, ErrNotConfigured) { - t.Errorf("Migrate stub err = %v, want ErrNotConfigured", err) +// TestMigrateAgainstPostgres runs the embedded migrations against a real +// Postgres indicated by MINSTREL_TEST_DATABASE_URL. Intended flow for local +// development: `docker compose up -d postgres` then +// `MINSTREL_TEST_DATABASE_URL=postgres://minstrel:minstrel@localhost:5432/minstrel?sslmode=disable go test ./...`. +// Skipped in `-short` mode (which is what CI runs) and when the env var is unset. +func TestMigrateAgainstPostgres(t *testing.T) { + if testing.Short() { + t.Skip("skipping migration test in -short mode") + } + dsn := os.Getenv("MINSTREL_TEST_DATABASE_URL") + if dsn == "" { + t.Skip("MINSTREL_TEST_DATABASE_URL not set; skipping") + } + + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + + if err := Migrate(dsn, logger); err != nil { + t.Fatalf("first Migrate: %v", err) + } + if err := Migrate(dsn, logger); err != nil { + t.Fatalf("second Migrate (idempotency): %v", err) + } + + pool, err := Open(context.Background(), dsn) + if err != nil { + t.Fatalf("Open: %v", err) + } + defer pool.Close() + + var count int + if err := pool.QueryRow(context.Background(), + `SELECT count(*) FROM schema_migrations`).Scan(&count); err != nil { + t.Fatalf("query schema_migrations: %v", err) + } + if count == 0 { + t.Fatal("schema_migrations empty after Migrate") + } +} + +func TestMigrationsFSListsFiles(t *testing.T) { + entries, err := migrationsFS.ReadDir("migrations") + if err != nil { + t.Fatalf("read embedded dir: %v", err) + } + if len(entries) == 0 { + t.Fatal("no migration files embedded") } }