package host import ( "context" "encoding/json" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" ) type plan9Fix2PhaseStore struct { release.Store phases []string key string } func (s *plan9Fix2PhaseStore) Put(ctx context.Context, key string, body []byte, etag string) (string, error) { next, err := s.Store.Put(ctx, key, body, etag) if err == nil && key == s.key { var c hostclaim.Claim if json.Unmarshal(body, &c) == nil { s.phases = append(s.phases, c.Op.Phase) } } return next, err } func TestPlan9Fix2LandingCatchUpPersistsEachAdjacentStep(t *testing.T) { t.Parallel() h := newLandingHarness(t) h.flip(t) h.lw = &movesWorker{cfg: h.landingCfg(t)} key, err := hostclaim.Key(p1Host) if err != nil { t.Fatal(err) } store := &plan9Fix2PhaseStore{Store: h.s, key: key} rec := h.read(t, p1Host) rec.Move.LandingPhase = provision.LandProbed run := landingRun{w: h.lw, s: store, rec: rec, g: provision.MoveGuard{ID: p1ID, Incarnation: h.inc}} before := h.claim(t) if _, err = run.syncClaimPhase(t.Context()); err != nil { t.Fatal(err) } order := provision.LandingOrder() if len(store.phases) != len(order)-1 { t.Fatalf("catch-up skipped durable phase transitions: %v", store.phases) } for i, phase := range store.phases { if phase != order[i+1] { t.Fatalf("transition %d: %s", i, phase) } } after := h.claim(t) if before.Holder.Session != after.Holder.Session || before.Holder.Stamp != after.Holder.Stamp || before.Holder.Generation != after.Holder.Generation { t.Fatal("catch-up changed admitted writer") } }