Repository: ayinke-llc/malak (language: go)

Issue:
Delete update



Discussion / hints:
<p><a href="https://linear.app/ayinke-ventures/issue/MAL-23/delete-update">MAL-23 Delete update</a></p>

Fix patch:
diff --git a/docs/docs.go b/docs/docs.go
index 43998929..eef28299 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -471,6 +471,60 @@ const docTemplate = `{
                         }
                     }
                 }
+            },
+            "delete": {
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "updates"
+                ],
+                "summary": "Delete a specific update",
+                "operationId": "deleteUpdate",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "update unique reference.. e.g update_",
+                        "name": "reference",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "401": {
+                        "description": "Unauthorized",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "404": {
+                        "description": "Not Found",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    }
+                }
             }
         },
         "/workspaces/updates/{reference}/duplicate": {
diff --git a/docs/swagger.json b/docs/swagger.json
index 76dc3a8f..488189ee 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -467,6 +467,60 @@
                         }
                     }
                 }
+            },
+            "delete": {
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "updates"
+                ],
+                "summary": "Delete a specific update",
+                "operationId": "deleteUpdate",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "update unique reference.. e.g update_",
+                        "name": "reference",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "400": {
+                        "description": "Bad Request",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "401": {
+                        "description": "Unauthorized",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "404": {
+                        "description": "Not Found",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/server.APIStatus"
+                        }
+                    }
+                }
             }
         },
         "/workspaces/updates/{reference}/duplicate": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 882e9b64..7c85cf21 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -607,6 +607,42 @@ paths:
       tags:
       - updates
   /workspaces/updates/{reference}:
+    delete:
+      consumes:
+      - application/json
+      operationId: deleteUpdate
+      parameters:
+      - description: update unique reference.. e.g update_
+        in: path
+        name: reference
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/server.APIStatus'
+        "400":
+          description: Bad Request
+          schema:
+            $ref: '#/definitions/server.APIStatus'
+        "401":
+          description: Unauthorized
+          schema:
+            $ref: '#/definitions/server.APIStatus'
+        "404":
+          description: Not Found
+          schema:
+            $ref: '#/definitions/server.APIStatus'
+        "500":
+          description: Internal Server Error
+          schema:
+            $ref: '#/definitions/server.APIStatus'
+      summary: Delete a specific update
+      tags:
+      - updates
     post:
       consumes:
       - application/json
diff --git a/internal/datastore/postgres/update.go b/internal/datastore/postgres/update.go
index 97fdc6ca..5a7b274a 100644
--- a/internal/datastore/postgres/update.go
+++ b/internal/datastore/postgres/update.go
@@ -99,3 +99,13 @@ func (u *updatesRepo) List(ctx context.Context,
 
 	return updates, err
 }
+
+func (u *updatesRepo) Delete(ctx context.Context,
+	update *malak.Update) error {
+
+	_, err := u.inner.NewDelete().Model(update).
+		Where("id = ?", update.ID).
+		Exec(ctx)
+
+	return err
+}
diff --git a/mocks/update.go b/mocks/update.go
index aecdd942..264460c3 100644
--- a/mocks/update.go
+++ b/mocks/update.go
@@ -54,6 +54,20 @@ func (mr *MockUpdateRepositoryMockRecorder) Create(arg0, arg1 any) *gomock.Call
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUpdateRepository)(nil).Create), arg0, arg1)
 }
 
+// Delete mocks base method.
+func (m *MockUpdateRepository) Delete(arg0 context.Context, arg1 *malak.Update) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Delete", arg0, arg1)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Delete indicates an expected call of Delete.
+func (mr *MockUpdateRepositoryMockRecorder) Delete(arg0, arg1 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUpdateRepository)(nil).Delete), arg0, arg1)
+}
+
 // Get mocks base method.
 func (m *MockUpdateRepository) Get(arg0 context.Context, arg1 malak.FetchUpdateOptions) (*malak.Update, error) {
 	m.ctrl.T.Helper()
diff --git a/server/http.go b/server/http.go
index 287896bd..9497e999 100644
--- a/server/http.go
+++ b/server/http.go
@@ -158,6 +158,8 @@ func buildRoutes(
 					WrapMalakHTTPHandler(logger, updateHandler.list, cfg, "updates.list"))
 				r.Post("/{reference}",
 					WrapMalakHTTPHandler(logger, updateHandler.update, cfg, "updates.content_update"))
+				r.Delete("/{reference}",
+					WrapMalakHTTPHandler(logger, updateHandler.delete, cfg, "updates.delete"))
 
 				r.Post("/{reference}/duplicate",
 					WrapMalakHTTPHandler(logger, updateHandler.duplicate, cfg, "updates.duplicate"))
diff --git a/server/update_actions.go b/server/update_actions.go
index 481e33a8..ac01c49a 100644
--- a/server/update_actions.go
+++ b/server/update_actions.go
@@ -77,3 +77,56 @@ func (u *updatesHandler) duplicate(
 		APIStatus: newAPIStatus(http.StatusCreated, "update has been duplicated"),
 	}, StatusSuccess
 }
+
+// @Tags updates
+// @Summary Delete a specific update
+// @id deleteUpdate
+// @Accept  json
+// @Produce  json
+// @Param reference path string required "update unique reference.. e.g update_"
+// @Success 200 {object} APIStatus
+// @Failure 400 {object} APIStatus
+// @Failure 401 {object} APIStatus
+// @Failure 404 {object} APIStatus
+// @Failure 500 {object} APIStatus
+// @Router /workspaces/updates/{reference} [delete]
+func (u *updatesHandler) delete(
+	ctx context.Context,
+	span trace.Span,
+	logger *zap.Logger,
+	w http.ResponseWriter,
+	r *http.Request) (render.Renderer, Status) {
+
+	ref := chi.URLParam(r, "reference")
+
+	span.SetAttributes(attribute.String("reference", ref))
+
+	logger = logger.With(zap.String("reference", ref))
+
+	logger.Debug("Deleting update")
+
+	update, err := u.updateRepo.Get(ctx, malak.FetchUpdateOptions{
+		Reference: malak.Reference(ref),
+	})
+	if errors.Is(err, malak.ErrUpdateNotFound) {
+		return newAPIStatus(http.StatusNotFound,
+			"update does not exists"), StatusFailed
+	}
+
+	if err != nil {
+		logger.Error("could not fetch update", zap.Error(err))
+		return newAPIStatus(http.StatusInternalServerError,
+			"an error occurred while fetching update"), StatusFailed
+	}
+
+	if err := u.updateRepo.Delete(ctx, update); err != nil {
+		logger.Error("could not create updates", zap.Error(err))
+
+		return newAPIStatus(http.StatusInternalServerError,
+			"could not create updates"), StatusFailed
+	}
+
+	return newAPIStatus(http.StatusOK,
+			"update has been deleted"),
+		StatusSuccess
+}
diff --git a/update.go b/update.go
index e3079640..5b59e622 100644
--- a/update.go
+++ b/update.go
@@ -126,4 +126,5 @@ type UpdateRepository interface {
 	Update(context.Context, *Update) error
 	Get(context.Context, FetchUpdateOptions) (*Update, error)
 	List(context.Context, ListUpdateOptions) ([]Update, error)
+	Delete(context.Context, *Update) error
 }
diff --git a/web/ui/src/components/ui/updates/list/single.tsx b/web/ui/src/components/ui/updates/list/single.tsx
index adfa2ded..f71f82b3 100644
--- a/web/ui/src/components/ui/updates/list/single.tsx
+++ b/web/ui/src/components/ui/updates/list/single.tsx
@@ -4,29 +4,38 @@ import { Divider } from "@/components/Divider";
 import { RiDeleteBin2Line, RiFileCopyLine, RiMoreLine, RiPushpinLine } from "@remixicon/react";
 import UpdateBadge from "../../custom/update/badge";
 import {
-  Dialog, DialogTrigger, DialogContent, DialogTitle,
-  DialogHeader, DialogFooter, DialogDescription,
-  DialogClose
+  Dialog, DialogContent, DialogTitle,
+  DialogHeader, DialogFooter, DialogDescription
 } from "@/components/Dialog";
 import { Popover, PopoverTrigger, PopoverContent } from "@/components/Popover";
 import { useMutation } from "@tanstack/react-query";
-import { DUPLICATE_UPDATE } from "@/lib/query-constants";
+import { DELETE_UPDATE, DUPLICATE_UPDATE } from "@/lib/query-constants";
 import { useState } from "react";
 import client from "@/lib/client";
 import { AxiosError, AxiosResponse } from "axios";
 import { toast } from "sonner";
 import { useRouter } from "next/navigation";
+import { usePostHog } from "posthog-js/react";
+import { EVENT_UPDATE_DELETE, EVENT_UPDATE_DUPLICATE } from "@/lib/analytics-constansts";
 
 const SingleUpdate = (update: MalakUpdate) => {
 
   const [loading, setLoading] = useState<boolean>(false)
+  const [deleted, setDeleted] = useState<boolean>(false)
+  const [duplicateDialogOpen, setDuplicateDialogOpen] = useState<boolean>(false)
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState<boolean>(false)
 
   const router = useRouter()
 
+  const posthog = usePostHog()
+
   const duplicateMutation = useMutation({
     mutationKey: [DUPLICATE_UPDATE],
     retry: false,
     gcTime: Infinity,
+    onMutate: () => {
+      posthog?.capture(EVENT_UPDATE_DUPLICATE)
+    },
     onSettled: () => setLoading(false),
     mutationFn: (reference: string) => client.workspaces.duplicateUpdate(reference),
     onError(err: AxiosError<ServerAPIStatus>) {
@@ -37,11 +46,40 @@ const SingleUpdate = (update: MalakUpdate) => {
       toast.error(msg)
     },
     onSuccess: (resp: AxiosResponse<ServerCreatedUpdateResponse>) => {
-      toast.success("update has been duplicated")
+      toast.success(resp.data.message)
+      setDuplicateDialogOpen(false)
       router.push(`/updates/${resp.data.update.reference}`)
     }
   })
 
+  const deletionMutation = useMutation({
+    mutationKey: [DELETE_UPDATE],
+    retry: false,
+    gcTime: Infinity,
+    onMutate: () => {
+      posthog?.capture(EVENT_UPDATE_DELETE)
+    },
+    onSettled: () => setLoading(false),
+    mutationFn: (reference: string) => client.workspaces.deleteUpdate(reference),
+    onError(err: AxiosError<ServerAPIStatus>) {
+      let msg = err.message
+      if (err.response !== undefined) {
+        msg = err.response.data.message
+      }
+      toast.error(msg)
+    },
+    onSuccess: (resp: AxiosResponse<ServerAPIStatus>) => {
+      setDeleteDialogOpen(false)
+      setDeleted(true)
+      toast.success(resp.data.message)
+    }
+  })
+
+  if (deleted) {
+    // essentially remove it from the list
+    return null
+  }
+
   return (
     <>
       <div key={update.id}
@@ -64,16 +102,17 @@ const SingleUpdate = (update: MalakUpdate) => {
               </Button>
             </PopoverTrigger>
             <PopoverContent className="w-40 p-0">
-              <Dialog>
-                <DialogTrigger asChild>
-                  <Button
-                    variant="ghost"
-                    className="w-full justify-start rounded-none px-2 py-1.5 text-sm"
-                  >
-                    <RiFileCopyLine className="mr-2 h-4 w-4" />
-                    Duplicate
-                  </Button>
-                </DialogTrigger>
+              <Dialog open={duplicateDialogOpen}>
+                <Button
+                  variant="ghost"
+                  className="w-full justify-start rounded-none px-2 py-1.5 text-sm"
+                  onClick={() => {
+                    setDuplicateDialogOpen(true)
+                  }}
+                >
+                  <RiFileCopyLine className="mr-2 h-4 w-4" />
+                  Duplicate
+                </Button>
                 <DialogContent>
                   <DialogHeader>
                     <DialogTitle>Duplicate update</DialogTitle>
@@ -83,13 +122,15 @@ const SingleUpdate = (update: MalakUpdate) => {
                     </DialogDescription>
                   </DialogHeader>
                   <DialogFooter className="mt-4">
-                    <DialogClose asChild>
-                      <Button
-                        variant="secondary"
-                        isLoading={loading}>
-                        Cancel
-                      </Button>
-                    </DialogClose>
+                    <Button
+                      variant="secondary"
+                      isLoading={loading}
+                      onClick={() => {
+                        setDuplicateDialogOpen(false)
+                      }}
+                    >
+                      Cancel
+                    </Button>
                     <Button
                       loadingText="Duplicating"
                       isLoading={loading}
@@ -102,16 +143,17 @@ const SingleUpdate = (update: MalakUpdate) => {
                   </DialogFooter>
                 </DialogContent>
               </Dialog>
-              <Dialog>
-                <DialogTrigger asChild>
-                  <Button
-                    variant="ghost"
-                    className="w-full justify-start rounded-none px-2 py-1.5 text-sm text-red-600"
-                  >
-                    <RiDeleteBin2Line className="mr-2 h-4 w-4" />
-                    Delete
-                  </Button>
-                </DialogTrigger>
+              <Dialog open={deleteDialogOpen}>
+                <Button
+                  variant="ghost"
+                  className="w-full justify-start rounded-none px-2 py-1.5 text-sm text-red-600"
+                  onClick={() => {
+                    setDeleteDialogOpen(true)
+                  }}
+                >
+                  <RiDeleteBin2Line className="mr-2 h-4 w-4" />
+                  Delete
+                </Button>
                 <DialogContent>
                   <DialogHeader>
                     <DialogTitle>Confirm Deletion</DialogTitle>
@@ -120,10 +162,22 @@ const SingleUpdate = (update: MalakUpdate) => {
                     </DialogDescription>
                   </DialogHeader>
                   <DialogFooter className="mt-4">
-                    <DialogClose asChild>
-                      <Button variant="secondary">Cancel</Button>
-                    </DialogClose>
-                    <Button variant="destructive">Delete</Button>
+                    <Button
+                      variant="secondary"
+                      isLoading={loading}
+                      onClick={() => {
+                        setDeleteDialogOpen(false)
+                      }}
+                    >
+                      Cancel
+                    </Button>
+                    <Button variant="destructive"
+                      loadingText="Deleting"
+                      isLoading={loading}
+                      onClick={() => {
+                        setLoading(true)
+                        deletionMutation.mutate(update.reference as string)
+                      }}>Delete</Button>
                   </DialogFooter>
                 </DialogContent>
               </Dialog>
diff --git a/web/ui/src/lib/analytics-constansts.ts b/web/ui/src/lib/analytics-constansts.ts
index 1f0afb7a..83cb5b39 100644
--- a/web/ui/src/lib/analytics-constansts.ts
+++ b/web/ui/src/lib/analytics-constansts.ts
@@ -1,1 +1,3 @@
 export const EVENT_LOGIN_BUTTON_CLICKED = 'EVENT_LOGIN_BUTTON_CLICKED' as const
+export const EVENT_UPDATE_DUPLICATE = 'EVENT_UPDATE_DUPLICATE' as const
+export const EVENT_UPDATE_DELETE = 'EVENT_UPDATE_DELETE' as const
diff --git a/web/ui/src/lib/query-constants.ts b/web/ui/src/lib/query-constants.ts
index 459e83ba..7a6a6e80 100644
--- a/web/ui/src/lib/query-constants.ts
+++ b/web/ui/src/lib/query-constants.ts
@@ -4,3 +4,4 @@ export const UPDATE_CONTENT = 'UPDATE_CONTENT' as const
 export const LIST_UPDATES = 'LIST_UPDATES' as const
 export const SWITCH_WORKSPACE = 'SWITCH_WORKSPACE' as const
 export const DUPLICATE_UPDATE = 'DUPLICATE_UPDATE' as const
+export const DELETE_UPDATE = 'DELETE_UPDATE' as const


Test patch:
diff --git a/internal/datastore/postgres/update_test.go b/internal/datastore/postgres/update_test.go
index 30393bf7..72887478 100644
--- a/internal/datastore/postgres/update_test.go
+++ b/internal/datastore/postgres/update_test.go
@@ -9,6 +9,29 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+func TestUpdates_Delete(t *testing.T) {
+
+	client, teardownFunc := setupDatabase(t)
+	defer teardownFunc()
+
+	updatesRepo := NewUpdatesRepository(client)
+	id := uuid.MustParse("0902ef67-903e-47b8-8f9d-111a9e0ca0c7")
+
+	// workspaces.yml testdata
+	updateByID, err := updatesRepo.Get(context.Background(), malak.FetchUpdateOptions{
+		ID: id,
+	})
+	require.NoError(t, err)
+
+	require.NoError(t, updatesRepo.Delete(context.Background(), updateByID))
+
+	_, err = updatesRepo.Get(context.Background(), malak.FetchUpdateOptions{
+		ID: id,
+	})
+	require.Error(t, err)
+	require.Equal(t, malak.ErrUpdateNotFound, err)
+}
+
 func TestUpdates_Update(t *testing.T) {
 
 	client, teardownFunc := setupDatabase(t)
diff --git a/server/testdata/TestUpdatesHandler_Delete/could_not_fetch_update_from_db.golden b/server/testdata/TestUpdatesHandler_Delete/could_not_fetch_update_from_db.golden
new file mode 100644
index 00000000..8ca4d306
--- /dev/null
+++ b/server/testdata/TestUpdatesHandler_Delete/could_not_fetch_update_from_db.golden
@@ -0,0 +1,1 @@
+{"message":"an error occurred while fetching update"}
diff --git a/server/testdata/TestUpdatesHandler_Delete/deleting_content_fails.golden b/server/testdata/TestUpdatesHandler_Delete/deleting_content_fails.golden
new file mode 100644
index 00000000..e5ed8ca2
--- /dev/null
+++ b/server/testdata/TestUpdatesHandler_Delete/deleting_content_fails.golden
@@ -0,0 +1,1 @@
+{"message":"could not create updates"}
diff --git a/server/testdata/TestUpdatesHandler_Delete/deleting_content_succeeds.golden b/server/testdata/TestUpdatesHandler_Delete/deleting_content_succeeds.golden
new file mode 100644
index 00000000..068ad163
--- /dev/null
+++ b/server/testdata/TestUpdatesHandler_Delete/deleting_content_succeeds.golden
@@ -0,0 +1,1 @@
+{"message":"update has been deleted"}
diff --git a/server/testdata/TestUpdatesHandler_Delete/update_not_exists.golden b/server/testdata/TestUpdatesHandler_Delete/update_not_exists.golden
new file mode 100644
index 00000000..70e23b23
--- /dev/null
+++ b/server/testdata/TestUpdatesHandler_Delete/update_not_exists.golden
@@ -0,0 +1,1 @@
+{"message":"update does not exists"}
diff --git a/server/update_actions_test.go b/server/update_actions_test.go
index f3033946..2014cde6 100644
--- a/server/update_actions_test.go
+++ b/server/update_actions_test.go
@@ -81,6 +81,112 @@ func generateUpdateDuplicationContent() []struct {
 	}
 }
 
+func TestUpdatesHandler_Delete(t *testing.T) {
+	for _, v := range generateUpdateDeletionTable() {
+
+		t.Run(v.name, func(t *testing.T) {
+
+			controller := gomock.NewController(t)
+			defer controller.Finish()
+
+			updateRepo := malak_mocks.NewMockUpdateRepository(controller)
+
+			v.mockFn(updateRepo)
+
+			u := &updatesHandler{
+				referenceGenerator: &mockReferenceGenerator{},
+				updateRepo:         updateRepo,
+			}
+
+			var b = bytes.NewBuffer(nil)
+
+			rr := httptest.NewRecorder()
+
+			req := httptest.NewRequest(http.MethodPost, "/", b)
+			req.Header.Add("Content-Type", "application/json")
+
+			req = req.WithContext(writeUserToCtx(req.Context(), &malak.User{}))
+			req = req.WithContext(writeWorkspaceToCtx(req.Context(), &malak.Workspace{}))
+
+			ctx := chi.NewRouteContext()
+			ctx.URLParams.Add("reference", "update_jfnkfjkf")
+			req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, ctx))
+
+			WrapMalakHTTPHandler(getLogger(t), u.delete, getConfig(), "updates.delete").
+				ServeHTTP(rr, req)
+
+			require.Equal(t, v.expectedStatusCode, rr.Code)
+			verifyMatch(t, rr)
+		})
+	}
+}
+
+func generateUpdateDeletionTable() []struct {
+	name               string
+	mockFn             func(update *malak_mocks.MockUpdateRepository)
+	expectedStatusCode int
+} {
+
+	return []struct {
+		name               string
+		mockFn             func(update *malak_mocks.MockUpdateRepository)
+		expectedStatusCode int
+	}{
+		{
+			name: "update not exists",
+			mockFn: func(update *malak_mocks.MockUpdateRepository) {
+				update.
+					EXPECT().
+					Get(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(nil, malak.ErrUpdateNotFound)
+			},
+			expectedStatusCode: http.StatusNotFound,
+		},
+		{
+			name: "could not fetch update from db",
+			mockFn: func(update *malak_mocks.MockUpdateRepository) {
+				update.
+					EXPECT().
+					Get(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(nil, errors.New("error while fetching"))
+			},
+			expectedStatusCode: http.StatusInternalServerError,
+		},
+		{
+			name: "deleting content fails",
+			mockFn: func(update *malak_mocks.MockUpdateRepository) {
+				update.
+					EXPECT().
+					Get(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(&malak.Update{}, nil)
+
+				update.EXPECT().Delete(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(errors.New("unknown error"))
+			},
+			expectedStatusCode: http.StatusInternalServerError,
+		},
+		{
+			name: "deleting content succeeds",
+			mockFn: func(update *malak_mocks.MockUpdateRepository) {
+				update.
+					EXPECT().
+					Get(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(&malak.Update{}, nil)
+
+				update.EXPECT().Delete(gomock.Any(), gomock.Any()).
+					Times(1).
+					Return(nil)
+			},
+			expectedStatusCode: http.StatusOK,
+		},
+	}
+}
+
 func TestUpdatesHandler_Duplicate(t *testing.T) {
 	for _, v := range generateUpdateDuplicationContent() {