# feat(control-plane): idempotent deletes + command-level wiring tests

`2ca6717`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·3w ago·2 files·+105 added/-0 removed

- org/project/repo delete now treat a 404 from the delete call as the desired end state (already gone), printing "<noun> <label> not found; nothing to delete" instead of surfacing a raw 404.
- Add command-level tests driving each delete command through cobra against a fake control plane: --force issues DELETE on the right path, an already-gone resource is idempotent, and a non-interactive run without --force refuses rather than deleting unprompted.

Addresses trail #642 findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

## Sessions

416181183067View transcript

## Changes

2

- cmd/entire/cli

- Mcorecmd.go+7

- Acorecmd_delete_test.go+98

```
89 unmodified lines

90
91
92
93
94
95
96
97
98
99
100
101
102

89 unmodified lines

return nil
		}
		if err := del(ctx, c, id); err != nil {
			// Idempotent delete: a resource that's already gone (a 404 from the
			// delete call — e.g. a ULID passed straight through, or a concurrent
			// delete) is the desired end state, not an error.
			if isNotFound(err) {
				cmd.Printf("%s not found; nothing to delete\n", label)
				return nil
			}
			return err
		}
		cmd.Printf("Deleted %s\n", label)
```

Mcmd/entire/cli/corecmd.go+7

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

package cli

import (
	"bytes"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

"github.com/spf13/cobra"
	"github.com/stretchr/testify/require"

"github.com/entireio/cli/internal/coreapi"
)

// testDeleteULID is a syntactically valid ULID (26 Crockford base32 chars, no
// I/L/O/U) so it passes looksLikeULID and the delete commands skip the name
// lookup, addressing the resource by id directly.
const testDeleteULID = "01HZX7QABCDEFGHJKMNPQRSTVW"

// runDeleteCmd points the active-context client at srv via the activeCoreClient
// seam, runs newCmd() with args, and returns its stdout and error. The caller
// must not be parallel: the seam is package-global.
func runDeleteCmd(t *testing.T, newCmd func() *cobra.Command, srvURL string, args ...string) (string, error) {

t.Helper()
	prev := activeCoreClient
	activeCoreClient = func(context.Context) (*coreapi.Client, error) {
		return coreapi.NewWithBearer(srvURL, "tok")
	}
	t.Cleanup(func() { activeCoreClient = prev })

cmd := newCmd()
	var out bytes.Buffer
	cmd.SetOut(&out)
	cmd.SetErr(&bytes.Buffer{})
	cmd.SetArgs(args)
	err := cmd.ExecuteContext(t.Context())
	return out.String(), err
}

// TestControlPlaneDelete_Wiring exercises the org/project/repo delete commands
// end-to-end through cobra: that --force bypasses the prompt and issues DELETE
// against the right path, that an already-gone resource (404) is idempotent,
// and that a non-interactive run without --force refuses rather than deleting
// unprompted.
//
// Not parallel: swaps the package-level activeCoreClient seam.
func TestControlPlaneDelete_Wiring(t *testing.T) {
	cases := []struct {
		noun     string
		newCmd   func() *cobra.Command
		wantPath string
	}{
		{"org", newOrgDeleteCmd, "/api/v1/orgs/" + testDeleteULID},
		{"project", newProjectDeleteCmd, "/api/v1/projects/" + testDeleteULID},
		{"repo", newRepoDeleteCmd, "/api/v1/repos/" + testDeleteULID},
	}

for _, tc := range cases {
		t.Run(tc.noun+"/force deletes via the right path", func(t *testing.T) {
			var gotMethod, gotPath string
			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				gotMethod, gotPath = r.Method, r.URL.Path
				w.WriteHeader(http.StatusNoContent)
			}))
			t.Cleanup(srv.Close)

out, err := runDeleteCmd(t, tc.newCmd, srv.URL, testDeleteULID, "--force")
			require.NoError(t, err)
			require.Equal(t, http.MethodDelete, gotMethod)
			require.Equal(t, tc.wantPath, gotPath)
			require.Contains(t, out, "Deleted "+tc.noun+" "+testDeleteULID)
		})

t.Run(tc.noun+"/already-gone is idempotent", func(t *testing.T) {
			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
				writeNotFoundProblem(t, w)
			}))
			t.Cleanup(srv.Close)

out, err := runDeleteCmd(t, tc.newCmd, srv.URL, testDeleteULID, "--force")
			require.NoError(t, err)
			require.Contains(t, out, "not found; nothing to delete")
		})

t.Run(tc.noun+"/refuses without --force when non-interactive", func(t *testing.T) {
			// A ULID needs no resolve, so the refusal lands before any request.
			srv := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
				t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
			}))
			t.Cleanup(srv.Close)

_, err := runDeleteCmd(t, tc.newCmd, srv.URL, testDeleteULID)
			require.Error(t, err)
			require.Contains(t, err.Error(), "--force")
		})
	}
}
