package hostops import ( "context" "errors" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" ) func TestPlan13RecoveryRefusesForeignHome(t *testing.T) { w := newC7World(t, func(c *hostclaim.Claim, _ *provision.Record, _ *AppAdmission) { c.Op.Intent["home_region"] = "ie" }) o, err := hostclaim.Read(t.Context(), w.raw, w.host) if err != nil { t.Fatal(err) } called := false err = w.control.RunRecovery(t.Context(), func(ctx context.Context) error { return runMoveRecovery(ctx, o, func(context.Context, *MoveRecoveryGrant) error { called = true; return nil }) }) if !errors.Is(err, ErrIdentity) || called { t.Fatalf("foreign recovery invoked=%v err=%v", called, err) } } func TestPlan13LossRefusesForeignHome(t *testing.T) { w := newLossWorld(t, "landing", func(c *hostclaim.Claim, _ *provision.Record, _ *AppAdmission) { c.Op.Intent["home_region"] = "ie" }) _, err := w.control.moveLossClaim(t.Context(), "bab", "app", lossHost) if !errors.Is(err, ErrIdentity) { t.Fatalf("foreign loss: %v", err) } } func TestPlan13RecoveryAndLossHomeAgreement(t *testing.T) { for _, home := range []string{"ie", "uk", ""} { t.Run(home, func(t *testing.T) { w := newC7World(t, func(c *hostclaim.Claim, _ *provision.Record, _ *AppAdmission) { c.Op.Intent["home_region"] = "ie" }) w.control.homeRegion = home called := false err := w.control.RunRecovery(t.Context(), func(ctx context.Context) error { return runMoveRecovery(ctx, w.claim(t), func(ctx context.Context, g *MoveRecoveryGrant) error { called = true _, _, _, err := g.current(ctx) return err }) }) if (err == nil) != (home == "ie") || called != (home == "ie") { t.Fatalf("recovery called=%v: %v", called, err) } l := newLossWorld(t, "landing", func(c *hostclaim.Claim, _ *provision.Record, _ *AppAdmission) { c.Op.Intent["home_region"] = "ie" }) l.control.homeRegion = home _, err = l.control.InspectMoveLoss(t.Context(), l.signer, lossBucket, "operator", "bab", "app", lossHost, "uk-label") if (err == nil) != (home == "ie") { t.Fatalf("loss: %v", err) } }) } } // A named move that started before any home designation records no home // name. Publishing the first designation must not strand it in recovery, // lifecycle writes or declare-lost replays; one that recorded a different // home must still refuse. func TestPlan13AliasFreeOperationSurvivesFirstDesignation(t *testing.T) { for _, kind := range []string{hostclaim.KindRestoring, hostclaim.KindReverting, "landing", "moving"} { c := &hostclaim.Claim{} c.Op.Kind = kind c.Op.Intent = map[string]string{} if !operationHomeMatches("ie", c) { t.Errorf("%s: alias-free op refused after the first designation", kind) } c.Op.Intent["home_region"] = "ie" if !operationHomeMatches("ie", c) { t.Errorf("%s: matching home refused", kind) } c.Op.Intent["home_region"] = "uk" if operationHomeMatches("ie", c) { t.Errorf("%s: mismatched home accepted", kind) } } }