diff --git a/src/constants/responses.ts b/src/constants/responses.ts index 44007852..bff6a2c0 100644 --- a/src/constants/responses.ts +++ b/src/constants/responses.ts @@ -77,3 +77,5 @@ export const INVALID_TOKEN_FORMAT = "Invalid Authentication header format. Expected 'Bearer '"; export const AUTHENTICATION_ERROR = "Invalid Authentication token"; +export const TASK_UPDATE_SENT_MESSAGE = + "Task update sent on Discord's tracking-updates channel."; diff --git a/src/controllers/taskUpdatesHandler.ts b/src/controllers/taskUpdatesHandler.ts index a87593eb..a509d80a 100644 --- a/src/controllers/taskUpdatesHandler.ts +++ b/src/controllers/taskUpdatesHandler.ts @@ -14,11 +14,18 @@ export const sendTaskUpdatesHandler = async (request: IRequest, env: env) => { try { await verifyNodejsBackendAuthToken(authHeader, env); const updates: TaskUpdates = await request.json(); - const { completed, planned, blockers, userName, taskId } = updates.content; - await sendTaskUpdate(completed, planned, blockers, userName, taskId, env); - return new JSONResponse( - "Task update sent on Discord's tracking-updates channel." + const { completed, planned, blockers, userName, taskId, taskTitle } = + updates.content; + await sendTaskUpdate( + completed, + planned, + blockers, + userName, + taskId, + taskTitle, + env ); + return new JSONResponse(response.TASK_UPDATE_SENT_MESSAGE); } catch (error: any) { return new JSONResponse({ res: response.INTERNAL_SERVER_ERROR, diff --git a/src/typeDefinitions/taskUpdate.d.ts b/src/typeDefinitions/taskUpdate.d.ts index b12b2977..a03c8919 100644 --- a/src/typeDefinitions/taskUpdate.d.ts +++ b/src/typeDefinitions/taskUpdate.d.ts @@ -5,5 +5,6 @@ export interface TaskUpdates { blockers: string; userName: string; taskId: string; + taskTitle: string; }; } diff --git a/src/utils/sendTaskUpdates.ts b/src/utils/sendTaskUpdates.ts index 10502a2e..33d5180a 100644 --- a/src/utils/sendTaskUpdates.ts +++ b/src/utils/sendTaskUpdates.ts @@ -7,11 +7,12 @@ export async function sendTaskUpdate( blockers: string, userName: string, taskId: string, + taskTitle: string, env: env ): Promise { const taskUrl = config(env).RDS_STATUS_SITE_URL + `/tasks/${taskId}`; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n${completed}\n\n` + `**Planned**\n${planned}\n\n` + `**Blockers**\n${blockers}`; diff --git a/tests/unit/handlers/taskUpdateHandler.test.ts b/tests/unit/handlers/taskUpdateHandler.test.ts index f8954d51..eb61f89a 100644 --- a/tests/unit/handlers/taskUpdateHandler.test.ts +++ b/tests/unit/handlers/taskUpdateHandler.test.ts @@ -2,7 +2,6 @@ import { sendTaskUpdatesHandler } from "../../../src/controllers/taskUpdatesHand import JSONResponse from "../../../src/utils/JsonResponse"; import * as response from "../../../src/constants/responses"; import { sendTaskUpdate } from "../../../src/utils/sendTaskUpdates"; - import { generateDummyRequestObject } from "../../fixtures/fixture"; jest.mock("../../../src/utils/verifyAuthToken", () => ({ @@ -11,7 +10,6 @@ jest.mock("../../../src/utils/verifyAuthToken", () => ({ jest.mock("../../../src/utils/sendTaskUpdates", () => ({ sendTaskUpdate: jest.fn().mockResolvedValue(undefined), })); - describe("sendTaskUpdatesHandler", () => { const mockEnv = { DISCORD_TOKEN: "mockToken" }; const mockData = { @@ -21,20 +19,22 @@ describe("sendTaskUpdatesHandler", () => { blockers: "NA", userName: "12345678910", taskId: "79wMEIek990", + taskTitle: "Hyperlink as task Title", }, }; afterEach(() => { jest.clearAllMocks(); }); - it("sendTaskUpdate function should return undefined after successfully sending the message", async () => { - const { completed, planned, blockers, userName, taskId } = mockData.content; + const { completed, planned, blockers, userName, taskId, taskTitle } = + mockData.content; const response = await sendTaskUpdate( completed, planned, blockers, userName, taskId, + taskTitle, mockEnv ); expect(response).toBe(undefined); @@ -50,4 +50,22 @@ describe("sendTaskUpdatesHandler", () => { expect(result.status).toBe(401); expect(jsonResponse).toEqual(response.UNAUTHORIZED); }); + it("should return success response if task update is sent successfully", async () => { + const mockRequest = generateDummyRequestObject({ + url: "/task/update", + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer dummyToken", + }, + }); + mockRequest.json = jest.fn().mockResolvedValue(mockData); + const result: JSONResponse = await sendTaskUpdatesHandler( + mockRequest, + mockEnv + ); + expect(result.status).toBe(200); + const res: JSONResponse = await result.json(); + expect(res).toBe(response.TASK_UPDATE_SENT_MESSAGE); + }); }); diff --git a/tests/unit/utils/sendTasksUpdates.test.ts b/tests/unit/utils/sendTasksUpdates.test.ts index 1ddfbb4d..6ffb6c08 100644 --- a/tests/unit/utils/sendTasksUpdates.test.ts +++ b/tests/unit/utils/sendTasksUpdates.test.ts @@ -9,6 +9,7 @@ describe("sendTaskUpdate function", () => { const blockers = "No blockers"; const userName = "Tejas"; const taskId = "69nduIn210"; + const taskTitle = "Hyperlink as task title"; const taskUrl = config(mockEnv).RDS_STATUS_SITE_URL + `/tasks/${taskId}`; const assertFetchCall = (url: string, bodyObj: any, mockEnv: any) => { expect(global.fetch).toHaveBeenCalledWith(url, { @@ -24,11 +25,42 @@ describe("sendTaskUpdate function", () => { afterEach(() => { jest.clearAllMocks(); }); + test("should throw an error if response status is not OK", async () => { + const url = config(mockEnv).TRACKING_CHANNEL_URL; + const formattedString = + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + + `\n**Completed**\n${completed}\n\n` + + `**Planned**\n${planned}\n\n` + + `**Blockers**\n${blockers}`; + const bodyObj = { + content: formattedString, + }; + + jest + .spyOn(global, "fetch") + .mockResolvedValueOnce( + new JSONResponse("", { status: 400, statusText: "Bad Request" }) + ); + + await expect( + sendTaskUpdate( + completed, + planned, + blockers, + userName, + taskId, + taskTitle, + mockEnv + ) + ).rejects.toThrowError("Failed to send task update: 400 - Bad Request"); + + assertFetchCall(url, bodyObj, mockEnv); + }); test("should send the task update to discord tracking channel when all fields are present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n${completed}\n\n` + `**Planned**\n${planned}\n\n` + `**Blockers**\n${blockers}`; @@ -46,6 +78,7 @@ describe("sendTaskUpdate function", () => { blockers, userName, taskId, + taskTitle, mockEnv ); @@ -55,7 +88,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only completed is present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n${completed}\n\n` + `**Planned**\n\n\n` + `**Blockers**\n`; @@ -67,7 +100,15 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate(completed, "", "", userName, taskId, mockEnv); + await sendTaskUpdate( + completed, + "", + "", + userName, + taskId, + taskTitle, + mockEnv + ); assertFetchCall(url, bodyObj, mockEnv); }); @@ -75,7 +116,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only planned is present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n\n\n` + `**Planned**\n${planned}\n\n` + `**Blockers**\n`; @@ -87,7 +128,7 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate("", planned, "", userName, taskId, mockEnv); + await sendTaskUpdate("", planned, "", userName, taskId, taskTitle, mockEnv); assertFetchCall(url, bodyObj, mockEnv); }); @@ -95,7 +136,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only blockers is present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n\n\n` + `**Planned**\n\n\n` + `**Blockers**\n${blockers}`; @@ -107,7 +148,15 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate("", "", blockers, userName, taskId, mockEnv); + await sendTaskUpdate( + "", + "", + blockers, + userName, + taskId, + taskTitle, + mockEnv + ); assertFetchCall(url, bodyObj, mockEnv); }); @@ -115,7 +164,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only completed and planned are present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n${completed}\n\n` + `**Planned**\n${planned}\n\n` + `**Blockers**\n`; @@ -127,7 +176,15 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate(completed, planned, "", userName, taskId, mockEnv); + await sendTaskUpdate( + completed, + planned, + "", + userName, + taskId, + taskTitle, + mockEnv + ); assertFetchCall(url, bodyObj, mockEnv); }); @@ -135,7 +192,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only completed and blockers are present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n${completed}\n\n` + `**Planned**\n\n\n` + `**Blockers**\n${blockers}`; @@ -147,7 +204,15 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate(completed, "", blockers, userName, taskId, mockEnv); + await sendTaskUpdate( + completed, + "", + blockers, + userName, + taskId, + taskTitle, + mockEnv + ); assertFetchCall(url, bodyObj, mockEnv); }); @@ -155,7 +220,7 @@ describe("sendTaskUpdate function", () => { test("should send the task update to discord tracking channel when only planned and blockers are present", async () => { const url = config(mockEnv).TRACKING_CHANNEL_URL; const formattedString = - `${userName} added an update to their task: <${taskUrl}>\n` + + `**${userName}** added an update to their task: [${taskTitle}](<${taskUrl}>)\n` + `\n**Completed**\n\n\n` + `**Planned**\n${planned}\n\n` + `**Blockers**\n${blockers}`; @@ -167,7 +232,15 @@ describe("sendTaskUpdate function", () => { .spyOn(global, "fetch") .mockImplementation(() => Promise.resolve(new JSONResponse(""))); - await sendTaskUpdate("", planned, blockers, userName, taskId, mockEnv); + await sendTaskUpdate( + "", + planned, + blockers, + userName, + taskId, + taskTitle, + mockEnv + ); assertFetchCall(url, bodyObj, mockEnv); });