- lagoon.OrderBy takes variadic lagoon.OrderOption values; lagoon.Collate(name) emits a validated, double-quoted COLLATE clause (e.g. "pl-x-icu") - remove the exported CheckLocale and the ICU pl-PL check from Open and Use - framework test containers and per-test databases are plain PostgreSQL - lagoon README, root README and docs pages drop the locale requirement; queries-and-pagination gains a "Sorting with a collation" section backed by ExampleCollate
146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
package lighthouse
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/lagoon"
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
|
)
|
|
|
|
var (
|
|
lhPG *postgres.PostgresContainer
|
|
lhSQL *sql.DB
|
|
lhDSN string
|
|
lhPGErr error
|
|
dbSeq atomic.Int64
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
code := 1
|
|
if !testShort() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
lhPGErr = startLighthousePostgres(ctx)
|
|
cancel()
|
|
if lhPGErr != nil {
|
|
fmt.Fprintf(os.Stderr, "lighthouse: testcontainers postgres: %v\n", lhPGErr)
|
|
stopLighthousePostgres()
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
code = m.Run()
|
|
stopLighthousePostgres()
|
|
os.Exit(code)
|
|
}
|
|
|
|
func testShort() bool {
|
|
for _, a := range os.Args {
|
|
if a == "-test.short" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func startLighthousePostgres(ctx context.Context) error {
|
|
ctr, err := postgres.Run(ctx,
|
|
"postgres:16-alpine",
|
|
postgres.WithDatabase("lighthouse"),
|
|
postgres.WithUsername("lighthouse"),
|
|
postgres.WithPassword("lighthouse"),
|
|
postgres.BasicWaitStrategies(),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lhPG = ctr
|
|
dsn, err := ctr.ConnectionString(ctx, "sslmode=disable")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := db.PingContext(ctx); err != nil {
|
|
_ = db.Close()
|
|
return err
|
|
}
|
|
lhSQL = db
|
|
lhDSN = dsn
|
|
return nil
|
|
}
|
|
|
|
func stopLighthousePostgres() {
|
|
if lhSQL != nil {
|
|
_ = lhSQL.Close()
|
|
}
|
|
if lhPG != nil {
|
|
_ = testcontainers.TerminateContainer(lhPG)
|
|
}
|
|
}
|
|
|
|
func adminDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
if testing.Short() {
|
|
t.Skip("requires testcontainers postgres")
|
|
}
|
|
if lhPGErr != nil {
|
|
t.Fatalf("postgres unavailable: %v", lhPGErr)
|
|
}
|
|
if lhSQL == nil {
|
|
t.Fatal("postgres unavailable: container was not started")
|
|
}
|
|
return lhSQL
|
|
}
|
|
|
|
// migratedDB returns a dedicated database migrated with
|
|
// lagoon.Migrate (River v7 and summer_jobs) plus its DSN.
|
|
func migratedDB(t *testing.T) (*sql.DB, string) {
|
|
t.Helper()
|
|
admin := adminDB(t)
|
|
ctx := t.Context()
|
|
name := fmt.Sprintf("lighthouse_%d", dbSeq.Add(1))
|
|
if _, err := admin.ExecContext(ctx, `CREATE DATABASE `+name+` TEMPLATE template0 ENCODING 'UTF8'`); err != nil && !strings.Contains(err.Error(), "already exists") {
|
|
t.Fatalf("create %s: %v", name, err)
|
|
}
|
|
dsn, err := dsnWithDB(lhDSN, name)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = db.Close()
|
|
_, _ = admin.ExecContext(context.Background(), `DROP DATABASE IF EXISTS `+name+` WITH (FORCE)`)
|
|
})
|
|
gdb, err := lagoon.Use(ctx, db)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := lagoon.Migrate(gdb, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return db, dsn
|
|
}
|
|
|
|
func dsnWithDB(dsn, name string) (string, error) {
|
|
u, err := url.Parse(dsn)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
u.Path = "/" + name
|
|
return u.String(), nil
|
|
}
|