package console import ( "context" "encoding/json" "errors" "net/http" "os" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" ) // Re-review finding 10: on a claim console the delete's retirement can // delete the record and then park while obligations remain. A retried // delete in that interval must get the claim flow's answer (its pending // operation and phase), never a 200 that says the delete is done. func TestClaimDeleteRetryWhileParkingIsNotDone(t *testing.T) { const host = "acme.go.tito.io" cx := newClaimCase(t) cx.instanceWorld(t) cx.instance(t, `{"host":"`+host+`","region":"ie"}`) url := cx.ts.URL + "/api/cli/apps/bab/titogo/instances/" + host status, _, out := rawReq(t, http.MethodDelete, url, cx.bearer, "") if status != http.StatusAccepted { t.Fatalf("delete = %d %s, want 202", status, out) } op := claimReplyField(t, out, "operation_id") parking := func() bool { obs, err := hostclaim.Read(context.Background(), cx.rel, host) if err != nil { t.Fatal(err) } _, gone := provision.GetRecord(context.Background(), cx.rel, "bab", "titogo", host) return !obs.Absent && obs.Claim.Op.Kind == hostclaim.KindParking && len(obs.Claim.Obligations) > 0 && errors.Is(gone, os.ErrNotExist) } for i := 0; i < 6 && !parking(); i++ { cx.ackClaim(t, host, op) claimWorker("StartClaimRecoveryPass")(t, cx) } if !parking() { t.Fatal("the claim world did not reach parking with obligations left") } status, _, out = rawReq(t, http.MethodDelete, url, cx.bearer, "") if status == http.StatusOK { t.Fatalf("a retry while the claim parks answered %d %s: the delete is not done", status, out) } if status != http.StatusAccepted { t.Fatalf("retry = %d %s, want the claim flow's 202", status, out) } var reply map[string]any if err := json.Unmarshal([]byte(out), &reply); err != nil || reply["operation_id"] != op || reply["phase"] != hostclaim.KindParking { t.Fatalf("retry answered %s, want operation %s at %s", out, op, hostclaim.KindParking) } }