Files
sm-journal-plugin/models/post_translatable_smoke_test.go
Jakub Zych 4e2aab6fe0 test(17-06): read -short through testing.Short in the Postgres harnesses
go test passes -test.short=true, so scanning os.Args for -test.short
never matched and every -short run started a Postgres container. The
three TestMains now parse the flags and ask testing.Short.
2026-10-12 19:31:45 +02:00

254 lines
6.9 KiB
Go

package models_test
import (
"context"
"database/sql"
"encoding/json"
"flag"
"fmt"
"os"
"testing"
"time"
"git.golem15.com/golem15/sm-journal-plugin"
"git.golem15.com/golem15/sm-journal-plugin/models"
"git.golem15.com/golem15/sm-translate-plugin"
"git.golem15.com/golem15/sm-translate-plugin/classes"
translatemodels "git.golem15.com/golem15/sm-translate-plugin/models"
"git.golem15.com/golem15/summercms/modules/lagoon"
"github.com/go-gormigrate/gormigrate/v2"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"gorm.io/gorm"
)
var (
journalPG *postgres.PostgresContainer
journalSQL *sql.DB
journalPGErr error
)
func TestMain(m *testing.M) {
code := 1
if !testShort() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
journalPGErr = startJournalPostgres(ctx)
cancel()
if journalPGErr != nil {
fmt.Fprintf(os.Stderr, "golem15.journal: testcontainers postgres: %v\n", journalPGErr)
stopJournalPostgres()
os.Exit(1)
}
}
code = m.Run()
stopJournalPostgres()
os.Exit(code)
}
// testShort reports -short. go test passes -test.short=true, so the flags
// are parsed and read through testing.Short, never by scanning os.Args.
func testShort() bool {
flag.Parse()
return testing.Short()
}
func startJournalPostgres(ctx context.Context) error {
ctr, err := postgres.Run(ctx,
"postgres:16-alpine",
postgres.WithDatabase("golem15_journal"),
postgres.WithUsername("golem15_journal"),
postgres.WithPassword("golem15_journal"),
postgres.BasicWaitStrategies(),
)
if err != nil {
return err
}
journalPG = 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
}
journalSQL = db
return nil
}
func stopJournalPostgres() {
if journalSQL != nil {
_ = journalSQL.Close()
}
if journalPG != nil {
_ = testcontainers.TerminateContainer(journalPG)
}
}
func journalDB(t *testing.T) *gorm.DB {
t.Helper()
if testing.Short() {
t.Skip("requires testcontainers postgres")
}
if journalPGErr != nil {
t.Fatalf("postgres unavailable: %v", journalPGErr)
}
if journalSQL == nil {
t.Fatal("postgres unavailable: container was not started")
}
gdb, err := lagoon.Use(t.Context(), journalSQL)
if err != nil {
t.Fatal(err)
}
return gdb
}
func migrateTranslateThenJournal(t *testing.T, gdb *gorm.DB) {
t.Helper()
backend := gormigrate.New(gdb, &gormigrate.Options{
TableName: "summer_migrations_summercms_cabana",
UseTransaction: true,
}, lagoon.BackendAdminMigrations)
if err := backend.Migrate(); err != nil {
t.Fatalf("backend_users migrations: %v", err)
}
translateMig := gormigrate.New(gdb, &gormigrate.Options{
TableName: "summer_migrations_golem15_translate",
UseTransaction: true,
}, (&translate.Plugin{}).Migrations())
if err := translateMig.Migrate(); err != nil {
t.Fatalf("translate migrations: %v", err)
}
journalMig := gormigrate.New(gdb, &gormigrate.Options{
TableName: "summer_migrations_golem15_journal",
UseTransaction: true,
}, (&journal.Plugin{}).Migrations())
if err := journalMig.Migrate(); err != nil {
t.Fatalf("journal migrations: %v", err)
}
}
// TestJournalEndToEnd keeps `go test ./... -run TestJournalEndToEnd`
// from reporting [no tests to run] in this package. The production-path
// proof lives in package journal_test.
func TestJournalEndToEnd(t *testing.T) {}
func TestPostTranslatableSmoke(t *testing.T) {
gdb := journalDB(t)
migrateTranslateThenJournal(t, gdb)
if gdb.Migrator().HasTable("rainlab_journal_posts") {
t.Fatal("rainlab-era journal table must not be created")
}
if !gdb.Migrator().HasTable("golem15_journal_posts") {
t.Fatal("golem15_journal_posts was not created")
}
post := &models.Post{}
if post.MorphName() != `Golem15\Journal\Models\Post` {
t.Fatalf("MorphName = %q", post.MorphName())
}
indexes := post.TranslatableIndexes()
if len(indexes) != 1 || indexes[0] != "slug" {
t.Fatalf("TranslatableIndexes = %#v, want [slug]", indexes)
}
for _, name := range post.Fillable() {
if name == "redactor_id" || name == "content_html" || name == "user_id" {
t.Fatalf("Fillable includes forbidden %q", name)
}
}
ctx := t.Context()
post = &models.Post{
Title: "Hello",
Slug: "hello",
Excerpt: "English excerpt",
Content: "English content",
}
if err := gdb.WithContext(ctx).Create(post).Error; err != nil {
t.Fatal(err)
}
if post.ID == 0 {
t.Fatal("expected persisted post id")
}
if err := classes.SetTranslated(ctx, gdb, post, "title", "en", "Hello"); err != nil {
t.Fatalf("set en title: %v", err)
}
if err := classes.SetTranslated(ctx, gdb, post, "content", "en", "English content"); err != nil {
t.Fatalf("set en content: %v", err)
}
if err := classes.SetTranslated(ctx, gdb, post, "content_html", "en", "<p>English content</p>"); err != nil {
t.Fatalf("set en content_html: %v", err)
}
if err := classes.SetTranslated(ctx, gdb, post, "excerpt", "en", "English excerpt"); err != nil {
t.Fatalf("set en excerpt: %v", err)
}
if err := classes.SetTranslated(ctx, gdb, post, "title", "pl", "Witaj"); err != nil {
t.Fatalf("set pl title: %v", err)
}
if err := classes.SetTranslated(ctx, gdb, post, "slug", "pl", "witaj"); err != nil {
t.Fatalf("set pl slug: %v", err)
}
got, err := classes.Translated(ctx, gdb, post, "title", "pl")
if err != nil {
t.Fatalf("read pl title: %v", err)
}
if got != "Witaj" {
t.Fatalf("pl title = %#v, want Witaj", got)
}
var stored models.Post
if err := gdb.WithContext(ctx).First(&stored, post.ID).Error; err != nil {
t.Fatal(err)
}
if stored.Title != "Hello" {
t.Fatalf("host title = %q, want Hello", stored.Title)
}
if stored.Slug != "hello" {
t.Fatalf("host slug = %q, want hello", stored.Slug)
}
var attrs []translatemodels.Attribute
if err := gdb.WithContext(ctx).
Where("model_id = ? AND model_type = ?", fmt.Sprint(post.ID), post.MorphName()).
Find(&attrs).Error; err != nil {
t.Fatal(err)
}
if len(attrs) != 1 {
t.Fatalf("attribute rows = %d, want 1 (Polish only)", len(attrs))
}
if attrs[0].Locale != "pl" {
t.Fatalf("attribute locale = %q, want pl", attrs[0].Locale)
}
if attrs[0].AttributeData == nil {
t.Fatal("attribute_data is nil")
}
var data map[string]any
if err := json.Unmarshal([]byte(*attrs[0].AttributeData), &data); err != nil {
t.Fatal(err)
}
if data["title"] != "Witaj" {
t.Fatalf("attribute_data.title = %#v", data["title"])
}
if data["slug"] != "witaj" {
t.Fatalf("attribute_data.slug = %#v", data["slug"])
}
var enCount int64
if err := gdb.WithContext(ctx).Model(&translatemodels.Attribute{}).
Where("locale = ? AND model_id = ?", "en", fmt.Sprint(post.ID)).
Count(&enCount).Error; err != nil {
t.Fatal(err)
}
if enCount != 0 {
t.Fatalf("en attribute rows = %d, want 0 (English lives on the host row)", enCount)
}
}