package database import ( "errors" "strings" "testing" "gorm.io/gorm" ) func TestMigrateSchemaRecordsAndValidatesVersion(t *testing.T) { db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-version?mode=memory&cache=shared"}) if err != nil { t.Fatal(err) } if err := MigrateSchema(db); err != nil { t.Fatal(err) } status, err := ReadSchemaStatus(db) if err != nil { t.Fatal(err) } if !status.Ready || status.Current != CurrentSchemaVersion { t.Fatalf("unexpected schema status: %#v", status) } if !db.Migrator().HasIndex(&schemaMigration{}, "idx_schema_migrations_applied_at") { t.Fatal("schema migration v2 did not create the applied_at index") } if err := MigrateSchema(db); err != nil { t.Fatalf("migration should be idempotent: %v", err) } } func TestMigrateSchemaRejectsChecksumMismatch(t *testing.T) { db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-checksum?mode=memory&cache=shared"}) if err != nil { t.Fatal(err) } if err := MigrateSchema(db); err != nil { t.Fatal(err) } if err := db.Model(&schemaMigration{}).Where("version = ?", CurrentSchemaVersion).Update("checksum", "changed").Error; err != nil { t.Fatal(err) } if err := MigrateSchema(db); err == nil || !strings.Contains(err.Error(), "校验和不一致") { t.Fatalf("expected checksum mismatch, got %v", err) } if err := RequireSchemaVersion(db); err == nil || !strings.Contains(err.Error(), "校验和不一致") { t.Fatalf("schema verification must reject checksum mismatch, got %v", err) } } func TestMigrateSchemaRollsBackFailedMigration(t *testing.T) { db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-rollback?mode=memory&cache=shared"}) if err != nil { t.Fatal(err) } if err := MigrateSchema(db); err != nil { t.Fatal(err) } original := schemaMigrations schemaMigrations = append(append([]migration(nil), original...), migration{ version: CurrentSchemaVersion + 1, name: "rollback_probe", checksum: "sha256:rollback-probe", apply: func(tx *gorm.DB) error { if err := tx.Exec("CREATE TABLE migration_rollback_probe (id INTEGER PRIMARY KEY)").Error; err != nil { return err } return errors.New("forced migration failure") }, }) t.Cleanup(func() { schemaMigrations = original }) if err := MigrateSchema(db); err == nil || !strings.Contains(err.Error(), "forced migration failure") { t.Fatalf("expected forced migration failure, got %v", err) } if db.Migrator().HasTable("migration_rollback_probe") { t.Fatal("failed migration left a partial table behind") } var count int64 if err := db.Model(&schemaMigration{}).Where("version = ?", CurrentSchemaVersion+1).Count(&count).Error; err != nil { t.Fatal(err) } if count != 0 { t.Fatalf("failed migration was recorded: %d", count) } } func TestRequireSchemaVersionRejectsUninitializedDatabase(t *testing.T) { db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-uninitialized?mode=memory&cache=shared"}) if err != nil { t.Fatal(err) } if err := RequireSchemaVersion(db); err == nil || !strings.Contains(err.Error(), "请先执行 migrate-schema up") { t.Fatalf("expected missing migration error, got %v", err) } }