feat(database): add repairSmokeAIAdviceIndexes function and corresponding tests

- Implemented repairSmokeAIAdviceIndexes to manage the unique index for fa_smoke_ai_advice.
- Added unit tests for the new function to ensure correct index recreation and validation.
- Updated AutoMigrate to include the new index repair function.
This commit is contained in:
你çšnepiedg
2026-03-16 15:35:32 +08:00
parent 6b5ce40140
commit 12619aa4ab
8 changed files with 253 additions and 12 deletions
+3
View File
@@ -102,6 +102,9 @@ func AutoMigrate(models ...interface{}) error {
if err := repairSmokeAINextSmokeIndexes(DB); err != nil {
return err
}
if err := repairSmokeAIAdviceIndexes(DB); err != nil {
return err
}
return nil
}
@@ -56,3 +56,47 @@ func repairSmokeAINextSmokeIndexes(db *gorm.DB) error {
return nil
}
func repairSmokeAIAdviceIndexes(db *gorm.DB) error {
if db == nil {
return nil
}
const (
tableName = "fa_smoke_ai_advice"
indexName = "uniq_smoke_ai_advice"
)
var rows []mysqlIndexColumn
if err := db.Raw(fmt.Sprintf("SHOW INDEX FROM `%s` WHERE Key_name = ?", tableName), indexName).Scan(&rows).Error; err != nil {
return fmt.Errorf("inspect %s: %w", indexName, err)
}
expected := []string{"uid", "type", "advice_date", "prompt_version"}
actual := make([]string, 0, len(rows))
for _, row := range rows {
actual = append(actual, row.ColumnName)
}
if slices.Equal(actual, expected) {
return nil
}
if len(rows) > 0 {
if err := db.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP INDEX `%s`", tableName, indexName)).Error; err != nil {
return fmt.Errorf("drop %s: %w", indexName, err)
}
}
if err := db.Exec(
fmt.Sprintf(
"ALTER TABLE `%s` ADD UNIQUE KEY `%s` (`uid`,`type`,`advice_date`,`prompt_version`)",
tableName,
indexName,
),
).Error; err != nil {
return fmt.Errorf("create %s: %w", indexName, err)
}
return nil
}
@@ -79,3 +79,54 @@ func TestRepairSmokeAINextSmokeIndexesKeepsCorrectIndex(t *testing.T) {
t.Fatalf("unmet expectations: %v", err)
}
}
func TestRepairSmokeAIAdviceIndexesRecreatesBrokenIndex(t *testing.T) {
t.Parallel()
db, mock, cleanup := newMockDB(t)
defer cleanup()
mock.ExpectQuery("SHOW INDEX FROM `fa_smoke_ai_advice` WHERE Key_name = \\?").
WithArgs("uniq_smoke_ai_advice").
WillReturnRows(
sqlmock.NewRows([]string{"Key_name", "Seq_in_index", "Column_name"}).
AddRow("uniq_smoke_ai_advice", 1, "uid").
AddRow("uniq_smoke_ai_advice", 2, "advice_date").
AddRow("uniq_smoke_ai_advice", 3, "prompt_version"),
)
mock.ExpectExec("ALTER TABLE `fa_smoke_ai_advice` DROP INDEX `uniq_smoke_ai_advice`").
WillReturnResult(sqlmock.NewResult(0, 0))
mock.ExpectExec("ALTER TABLE `fa_smoke_ai_advice` ADD UNIQUE KEY `uniq_smoke_ai_advice` \\(`uid`,`type`,`advice_date`,`prompt_version`\\)").
WillReturnResult(sqlmock.NewResult(0, 0))
if err := repairSmokeAIAdviceIndexes(db); err != nil {
t.Fatalf("repairSmokeAIAdviceIndexes: %v", err)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("unmet expectations: %v", err)
}
}
func TestRepairSmokeAIAdviceIndexesKeepsCorrectIndex(t *testing.T) {
t.Parallel()
db, mock, cleanup := newMockDB(t)
defer cleanup()
mock.ExpectQuery("SHOW INDEX FROM `fa_smoke_ai_advice` WHERE Key_name = \\?").
WithArgs("uniq_smoke_ai_advice").
WillReturnRows(
sqlmock.NewRows([]string{"Key_name", "Seq_in_index", "Column_name"}).
AddRow("uniq_smoke_ai_advice", 1, "uid").
AddRow("uniq_smoke_ai_advice", 2, "type").
AddRow("uniq_smoke_ai_advice", 3, "advice_date").
AddRow("uniq_smoke_ai_advice", 4, "prompt_version"),
)
if err := repairSmokeAIAdviceIndexes(db); err != nil {
t.Fatalf("repairSmokeAIAdviceIndexes: %v", err)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("unmet expectations: %v", err)
}
}