carlos / platform Public

console: tests for fix round 1

The switch-off operator sentinel with pinned instances; the prepare before
the cutover is not enabled; freshness and the lifetime before the walk's
verdicts; the run before an unreadable journal; the unreadable journal's
502; a cancel of a returning move; TestOperatorStatusReadsNoIdentity (an
operator status poll of a staging self-serve move and of an operator move
reads nothing under control/boxes/ and not the roster).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev adca350192e16d17ee1027349c3fcfdaa8fe8f89 parent b920b68
1 file changed, +93 −4
  • internal/console/selfserve_door_test.go +93 −4
diff --git a/internal/console/selfserve_door_test.go b/internal/console/selfserve_door_test.go
index b4eb2f097..3bb14d047 100644
--- a/internal/console/selfserve_door_test.go
+++ b/internal/console/selfserve_door_test.go
@@ -14,6 +14,7 @@ import (
"github.com/carlosframework/platform/internal/boxid"
"github.com/carlosframework/platform/internal/boxid/boxidtest"
+ "github.com/carlosframework/platform/internal/hostactors"
"github.com/carlosframework/platform/internal/moves"
"github.com/carlosframework/platform/internal/provision"
"github.com/carlosframework/platform/internal/release"
@@ -213,7 +214,7 @@ func (w *ssWorld) refusedSS(body string, wantCode int, wantMsg string) {
w.t.Fatalf("a refusal changed %s", k)
}
}
- if open, _ := w.rows(); open != nil && wantMsg != "a move to uk is already preparing" {
+ if open, _, _ := w.fx.st.SelfServeMoveForHost("bab", "shop", offlineHost); open != nil && wantMsg != "a move to uk is already preparing" {
w.t.Fatalf("a refusal admitted a row: %+v", open)
}
}
@@ -426,6 +427,20 @@ func TestParticipantPinning(t *testing.T) {
t.Fatalf("an operator door read identity records: %v", r)
}
})
+ t.Run("operator, the switch off: the instances pinned, the sentinel reserved", func(t *testing.T) {
+ w := newSelfServeWorld(t, false)
+ w.fx.srv.cfg.SelfServeMoves = ""
+ sel, ref, _ := pin(t, w, false)
+ if ref != nil {
+ t.Fatal(ref.msg)
+ }
+ if sel.Source[0].InstanceID != "i-ie-box" || sel.Target.InstanceID != "i-uk-box" || !sel.Sentinel {
+ t.Fatalf("source %+v target %+v sentinel %v", sel.Source[0], sel.Target, sel.Sentinel)
+ }
+ if got := sel.reservationKeys(); len(got) != 1 || got[0] != moves.OfflineSentinel {
+ t.Fatalf("an operator selection with the switch off reserves %v", got)
+ }
+ })
for _, c := range []struct {
name string
edit func(b *status.Box)
@@ -619,6 +634,27 @@ func TestPrepareRefusesBeforeWriting(t *testing.T) {
{name: "a replica listing from the future", setup: func(w *ssWorld) {
w.facts(func(f *status.HostFacts) { f.ReplicaCollectedAt = time.Now().Add(2 * time.Minute).Unix() })
}, body: ssBody, code: 409, msg: "the source box has not reported recently. try again in a minute"},
+ // Freshness and the lifetime come before the walk's own verdicts.
+ {name: "over the cap, collected long ago", setup: func(w *ssWorld) {
+ w.facts(func(f *status.HostFacts) { f.OverCap, f.CollectedAt = true, time.Now().Add(-11*time.Minute).Unix() })
+ }, body: ssBody, code: 409, msg: "the source box has not reported recently. try again in a minute"},
+ {name: "not known, of another lifetime", setup: func(w *ssWorld) {
+ w.facts(func(f *status.HostFacts) { f.Known = false; f.Incarnation++ })
+ }, body: ssBody, code: 409, msg: "the source box has not reported recently. try again in a minute"},
+ // The record's checks come before the journal's read.
+ {name: "an open run with the rows unreadable", setup: func(w *ssWorld) {
+ rawEdit(w, func(m map[string]any) {
+ m["run"] = map[string]any{"id": "run-1", "state": provision.RunQueued, "deadline": time.Now().Add(time.Hour)}
+ })
+ if _, err := w.fx.st.db.Exec(`ALTER TABLE self_serve_moves RENAME TO self_serve_moves_gone`); err != nil {
+ w.t.Fatal(err)
+ }
+ }, body: ssBody, code: 409, msg: (&provision.MoveRunOpenError{Run: provision.RunState{ID: "run-1"}}).Sentence()},
+ {name: "the rows unreadable", setup: func(w *ssWorld) {
+ if _, err := w.fx.st.db.Exec(`ALTER TABLE self_serve_moves RENAME TO self_serve_moves_gone`); err != nil {
+ w.t.Fatal(err)
+ }
+ }, body: ssBody, code: 502, msg: "could not read the move"},
{name: "a truncated walk", setup: func(w *ssWorld) { w.facts(func(f *status.HostFacts) { f.Truncated = true }) },
body: ssBody, code: 409, msg: "could not measure the account's files. try again in a minute"},
{name: "no lane", setup: func(w *ssWorld) {
@@ -735,7 +771,7 @@ func TestPrepareMigratesALegacyRecordBoundThenAdmits(t *testing.T) {
t.Fatalf("the moved record was migrated to %d", rec.Incarnation)
}
})
- t.Run("before the cutover a legacy record is settling", func(t *testing.T) {
+ t.Run("before the cutover the prepare is not enabled", func(t *testing.T) {
w := newOfflineWorld(t, oneEach...)
w.fx.srv.cfg.SelfServeMoves = "offline"
owner, err := w.fx.st.NewAppToken(OpsAccountID, "shop", "paul@keymail.dev", "shop-mover", true, time.Now())
@@ -752,8 +788,11 @@ func TestPrepareMigratesALegacyRecordBoundThenAdmits(t *testing.T) {
}
}
code, _, out := rawReq(t, http.MethodPost, strings.TrimSuffix(w.base, "/offline"), owner, ssBody)
- if code != http.StatusConflict || errorText(out) != msgLifetimeSettling {
- t.Fatalf("prepare before the cutover = %d %s", code, out)
+ if code != http.StatusConflict || errorText(out) != "self-serve moves are not enabled on this console" {
+ t.Fatalf("prepare before the cutover = %d %s, want 409 not enabled", code, out)
+ }
+ if open, _, _ := w.fx.st.SelfServeMoveForHost("bab", "shop", offlineHost); open != nil {
+ t.Fatalf("a row was admitted before the cutover: %+v", open)
}
})
}
@@ -906,6 +945,13 @@ func TestMutationsNameTheMoveAndRace(t *testing.T) {
resume()
want(t, "the cancel after the finish", code, out, http.StatusConflict, "no move is in flight")
})
+ t.Run("a cancel of a move the job is returning", func(t *testing.T) {
+ w := newSelfServeWorld(t, false)
+ row := w.prepared()
+ w.setPhase(row.ID, ssReverting)
+ code, out := cancel(w, row.ID)
+ want(t, "cancel while reverting", code, out, http.StatusConflict, "the move was confirmed and cannot be cancelled. it completes or returns on its own")
+ })
t.Run("a stale nonce cannot confirm or consume", func(t *testing.T) {
w := newSelfServeWorld(t, false)
row := w.prepared()
@@ -1148,3 +1194,46 @@ func TestSwitchOffIsByteIdentical(t *testing.T) {
t.Fatal("a legacy door admitted a row")
}
}
+
+// TestOperatorStatusReadsNoIdentity: the operator's status poll judges a
+// staging self-serve move's start without the identity reads, which are the
+// self-serve door's and the job's; an operator move's poll reads none
+// either.
+func TestOperatorStatusReadsNoIdentity(t *testing.T) {
+ poll := func(t *testing.T, w *ssWorld) {
+ t.Helper()
+ for _, b := range []string{"ie-box", "uk-box"} {
+ region := "eu-west-1"
+ if b == "uk-box" {
+ region = "eu-west-2"
+ }
+ _, raw := w.signer.Record(t, b, moveTestAccount, region, "i-"+b)
+ offlinePut(t, w.fx.rel, boxid.Key(b), raw)
+ }
+ kr := &keyReads{Store: w.fx.rel}
+ w.fx.srv.cfg.Releases = kr
+ defer func() { w.fx.srv.cfg.Releases = w.fx.rel }()
+ v := w.status()
+ if v.State != provision.OfflineStaging {
+ t.Fatalf("status = %+v", v)
+ }
+ if r := append(kr.read(boxid.Prefix), kr.read(hostactors.Key)...); len(r) != 0 {
+ t.Fatalf("an operator status poll read %v", r)
+ }
+ if len(kr.read(provision.RecordKey("bab", "shop", offlineHost))) == 0 {
+ t.Fatal("the poll read nothing: the counter sees no reads")
+ }
+ }
+ t.Run("a self-serve move in staging", func(t *testing.T) {
+ w := newSelfServeWorld(t, false)
+ row := w.prepared()
+ w.queued(row)
+ w.edit(provision.OfflineQueuedToStaging(row.ID, time.Now(), provision.Receipt{}))
+ poll(t, w)
+ })
+ t.Run("an operator move in staging", func(t *testing.T) {
+ w := newSelfServeWorld(t, false)
+ w.ok("/stage", ssBody, provision.OfflineStaging)
+ poll(t, w)
+ })
+}