@@ -216,6 +216,13 @@ func (c *Client) UpdateRecipeContent(ctx context.Context, recipeID, markdown str
return c.doAuthenticated(ctx, http.MethodPost, path, body, nil)
}
+// DeleteRecipe logs in when needed and deletes an existing recipe.
+func (c *Client) DeleteRecipe(ctx context.Context, recipeID string) error {
+ path := "/api/recipe/" + url.PathEscape(recipeID)
+
+ return c.doAuthenticated(ctx, http.MethodDelete, path, nil, nil)
+}
+
func (c *Client) getRecipes(ctx context.Context, path string) ([]RecipeCard, error) {
var response recipeListResponse
decodeRecipes := func(decoder *json.Decoder) error {
@@ -159,6 +159,21 @@ func TestUpdateRecipeContentACIDRecipesSave8And9And13UpdatesExistingRecipe(t *te
}
}
+func TestDeleteRecipeDeletesSavedRecipe(t *testing.T) {
+ var deleteCalls int
+ client, closeServer := newTestClient(t, deleteRecipeTestHandler(t, &deleteCalls))
+ defer closeServer()
+
+ err := client.DeleteRecipe(context.Background(), "recipe/with space")
+ if err != nil {
+ t.Fatalf("DeleteRecipe() error = %v", err)
+ }
+
+ if deleteCalls != 1 {
+ t.Fatalf("delete calls = %d, want 1", deleteCalls)
+ }
+}
+
func TestUserPathEscapesAuthenticatedUsername(t *testing.T) {
client := &Client{authenticatedUsername: "user/name"}
@@ -356,6 +371,24 @@ func updateRecipeContentTestHandler(t *testing.T, updateCalls *int) http.Handler
}
}
+func deleteRecipeTestHandler(t *testing.T, deleteCalls *int) http.HandlerFunc {
+ t.Helper()
+
+ return func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.EscapedPath() {
+ case "/api/public/login":
+ writeLoginResponse(t, w)
+ case "/api/recipe/recipe%2Fwith%20space":
+ requireMethod(t, r, http.MethodDelete, "delete recipe")
+ requireSessionCookie(t, r)
+ (*deleteCalls)++
+ w.WriteHeader(http.StatusNoContent)
+ default:
+ t.Fatalf("unexpected path %s", r.URL.EscapedPath())
+ }
+ }
+}
+
func requirePreviewRecipeTextRequest(t *testing.T, r *http.Request) {
t.Helper()