Files
OpenFlare/internal/db/migrator/pages_source_scan_migration_test.go
T
ryan f0b51a99b3 fix(db): 重编号 Pages 部署源迁移避免版本冲突
main 已占用 202607190001(OpenResty 默认限流),
将 Pages source runtime / scanner seed 顺延为
202607190002、202607190003,并同步迁移测试期望配置数。
2026-07-19 19:41:30 +08:00

161 lines
5.2 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package migrator
import (
"database/sql"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/glebarez/sqlite"
"github.com/pressly/goose/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
const (
pagesSourceScanPreviousMigration = int64(202607190002)
pagesSourceScanMigration = int64(202607190003)
pagesSourceScanTaskType = "of_pages_source_scan"
)
func TestPagesSourceScanScheduleMigrationSQLite(t *testing.T) {
dbPath := t.TempDir() + "/pages-source-scan-migration.db"
gormDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
sqlDB.SetMaxOpenConns(1)
t.Cleanup(func() { require.NoError(t, sqlDB.Close()) })
runPagesSourceScanScheduleMigration(t, gormDB, sqlDB, dialectSqlite, "goose/sqlite")
}
func TestPagesSourceScanScheduleMigrationPostgres(t *testing.T) {
dsn := strings.TrimSpace(os.Getenv("OPENFLARE_TEST_POSTGRES_DSN"))
if dsn == "" {
t.Skip("OPENFLARE_TEST_POSTGRES_DSN is not set")
}
gormDB, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
sqlDB.SetMaxOpenConns(1)
schema := fmt.Sprintf("pages_source_scan_migration_%d", time.Now().UnixNano())
require.Regexp(t, `^[a-z0-9_]+$`, schema)
require.NoError(t, gormDB.Exec(`CREATE SCHEMA "`+schema+`"`).Error)
require.NoError(t, gormDB.Exec(`SET search_path TO "`+schema+`"`).Error)
t.Cleanup(func() {
assert.NoError(t, gormDB.Exec("SET search_path TO public").Error)
assert.NoError(t, gormDB.Exec(`DROP SCHEMA IF EXISTS "`+schema+`" CASCADE`).Error)
assert.NoError(t, sqlDB.Close())
})
runPagesSourceScanScheduleMigration(t, gormDB, sqlDB, dialectPostgres, "goose/postgres")
}
func runPagesSourceScanScheduleMigration(
t *testing.T,
gormDB *gorm.DB,
sqlDB *sql.DB,
dialect string,
dir string,
) {
t.Helper()
goose.SetBaseFS(migrationFS)
require.NoError(t, goose.SetDialect(dialect))
require.NoError(t, goose.UpTo(sqlDB, dir, pagesSourceScanPreviousMigration))
var previousMaxID uint64
require.NoError(t, gormDB.Table("w_schedules").Select("COALESCE(MAX(id), 0)").Scan(&previousMaxID).Error)
require.NoError(t, goose.UpTo(sqlDB, dir, pagesSourceScanMigration))
seeded := assertPagesSourceScanSchedule(t, gormDB)
assert.NotZero(t, seeded.ID)
if dialect == dialectPostgres {
assert.Greater(t, seeded.ID, previousMaxID)
}
require.NoError(t, goose.DownTo(sqlDB, dir, pagesSourceScanPreviousMigration))
assertPagesSourceScanScheduleMissing(t, gormDB)
custom := model.Schedule{
ID: 900001,
Name: "用户保留的 Pages 扫描任务",
TaskType: pagesSourceScanTaskType,
Cron: "0 * * * *",
Payload: `{"custom":true}`,
IsActive: false,
}
require.NoError(t, gormDB.Create(&custom).Error)
require.NoError(t, goose.UpTo(sqlDB, dir, pagesSourceScanMigration))
var schedules []model.Schedule
require.NoError(t, gormDB.Where("task_type = ?", pagesSourceScanTaskType).Find(&schedules).Error)
require.Len(t, schedules, 1)
assert.Equal(t, custom.ID, schedules[0].ID)
assert.Equal(t, custom.Name, schedules[0].Name)
require.NoError(t, goose.DownTo(sqlDB, dir, pagesSourceScanPreviousMigration))
var retained model.Schedule
require.NoError(t, gormDB.First(&retained, custom.ID).Error)
assert.Equal(t, custom.TaskType, retained.TaskType)
}
func TestPagesSourceScanScheduleMigrationsUseDatabaseGeneratedIDs(t *testing.T) {
for _, name := range []string{
"goose/postgres/202607190003_seed_pages_source_scan.sql",
"goose/sqlite/202607190003_seed_pages_source_scan.sql",
} {
t.Run(name, func(t *testing.T) {
content, err := migrationFS.ReadFile(name)
require.NoError(t, err)
normalized := strings.ToLower(string(content))
assert.NotContains(t, normalized, "insert into w_schedules (id,")
assert.NotContains(t, normalized, "coalesce(max(id)")
})
}
postgresContent, err := migrationFS.ReadFile("goose/postgres/202607190003_seed_pages_source_scan.sql")
require.NoError(t, err)
compactPostgres := strings.Join(strings.Fields(strings.ToLower(string(postgresContent))), " ")
assert.Contains(
t,
compactPostgres,
"select setval( pg_get_serial_sequence('w_schedules', 'id'), greatest( 1,",
"sequence synchronization must retain a valid lower bound for an empty table",
)
}
func assertPagesSourceScanSchedule(t *testing.T, gormDB *gorm.DB) model.Schedule {
t.Helper()
var schedules []model.Schedule
require.NoError(t, gormDB.Where("task_type = ?", pagesSourceScanTaskType).Find(&schedules).Error)
require.Len(t, schedules, 1)
schedule := schedules[0]
assert.Equal(t, "OpenFlare Pages 部署源扫描", schedule.Name)
assert.Equal(t, "*/5 * * * *", schedule.Cron)
assert.Equal(t, "{}", schedule.Payload)
assert.True(t, schedule.IsActive)
return schedule
}
func assertPagesSourceScanScheduleMissing(t *testing.T, gormDB *gorm.DB) {
t.Helper()
var count int64
require.NoError(t, gormDB.Model(&model.Schedule{}).
Where("task_type = ?", pagesSourceScanTaskType).
Count(&count).Error)
assert.Zero(t, count)
}