feat(17-03): sitemap plugin tracer, /sitemap.xml byte-identical to PHP
- golem15.sitemap plugin: golem15_sitemap_definitions migration, Definition model (Fillable theme and data) with tolerant PHP-shaped item decoding and PHP toArray item encoding - Url::to and buildUrl port, libxml-format serializer, generator for url and registry items - GET /sitemap.xml serves the definition with theme default (404 without one, empty body without items) - tests/php/gen-goldens.php and the tracer golden; TestSitemapGolden and the Postgres TestSitemapTracer
This commit is contained in:
115
testmain_test.go
Normal file
115
testmain_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package sitemap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
||||
)
|
||||
|
||||
var (
|
||||
pgContainer *postgres.PostgresContainer
|
||||
pgAdmin *sql.DB
|
||||
pgDSN string
|
||||
pgErr error
|
||||
)
|
||||
|
||||
// TestMain starts one Postgres container for the smoke tests unless the run
|
||||
// is -short. go test passes -test.short=true, so the flag is parsed and read
|
||||
// through testing.Short, never by scanning os.Args.
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
if !testing.Short() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
pgErr = startPostgres(ctx)
|
||||
cancel()
|
||||
if pgErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "golem15.sitemap: testcontainers postgres: %v\n", pgErr)
|
||||
}
|
||||
}
|
||||
code := m.Run()
|
||||
stopPostgres()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func startPostgres(ctx context.Context) error {
|
||||
ctr, err := postgres.Run(ctx,
|
||||
"postgres:16-alpine",
|
||||
postgres.WithDatabase("golem15_sitemap"),
|
||||
postgres.WithUsername("golem15_sitemap"),
|
||||
postgres.WithPassword("golem15_sitemap"),
|
||||
postgres.BasicWaitStrategies(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pgContainer = 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
|
||||
}
|
||||
pgAdmin, pgDSN = db, dsn
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopPostgres() {
|
||||
if pgAdmin != nil {
|
||||
_ = pgAdmin.Close()
|
||||
}
|
||||
if pgContainer != nil {
|
||||
_ = testcontainers.TerminateContainer(pgContainer)
|
||||
}
|
||||
}
|
||||
|
||||
// freshDB creates a database of its own for one test and drops it after.
|
||||
func freshDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
if testing.Short() {
|
||||
t.Skip("requires testcontainers postgres")
|
||||
}
|
||||
if pgErr != nil {
|
||||
t.Fatalf("postgres unavailable: %v", pgErr)
|
||||
}
|
||||
if pgAdmin == nil {
|
||||
t.Fatal("postgres unavailable: container was not started")
|
||||
}
|
||||
name := fmt.Sprintf("sitemap_%d", time.Now().UnixNano())
|
||||
quoted := `"` + strings.ReplaceAll(name, `"`, `""`) + `"`
|
||||
if _, err := pgAdmin.ExecContext(t.Context(), `CREATE DATABASE `+quoted+` TEMPLATE template0 ENCODING 'UTF8'`); err != nil {
|
||||
t.Fatalf("create %s: %v", name, err)
|
||||
}
|
||||
u, err := url.Parse(pgDSN)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u.Path = "/" + name
|
||||
db, err := sql.Open("pgx", u.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = db.Close()
|
||||
_, _ = pgAdmin.ExecContext(context.Background(), `DROP DATABASE IF EXISTS `+quoted+` WITH (FORCE)`)
|
||||
})
|
||||
if err := db.PingContext(t.Context()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
Reference in New Issue
Block a user