The one-way migration 20261108140000_move_zjazd_tags_to_meetings gives every material tagged zjazd-N without a meeting meeting N (the lowest when it has several), never overwrites a meeting_id, logs tags naming a missing meeting or disagreeing with meeting_id at Warn, and deletes the exact zjazd-N tags; their links go through ON DELETE CASCADE. Rollback is a no-op: the backup is the pg_dump taken before migrate.
122 lines
4.2 KiB
Go
122 lines
4.2 KiB
Go
package bm_test
|
|
|
|
import (
|
|
"database/sql"
|
|
"testing"
|
|
|
|
"git.golem15.com/jakub/sm-bm-plugin/updates"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// TestMoveZjazdTagsAgainstDatabase pins the data behaviour of the one-way
|
|
// zjazd migration (D-12): a material with no meeting gets the meeting its
|
|
// zjazd-N tag names (the lowest when it has several), an existing meeting
|
|
// is kept, a tag naming a missing meeting assigns nothing, only exact
|
|
// zjazd-N slugs are deleted and their links go with them, and a second run
|
|
// changes nothing.
|
|
func TestMoveZjazdTagsAgainstDatabase(t *testing.T) {
|
|
_, _, gdb := migrated(t)
|
|
|
|
insertID := func(query string, args ...any) int64 {
|
|
t.Helper()
|
|
var id int64
|
|
if err := gdb.Raw(query+" RETURNING id", args...).Scan(&id).Error; err != nil {
|
|
t.Fatalf("%s: %v", query, err)
|
|
}
|
|
return id
|
|
}
|
|
meeting := map[int]int64{}
|
|
for _, n := range []int{2, 3, 4} {
|
|
meeting[n] = insertID(`INSERT INTO jz_bm_meetings (number, starts_on, ends_on) VALUES (?, '2026-10-01', '2026-10-02')`, n)
|
|
}
|
|
tag := map[string]int64{}
|
|
for _, slug := range []string{"zjazd-3", "zjazd-4", "zjazd-5", "zjazd-3-24-10", "globalizacja"} {
|
|
tag[slug] = insertID(`INSERT INTO jz_bm_tags (name, slug) VALUES (?, ?)`, slug, slug)
|
|
}
|
|
material := func(title string, meetingID *int64, slugs ...string) int64 {
|
|
t.Helper()
|
|
id := insertID(`INSERT INTO jz_bm_materials (title, type, meeting_id) VALUES (?, 'notatka', ?)`, title, meetingID)
|
|
for _, s := range slugs {
|
|
if err := gdb.Exec(`INSERT INTO jz_bm_material_tag (material_id, tag_id) VALUES (?, ?)`, id, tag[s]).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return id
|
|
}
|
|
four := meeting[4]
|
|
a := material("A", nil, "zjazd-3", "globalizacja")
|
|
b := material("B", &four, "zjazd-3")
|
|
c := material("C", nil, "zjazd-5")
|
|
d := material("D", nil, "zjazd-4", "zjazd-3")
|
|
e := material("E", nil, "zjazd-3-24-10")
|
|
|
|
run := func() {
|
|
t.Helper()
|
|
if err := gdb.Transaction(func(tx *gorm.DB) error {
|
|
return updates.MoveZjazdTagsToMeetings().Migrate(tx)
|
|
}); err != nil {
|
|
t.Fatalf("Migrate: %v", err)
|
|
}
|
|
}
|
|
meetingOf := func(id int64) sql.NullInt64 {
|
|
t.Helper()
|
|
var m sql.NullInt64
|
|
if err := gdb.Raw(`SELECT meeting_id FROM jz_bm_materials WHERE id = ?`, id).Scan(&m).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return m
|
|
}
|
|
check := func(round string) {
|
|
t.Helper()
|
|
for _, w := range []struct {
|
|
name string
|
|
id int64
|
|
want sql.NullInt64
|
|
}{
|
|
{"A (zjazd-3, no meeting)", a, sql.NullInt64{Int64: meeting[3], Valid: true}},
|
|
{"B (meeting 4, tag zjazd-3)", b, sql.NullInt64{Int64: meeting[4], Valid: true}},
|
|
{"C (zjazd-5, no meeting 5)", c, sql.NullInt64{}},
|
|
{"D (zjazd-4 and zjazd-3)", d, sql.NullInt64{Int64: meeting[3], Valid: true}},
|
|
{"E (zjazd-3-24-10, a topic)", e, sql.NullInt64{}},
|
|
} {
|
|
if got := meetingOf(w.id); got != w.want {
|
|
t.Errorf("%s: material %s meeting_id = %+v, want %+v", round, w.name, got, w.want)
|
|
}
|
|
}
|
|
var zjazd int64
|
|
if err := gdb.Raw(`SELECT COUNT(*) FROM jz_bm_tags WHERE slug ~ '^zjazd-[0-9]+$'`).Scan(&zjazd).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if zjazd != 0 {
|
|
t.Errorf("%s: %d zjazd-N tags remain", round, zjazd)
|
|
}
|
|
var kept []string
|
|
if err := gdb.Raw(`SELECT slug FROM jz_bm_tags ORDER BY slug`).Scan(&kept).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(kept) != 2 || kept[0] != "globalizacja" || kept[1] != "zjazd-3-24-10" {
|
|
t.Errorf("%s: tags left %v, want [globalizacja zjazd-3-24-10]", round, kept)
|
|
}
|
|
var links int64
|
|
if err := gdb.Raw(`SELECT COUNT(*) FROM jz_bm_material_tag WHERE (material_id = ? AND tag_id = ?) OR (material_id = ? AND tag_id = ?)`,
|
|
a, tag["globalizacja"], e, tag["zjazd-3-24-10"]).Scan(&links).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if links != 2 {
|
|
t.Errorf("%s: topic links left %d, want A-globalizacja and E-zjazd-3-24-10", round, links)
|
|
}
|
|
var orphans int64
|
|
if err := gdb.Raw(`SELECT COUNT(*) FROM jz_bm_material_tag mt LEFT JOIN jz_bm_tags t ON t.id = mt.tag_id WHERE t.id IS NULL`).Scan(&orphans).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if orphans != 0 || count(t, gdb, "jz_bm_material_tag") != 2 {
|
|
t.Errorf("%s: %d pivot rows point at deleted tags, %d rows in all", round, orphans, count(t, gdb, "jz_bm_material_tag"))
|
|
}
|
|
}
|
|
|
|
run()
|
|
check("first run")
|
|
run()
|
|
check("second run")
|
|
}
|