package db import ( "context" "database/sql" "testing" ) // columnExists reports whether table has a column named column. func columnExists(t *testing.T, ctx context.Context, conn *sql.DB, table, column string) bool { t.Helper() var n int if err := conn.QueryRowContext(ctx, "SELECT count(*) FROM pragma_table_info(?) WHERE name = ?", table, column).Scan(&n); err != nil { t.Fatalf("pragma_table_info(%s): %v", table, err) } return n == 1 } // TestMigrate0009PinnedColumnIdempotent verifies the pinned migration (Android // collaboration batch ③a / D5) is reversible and re-appliable: up adds the column, // a second up is a no-op, down drops it, and a re-up restores it. func TestMigrate0009PinnedColumnIdempotent(t *testing.T) { t.Parallel() ctx := context.Background() conn := openMemory(t) if err := MigrateUp(ctx, conn, testLogger()); err != nil { t.Fatalf("MigrateUp: %v", err) } if !columnExists(t, ctx, conn, "chat_sessions", "pinned") { t.Fatal("chat_sessions.pinned should exist after MigrateUp") } // Up twice: the recorded migration is skipped, so the column stays and no error. if err := MigrateUp(ctx, conn, testLogger()); err != nil { t.Fatalf("MigrateUp second call: %v", err) } if !columnExists(t, ctx, conn, "chat_sessions", "pinned") { t.Fatal("chat_sessions.pinned should still exist after idempotent re-run") } // Roll back every migration newer than 0008 (0009 itself plus any migration added // after it, currently 0010), so this test keeps validating the 0009 down no matter // how many later migrations the repo has grown. steps := len(upMigrationIDs(t)) - 8 if err := MigrateDown(ctx, conn, steps, testLogger()); err != nil { t.Fatalf("MigrateDown(%d): %v", steps, err) } if columnExists(t, ctx, conn, "chat_sessions", "pinned") { t.Fatal("chat_sessions.pinned should be dropped by the 0009 down") } // Re-applying restores it end to end. if err := MigrateUp(ctx, conn, testLogger()); err != nil { t.Fatalf("re-MigrateUp: %v", err) } if !columnExists(t, ctx, conn, "chat_sessions", "pinned") { t.Fatal("chat_sessions.pinned should exist again after re-MigrateUp") } }