carlos / platform Public

console: retirement tests: the refused create names its settling sentence, and an orphan retirement crashed before O1 recovers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 76bcdfbfb78514d143bc9e1366acaa356d4d54ac parent 4b69f7b
1 file changed, +21 −2
  • internal/console/lifetimes_retire_test.go +21 −2
diff --git a/internal/console/lifetimes_retire_test.go b/internal/console/lifetimes_retire_test.go
index 9661d77bf..48782f752 100644
--- a/internal/console/lifetimes_retire_test.go
+++ b/internal/console/lifetimes_retire_test.go
@@ -893,6 +893,25 @@ func TestRetirementRecoveryBranches(t *testing.T) {
writeSlot(t, l.fx.rel, ltOne, provision.HostIncarnation{Incarnation: 1, State: provision.SlotLive, Account: "bab", App: ltApp, Intent: "create someone-else"})
l.recoveredTo(t, r.OpID, lifetimePending)
})
+ t.Run("orphan, crashed right after BeginRetirement", func(t *testing.T) {
+ l := newLtOn(t)
+ writeSlot(t, l.fx.rel, ltOne, provision.HostIncarnation{Incarnation: 1, State: provision.SlotLive, Account: "bab", App: ltApp, Intent: "create lost"})
+ // O1's slot write fails: the row is durable and nothing else moved.
+ l.bucket.setHook(failNth("put", provision.HostIncarnationKey(ltOne), 1, false))
+ if err := retireOn(t, l.fx.srv, ltOne, retireAny); !errors.Is(err, errLifetimePending) {
+ t.Fatalf("retire = %v, want pending", err)
+ }
+ l.bucket.setHook(nil)
+ r := lastRetirement(t, l.fx.st, ltOne)
+ if r.Kind != retireOrphan || r.State != lifetimePending {
+ t.Fatalf("retirement = %+v, want a pending orphan", r)
+ }
+ if slot, _ := slotOf(t, l.fx.rel, ltOne); slot == nil || slot.State != provision.SlotLive {
+ t.Fatalf("slot = %+v, want it still live before O1", slot)
+ }
+ l.recoveredTo(t, r.OpID, lifetimeCommitted)
+ requireRetired(t, l.fx.rel, ltApp, ltOne, 1)
+ })
for _, step := range []string{"c1", "c2"} {
t.Run("cleanup, crashed at "+step, func(t *testing.T) {
l := newLtOn(t)
@@ -1221,8 +1240,8 @@ func TestOrphanSlotReclaimedByTheNextCreate(t *testing.T) {
if err := l.fx.st.BeginRetirement(Retirement{OpID: "held", Kind: retireNormal, Account: "bab", App: "web", Host: ltOne, Incarnation: 1}); err != nil {
t.Fatal(err)
}
- if code, body := l.create(t, ltOne); code != http.StatusConflict {
- t.Fatalf("create = %d %s, want 409", code, body)
+ if code, body := l.create(t, ltOne); code != http.StatusConflict || ltErr(body) != msgLifetimeSettling {
+ t.Fatalf("create = %d %s, want 409 %q", code, body, msgLifetimeSettling)
}
if rows := allocRows(t, l.fx.st, ltOne); len(rows) != 0 {
t.Fatalf("allocations = %+v, want none", rows)