package provision import ( "errors" "testing" "time" ) func TestMoveRollbackLossRebind(t *testing.T) { g := MoveGuard{ID: "mv_abcdefghijklmnopqrstuvwxyz", Incarnation: "inc"} now := time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC) lost := func(phase, ref string, dirty bool) *Record { at := now.Add(-time.Minute) return &Record{Move: &MoveState{ID: g.ID, Incarnation: g.Incarnation, State: MoveRollingBack, Rollback: &MoveRollback{ID: "rb_bbbbbbbbbbbbbbbbbbbbbbbbbb", TargetPhase: phase, Dirty: dirty, LossRef: ref, LostAt: &at, SourcePhase: RbSourceWaiting}}} } rec := lost(RbTargetLost, "old", true) if err := MoveRollbackLossRebind(g, "old", "new", now)(rec); err != nil { t.Fatal(err) } rb := rec.Move.Rollback if rb.LossRef != "new" || !rb.LostAt.Equal(now) || rb.TargetPhase != RbTargetLost || !rb.Dirty || rb.ID != "rb_bbbbbbbbbbbbbbbbbbbbbbbbbb" { t.Fatalf("rollback = %+v", rb) } // A replay changes nothing. if err := MoveRollbackLossRebind(g, "old", "new", now.Add(time.Hour))(rec); err != nil || !rb.LostAt.Equal(now) { t.Fatalf("replay = %v %v", err, rb.LostAt) } for name, tc := range map[string]struct { rec *Record g MoveGuard oldRef, newRef string }{ "other old ref": {lost(RbTargetLost, "other", false), g, "old", "new"}, "not lost": {lost(RbTargetReleasing, "", false), g, "old", "new"}, "released": {lost(RbTargetReleased, "old", false), g, "old", "new"}, "same ref": {lost(RbTargetLost, "old", false), g, "old", "old"}, "empty old": {lost(RbTargetLost, "", false), g, "", "new"}, "other move": {lost(RbTargetLost, "old", false), MoveGuard{ID: "mv_cccccccccccccccccccccccccc", Incarnation: "inc"}, "old", "new"}, "other incarnate": {lost(RbTargetLost, "old", false), MoveGuard{ID: g.ID, Incarnation: "other"}, "old", "new"}, } { before := *tc.rec.Move.Rollback err := MoveRollbackLossRebind(tc.g, tc.oldRef, tc.newRef, now)(tc.rec) if err == nil { t.Errorf("%s accepted", name) } if name != "same ref" && name != "empty old" && !errors.Is(err, ErrMoveConflict) { t.Errorf("%s = %v", name, err) } if after := *tc.rec.Move.Rollback; after.LossRef != before.LossRef || after.TargetPhase != before.TargetPhase { t.Errorf("%s changed the rollback", name) } } }