package store import ( "os" "path/filepath" "testing" "time" ) func TestFailingMigrationReusesBackup(t *testing.T) { t.Parallel() dir := t.TempDir() db, err := Open(dir, "FULL") if err != nil { t.Fatal(err) } _ = db.Close() w, err := OpenWriter(dir, "FULL") if err != nil { t.Fatal(err) } defer func() { _ = w.Close() }() pending := []migrationFile{{ version: 9999, name: "9999_fail.sql", body: "THIS IS NOT VALID SQL", }} if applyErr := applyPending(w, dir, true, pending); applyErr == nil { t.Fatal("expected first failing migration to error") } backupDir := filepath.Join(dir, "backup") first, err := os.ReadDir(backupDir) if err != nil { t.Fatal(err) } if len(first) != 1 || first[0].Name() != "pre-migrate-v4-to-v9999.db" { t.Fatalf("first backups=%v", dirNames(first)) } if applyErr := applyPending(w, dir, true, pending); applyErr == nil { t.Fatal("expected second failing migration to error") } second, err := os.ReadDir(backupDir) if err != nil { t.Fatal(err) } if len(second) != 1 || second[0].Name() != first[0].Name() { t.Fatalf("second backups=%v first=%v", dirNames(second), dirNames(first)) } info1, err := first[0].Info() if err != nil { t.Fatal(err) } info2, err := second[0].Info() if err != nil { t.Fatal(err) } if info2.ModTime().After(info1.ModTime().Add(time.Second)) && info2.Size() != info1.Size() { // 复用同一文件即可;时钟精度下允许 mtime 相同。 t.Logf("mtime first=%s second=%s", info1.ModTime(), info2.ModTime()) } } func dirNames(entries []os.DirEntry) []string { out := make([]string, 0, len(entries)) for _, e := range entries { out = append(out, e.Name()) } return out }