package moves import ( "testing" "time" "github.com/carlosframework/platform/internal/provision" ) // The auto-abort bound (spec ยง7.11, ruling K18): a Phase 1 move still // preparing AbortAfter after max(RequestedAt, ReadyAt) is due; one that is // not yet there, or is in any other state, or is a legacy move, is not. func TestAbortDue(t *testing.T) { if AbortAfter != 6*time.Hour { t.Fatalf("AbortAfter = %v, want 6h", AbortAfter) } req := time.Date(2026, 10, 9, 8, 0, 0, 0, time.UTC) rec := func(state string, ready *time.Time) provision.Record { return provision.Record{Host: "a.go.example.com", Move: &provision.MoveState{ID: "mv_abcdefghijklmnopqrstuvwxyz", State: state, RequestedAt: req, ReadyAt: ready}} } at := func(d time.Duration) time.Time { return req.Add(d) } later := req.Add(2 * time.Hour) for name, c := range map[string]struct { rec provision.Record now time.Time want bool }{ "preparing, just short": {rec(provision.MovePreparing, nil), at(AbortAfter - time.Second), false}, "preparing, at the bound": {rec(provision.MovePreparing, nil), at(AbortAfter), true}, "preparing, well past": {rec(provision.MovePreparing, nil), at(3 * AbortAfter), true}, "the bound is from the later time": {rec(provision.MovePreparing, &later), at(AbortAfter + time.Hour), false}, "and reached from it": {rec(provision.MovePreparing, &later), later.Add(AbortAfter), true}, "a non-UTC clock": {rec(provision.MovePreparing, nil), at(AbortAfter).In(time.FixedZone("x", -7*3600)), true}, "ready": {rec(provision.MoveReady, &later), at(10 * AbortAfter), false}, "cutting": {rec(provision.MoveCutting, &later), at(10 * AbortAfter), false}, "aborting": {rec(provision.MoveAborting, nil), at(10 * AbortAfter), false}, "landing": {rec(provision.MoveLanding, nil), at(10 * AbortAfter), false}, "legacy": {provision.Record{Move: &provision.MoveState{State: provision.MovePreparing, RequestedAt: req}}, at(10 * AbortAfter), false}, "no move": {provision.Record{}, at(10 * AbortAfter), false}, "no request time": {provision.Record{Move: &provision.MoveState{ID: "mv_abcdefghijklmnopqrstuvwxyz", State: provision.MovePreparing}}, at(10 * AbortAfter), false}, } { if got := AbortDue(c.rec, c.now); got != c.want { t.Errorf("%s: AbortDue = %v, want %v", name, got, c.want) } } }