refactor(10.2-01): nest framework packages under modules
- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
This commit is contained in:
277
modules/lagoon/migrations_test.go
Normal file
277
modules/lagoon/migrations_test.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package lagoon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/bonfire"
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
"git.golem15.com/golem15/summercms/modules/party"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestHistoryTableName(t *testing.T) {
|
||||
got, err := HistoryTableName("golem15.user")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != "summer_migrations_golem15_user" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
got, err = HistoryTableName("golem15.fonoteka")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != "summer_migrations_golem15_fonoteka" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
if _, err := HistoryTableName("golem15.user;drop"); err == nil {
|
||||
t.Fatal("want invalid plugin id error")
|
||||
}
|
||||
if _, err := HistoryTableName(""); err == nil {
|
||||
t.Fatal("want empty plugin id error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLocaleMessage(t *testing.T) {
|
||||
if err := checkLocale("i", "pl-PL"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := checkLocale("c", "en_US")
|
||||
if err == nil {
|
||||
t.Fatal("want locale error")
|
||||
}
|
||||
msg := err.Error()
|
||||
for _, want := range []string{"ICU", "pl-PL", "CREATE DATABASE", "LOCALE_PROVIDER icu", "en_US"} {
|
||||
if !strings.Contains(msg, want) {
|
||||
t.Fatalf("missing %q in %s", want, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeCommandsRegisterBareAndColonNames(t *testing.T) {
|
||||
cmds := RuntimeCommands(nil, nil)
|
||||
names := map[string]bool{}
|
||||
for _, c := range cmds {
|
||||
names[c.Name] = true
|
||||
}
|
||||
for _, want := range []string{"migrate", "migrate:rollback", "migrate:status", "key:generate"} {
|
||||
if !names[want] {
|
||||
t.Fatalf("missing %s", want)
|
||||
}
|
||||
}
|
||||
root, err := bonfire.NewRoot("app", cmds, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root.SetArgs([]string{"migrate", "--help"})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoAutoMigrate(t *testing.T) {
|
||||
entries, err := os.ReadDir(".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") {
|
||||
continue
|
||||
}
|
||||
body, err := os.ReadFile(entry.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Contains(body, []byte("AutoMigrate")) {
|
||||
t.Fatalf("%s must not call AutoMigrate", entry.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenRequiresDSN(t *testing.T) {
|
||||
_, _, err := Open(t.Context(), " ")
|
||||
if err == nil || !strings.Contains(err.Error(), "database.dsn") {
|
||||
t.Fatalf("got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type stubPlugin struct{ id string }
|
||||
|
||||
func (s stubPlugin) ID() string { return s.id }
|
||||
func (s stubPlugin) Requires() []string { return nil }
|
||||
func (s stubPlugin) Register(*backpack.App) error { return nil }
|
||||
func (s stubPlugin) Boot(*backpack.App) error { return nil }
|
||||
|
||||
func TestRollbackUnknownPluginIsNamedError(t *testing.T) {
|
||||
err := RollbackLast(&gorm.DB{}, []party.Plugin{stubPlugin{id: "golem15.user"}}, "golem15.missing")
|
||||
if !errors.Is(err, ErrUnknownPlugin) {
|
||||
t.Fatalf("got %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "golem15.missing") {
|
||||
t.Fatalf("error should name plugin: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollbackMissingPluginIsNamedError(t *testing.T) {
|
||||
err := RollbackLast(&gorm.DB{}, nil, "")
|
||||
if !errors.Is(err, ErrNoMigrations) {
|
||||
t.Fatalf("got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type migPlugin struct {
|
||||
id string
|
||||
migrations []*gormigrate.Migration
|
||||
}
|
||||
|
||||
func (p migPlugin) ID() string { return p.id }
|
||||
func (p migPlugin) Requires() []string { return nil }
|
||||
func (p migPlugin) Register(*backpack.App) error { return nil }
|
||||
func (p migPlugin) Boot(*backpack.App) error { return nil }
|
||||
func (p migPlugin) Migrations() []*gormigrate.Migration { return p.migrations }
|
||||
|
||||
func TestMigrateRunsSystemFilesFirst(t *testing.T) {
|
||||
db, _ := dedicatedDB(t, "lagoon_attach_first")
|
||||
gdb, err := Use(t.Context(), db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := Migrate(gdb, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !gdb.Migrator().HasTable("system_files") {
|
||||
t.Fatal("system_files must exist after Migrate with an empty plugin list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwoPluginMigrationSetsIsolated(t *testing.T) {
|
||||
db, _ := dedicatedDB(t, "lagoon_mig_iso")
|
||||
gdb, err := Use(t.Context(), db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alpha := migPlugin{
|
||||
id: "demo.alpha",
|
||||
migrations: []*gormigrate.Migration{{
|
||||
ID: "202609170001_create_alpha",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`CREATE TABLE lagoon_alpha (id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL)`).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`DROP TABLE IF EXISTS lagoon_alpha`).Error
|
||||
},
|
||||
}},
|
||||
}
|
||||
beta := migPlugin{
|
||||
id: "demo.beta",
|
||||
migrations: []*gormigrate.Migration{
|
||||
{
|
||||
ID: "202609170001_create_beta",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`CREATE TABLE lagoon_beta (id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL)`).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`DROP TABLE IF EXISTS lagoon_beta`).Error
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "202609170002_seed_beta",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`INSERT INTO lagoon_beta (name) VALUES ('seed')`).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`DELETE FROM lagoon_beta WHERE name = 'seed'`).Error
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
plugins := []party.Plugin{alpha, beta}
|
||||
|
||||
if err := Migrate(gdb, plugins); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := Migrate(gdb, plugins); err != nil {
|
||||
t.Fatalf("idempotent migrate: %v", err)
|
||||
}
|
||||
|
||||
status, err := Status(gdb, plugins)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(status) != 2 {
|
||||
t.Fatalf("status rows = %+v", status)
|
||||
}
|
||||
if status[0].Plugin != "demo.alpha" || status[0].Table != "summer_migrations_demo_alpha" || strings.Join(status[0].IDs, ",") != "202609170001_create_alpha" {
|
||||
t.Fatalf("alpha status = %+v", status[0])
|
||||
}
|
||||
if status[1].Plugin != "demo.beta" || status[1].Table != "summer_migrations_demo_beta" || strings.Join(status[1].IDs, ",") != "202609170001_create_beta,202609170002_seed_beta" {
|
||||
t.Fatalf("beta status = %+v", status[1])
|
||||
}
|
||||
|
||||
if !gdb.Migrator().HasTable("lagoon_alpha") || !gdb.Migrator().HasTable("lagoon_beta") {
|
||||
t.Fatal("both plugin tables must exist after migrate")
|
||||
}
|
||||
var seedName string
|
||||
if err := gdb.Raw(`SELECT name FROM lagoon_beta`).Scan(&seedName).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if seedName != "seed" {
|
||||
t.Fatalf("seed = %q", seedName)
|
||||
}
|
||||
|
||||
if err := RollbackLast(gdb, plugins, "demo.beta"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var n int64
|
||||
if err := gdb.Table("lagoon_beta").Count(&n).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatalf("beta seed rollback left %d rows", n)
|
||||
}
|
||||
if !gdb.Migrator().HasTable("lagoon_alpha") || !gdb.Migrator().HasTable("lagoon_beta") {
|
||||
t.Fatal("schema rollback must not run on the first beta RollbackLast")
|
||||
}
|
||||
|
||||
status, err = Status(gdb, plugins)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Join(status[0].IDs, ",") != "202609170001_create_alpha" {
|
||||
t.Fatalf("alpha history changed: %+v", status[0])
|
||||
}
|
||||
if strings.Join(status[1].IDs, ",") != "202609170001_create_beta" {
|
||||
t.Fatalf("beta history = %+v", status[1])
|
||||
}
|
||||
|
||||
if err := RollbackLast(gdb, plugins, "demo.beta"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gdb.Migrator().HasTable("lagoon_beta") {
|
||||
t.Fatal("second beta rollback must drop lagoon_beta")
|
||||
}
|
||||
if !gdb.Migrator().HasTable("lagoon_alpha") {
|
||||
t.Fatal("alpha table must survive beta rollback")
|
||||
}
|
||||
|
||||
status, err = Status(gdb, plugins)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(status[0].IDs) != 1 {
|
||||
t.Fatalf("alpha history after beta down = %+v", status[0])
|
||||
}
|
||||
if len(status[1].IDs) != 0 {
|
||||
t.Fatalf("beta history should be empty, got %+v", status[1])
|
||||
}
|
||||
}
|
||||
|
||||
var _ pact.HasMigrations = migPlugin{}
|
||||
Reference in New Issue
Block a user