package hostops import ( "context" "errors" "testing" "github.com/carlosframework/platform/internal/release" ) func TestAppMutationRunnerOwnsTheWholeCall(t *testing.T) { for _, kind := range []string{"joined", "validation", "ambiguous-bucket", "ambiguous-backend", "foreign", "nested"} { t.Run(kind, func(t *testing.T) { ctx := t.Context() w, raw, _ := testWriter(t) seedAdmission(t, raw, "bab", "demo", openAdmission("app")) check, err := NewProductionMutationCheck(raw, w) if err != nil { t.Fatal(err) } data, err := NewMutationBoundary(true, raw, check) if err != nil { t.Fatal(err) } owner := RegistrationOwner{Console: "console", Enrollment: "enrollment", Controller: "controller"} if err = BindMutationRunner(data, w, owner, func(context.Context, string, string) (string, error) { return "app", nil }); err != nil { t.Fatal(err) } if kind == "foreign" { _, err = w.BeginExclusive(ctx, "bab", "demo", "app", AppOperation{ID: "foreign", Kind: OpAppMutation, AppID: "app"}, owner) if err != nil { t.Fatal(err) } } before, _ := ReadAdmission(ctx, raw, "bab", "demo", "app") called := false failure := errors.New("joined callback failure") var saved context.Context err = release.WithAppMutation(ctx, data, "bab", "demo", "test", func(pctx context.Context) error { called = true saved = pctx if err := RequireAppMutation(pctx, raw, "bab", "demo", "app"); err != nil { return err } switch kind { case "validation": return failure case "ambiguous-backend": MarkAppMutationIssued(pctx) return failure case "nested": a, _ := ReadAdmission(pctx, raw, "bab", "demo", "app") return release.WithAppMutation(pctx, data, "bab", "demo", "nested", func(inner context.Context) error { b, _ := ReadAdmission(inner, raw, "bab", "demo", "app") if a.Value.Op.ID != b.Value.Op.ID { t.Fatal("nested call acquired another operation") } _, err := data.Put(inner, "apps/bab/demo/test.json", []byte(`{}`), "") return err }) } _, err := data.Put(pctx, "apps/bab/demo/test.json", []byte(`{}`), "") if err != nil { return err } if kind == "ambiguous-bucket" { return failure } return nil }) after, _ := ReadAdmission(ctx, raw, "bab", "demo", "app") switch kind { case "foreign": if called || !errors.Is(err, ErrPending) || before.ETag != after.ETag { t.Fatalf("crossed foreign op: called=%v err=%v", called, err) } case "validation": if !errors.Is(err, failure) || after.Value.State != "open" || after.Value.Op != nil { t.Fatalf("unissued failure retained authority: %v %+v", err, after.Value) } case "ambiguous-bucket", "ambiguous-backend": if !errors.Is(err, failure) || after.Value.State != "closed" || after.Value.Op == nil { t.Fatalf("ambiguous effect lost authority: %v %+v", err, after.Value) } default: if err != nil || after.Value.State != "open" || after.Value.Op != nil { t.Fatalf("joined call did not finish: %v %+v", err, after.Value) } } if saved != nil && RequireAppMutation(saved, raw, "bab", "demo", "app") == nil { t.Fatal("callback permit survived settlement") } }) } } func TestRunAppMutationRequiresLocalIncarnation(t *testing.T) { for _, local := range []string{"", "old", "app"} { t.Run("local="+local, func(t *testing.T) { w, raw, _ := testWriter(t) seedAdmission(t, raw, "bab", "demo", openAdmission("app")) check, err := NewProductionMutationCheck(raw, w) if err != nil { t.Fatal(err) } data, err := NewMutationBoundary(true, raw, check) if err != nil { t.Fatal(err) } owner := RegistrationOwner{Console: "console", Enrollment: "enrollment", Controller: "controller"} if err = BindMutationRunner(data, w, owner, func(context.Context, string, string) (string, error) { return local, nil }); err != nil { t.Fatal(err) } before, err := ReadAdmission(t.Context(), raw, "bab", "demo", "app") if err != nil { t.Fatal(err) } called := false err = release.WithAppMutation(t.Context(), data, "bab", "demo", "test", func(context.Context) error { called = true; return nil }) after, e := ReadAdmission(t.Context(), raw, "bab", "demo", "app") if e != nil { t.Fatal(e) } if local == "app" { if err != nil || !called { t.Fatalf("matching identity refused: %v", err) } } else if err == nil || called || before.ETag != after.ETag { t.Fatalf("local identity %q acquired authority: called=%v err=%v", local, called, err) } }) } }