Files
2026-04-26 20:34:39 +02:00

116 lines
3.0 KiB
Go

package postgres_test
import (
"context"
"embed"
"io/fs"
"testing"
"time"
"galaxy/postgres"
testcontainers "github.com/testcontainers/testcontainers-go"
tcpostgres "github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
)
const smokeImage = "postgres:16-alpine"
//go:embed testdata/migrations/*.sql
var smokeMigrationsFS embed.FS
func TestPostgresPackageRoundTrip(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
t.Cleanup(cancel)
pgContainer, err := tcpostgres.Run(ctx,
smokeImage,
tcpostgres.WithDatabase("galaxy_smoke"),
tcpostgres.WithUsername("galaxy_smoke"),
tcpostgres.WithPassword("galaxy_smoke"),
// The Postgres image emits "ready to accept connections" twice during
// startup: once for the temporary bootstrap instance, once for the real
// listener on the mapped port. Waiting for the second occurrence
// prevents racing the bootstrap.
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
WithStartupTimeout(60*time.Second),
),
)
if err != nil {
t.Fatalf("start postgres container: %v", err)
}
t.Cleanup(func() {
if err := testcontainers.TerminateContainer(pgContainer); err != nil {
t.Errorf("terminate postgres container: %v", err)
}
})
dsn, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
if err != nil {
t.Fatalf("postgres connection string: %v", err)
}
cfg := postgres.DefaultConfig()
cfg.PrimaryDSN = dsn
cfg.OperationTimeout = 5 * time.Second
db, err := postgres.OpenPrimary(ctx, cfg)
if err != nil {
t.Fatalf("open primary: %v", err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Errorf("close db: %v", err)
}
})
if err := postgres.Ping(ctx, db, cfg.OperationTimeout); err != nil {
t.Fatalf("ping: %v", err)
}
migrationsDir, err := fs.Sub(smokeMigrationsFS, "testdata/migrations")
if err != nil {
t.Fatalf("sub migrations FS: %v", err)
}
if err := postgres.RunMigrations(ctx, db, migrationsDir, "."); err != nil {
t.Fatalf("run migrations: %v", err)
}
var insertedID int64
if err := db.QueryRowContext(ctx,
"INSERT INTO smoke (note) VALUES ($1) RETURNING id", "hello",
).Scan(&insertedID); err != nil {
t.Fatalf("insert returning id: %v", err)
}
if insertedID <= 0 {
t.Fatalf("inserted id = %d, want > 0", insertedID)
}
var note string
if err := db.QueryRowContext(ctx,
"SELECT note FROM smoke WHERE id = $1", insertedID,
).Scan(&note); err != nil {
t.Fatalf("select note: %v", err)
}
if note != "hello" {
t.Fatalf("note = %q, want %q", note, "hello")
}
}
func TestOpenReplicasReturnsNilWhenUnconfigured(t *testing.T) {
t.Parallel()
cfg := postgres.DefaultConfig()
cfg.PrimaryDSN = "postgres://localhost:5432/galaxy?sslmode=disable"
dbs, err := postgres.OpenReplicas(context.Background(), cfg)
if err != nil {
t.Fatalf("open replicas: %v", err)
}
if dbs != nil {
t.Fatalf("replicas = %v, want nil", dbs)
}
}