//go:build unix package hostbox import ( "context" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/release" ) // plan9D1Probe turns the adopted fixture into a landing op at probing with the // fixture box executing, keeping the holder's stamp so the local record stays // active, then selects the probe's spawn work there. func plan9D1Probe(t *testing.T, continues func(string, string, string) bool) (hostwrite.Work, release.Store, func(func(*hostclaim.Claim) error)) { t.Helper() source, store, obs := workFixture(t) source.Continue = continues source.Policy = func(context.Context, *hostclaim.Claim, Selection, hostwrite.Identity) error { return nil } ctx := context.Background() update := func(fn func(*hostclaim.Claim) error) { t.Helper() current, err := hostclaim.Read(ctx, store, "shop.example") if err != nil { t.Fatal(err) } if _, err = hostclaim.Update(ctx, store, current, fn); err != nil { t.Fatal(err) } } obs, err := hostclaim.Update(ctx, store, obs, func(c *hostclaim.Claim) error { c.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "m1", Phase: hostclaim.PhaseLandingProbing, Exec: "box", Participants: []string{"source", "box"}, Intent: map[string]string{"to": "ie", "source": "source", "target": "box"}} return nil }) if err != nil { t.Fatal(err) } work, err := source.Select(ctx, obs.Host, "spawn") if err != nil { t.Fatal(err) } return work, store, update } func plan9D1Write(work hostwrite.Work) error { return work.With(context.Background(), hostwrite.Identity{Config: "c", Release: "r"}, func() error { return nil }) } func TestPlan9D1ProbeSessionSurvivesForwardSteps(t *testing.T) { work, _, update := plan9D1Probe(t, hostclaim.SessionContinues) if err := plan9D1Write(work); err != nil { t.Fatal(err) } update(hostclaim.SetMovePhase("m1", hostclaim.KindLanding, hostclaim.PhaseLandingProbing, hostclaim.PhaseLandingProbed)) if err := plan9D1Write(work); err != nil { t.Fatalf("probed ended the probe's session: %v", err) } update(hostclaim.KeepWriterSession("m1", hostclaim.SetMovePhase("m1", hostclaim.KindLanding, hostclaim.PhaseLandingProbed, hostclaim.PhaseLandingCommitted))) if err := plan9D1Write(work); err != nil { t.Fatalf("committed ended the probe's session: %v", err) } } func TestPlan9D1ProbeSessionEndsOnEveryOtherChange(t *testing.T) { for name, change := range map[string]func(*hostclaim.Claim) error{ // A landing failure reverts the op: the claim moves to restoring // under the same op id (moves ยง7.8). "rollback": func(c *hostclaim.Claim) error { c.Op.Kind, c.Op.Phase = hostclaim.KindRestoring, "releasing" return nil }, "backward": func(c *hostclaim.Claim) error { c.Op.Phase = "restored"; return nil }, "release": func(c *hostclaim.Claim) error { c.Op = hostclaim.Op{Kind: hostclaim.KindServing, Exec: "box"} return nil }, "other-op": func(c *hostclaim.Claim) error { c.Op.ID = "m2"; return nil }, "session": func(c *hostclaim.Claim) error { c.Holder.Session++; return nil }, "generation": func(c *hostclaim.Claim) error { c.Holder.Generation = "fresh" return nil }, } { t.Run(name, func(t *testing.T) { work, _, update := plan9D1Probe(t, hostclaim.SessionContinues) update(change) if err := plan9D1Write(work); err == nil { t.Fatalf("%s kept the probe's session", name) } }) } } // Without the moves C1 table (either flag off) main's phase equality stands. func TestPlan9D1PhaseEqualityStandsWithoutTheTable(t *testing.T) { work, _, update := plan9D1Probe(t, nil) update(hostclaim.SetMovePhase("m1", hostclaim.KindLanding, hostclaim.PhaseLandingProbing, hostclaim.PhaseLandingProbed)) if err := plan9D1Write(work); err == nil { t.Fatal("a phase step kept the session without the table") } }