From a7e44d784ec88607ce94507bc1b5aaf10f6cd564 Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Mon, 28 Jul 2025 01:45:19 +0530 Subject: [PATCH 1/2] Some fixes in update acheivement controller --- src/controllers/achievement.controller.ts | 37 +++++++++++------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/controllers/achievement.controller.ts b/src/controllers/achievement.controller.ts index cf822f8..b7e176d 100644 --- a/src/controllers/achievement.controller.ts +++ b/src/controllers/achievement.controller.ts @@ -73,6 +73,7 @@ export const createAchievement = async (req: Request, res: Response) => { export const updateAchievementById = async (req: Request, res: Response) => { const achievementId = parseInt(req.params.achievementId); + if (!achievementId || isNaN(achievementId)) { throw new ApiError("Invalid achievement ID", 400); } @@ -81,7 +82,7 @@ export const updateAchievementById = async (req: Request, res: Response) => { let imageUrl: string | undefined; let achievementData = req.body.achievementData; - if (typeof achievementData === 'string') { + if (typeof achievementData === "string") { try { achievementData = JSON.parse(achievementData); } catch (e) { @@ -95,33 +96,31 @@ export const updateAchievementById = async (req: Request, res: Response) => { throw new ApiError("updatedById is required", 400); } - const existingAchievement = await achievementService.getAchievementById(achievementId); if (!existingAchievement) { throw new ApiError("Achievement not found", 404); } - + if (file) { - imageUrl = await uploadImage(supabase, file, 'achievements', existingAchievement.imageUrl ); + imageUrl = await uploadImage(supabase, file, "achievements", existingAchievement.imageUrl); } - + + const { memberIds: _, ...updatePayload } = achievementData; + if (imageUrl) { - achievementData.imageUrl = imageUrl; - } - - if ( - !title && - !description && - !achievedAt && - !imageUrl && - (!Array.isArray(memberIds) || memberIds.length === 0) - ) { - throw new ApiError("At least one field must be provided for update", 400); - } - + updatePayload.imageUrl = imageUrl; + } + + const hasSomethingToUpdate = + title || description || achievedAt || imageUrl || (Array.isArray(memberIds) && memberIds.length > 0); + + if (!hasSomethingToUpdate) { + throw new ApiError("At least one field (title, description, achievedAt, image, or memberIds) must be provided for update", 400); + } + const updatedAchievement = await achievementService.updateAchievementById( achievementId, - achievementData + updatePayload ); if (Array.isArray(memberIds) && memberIds.length > 0) { From 86fd7204bb983d4f8413256fd44db295feaa56b0 Mon Sep 17 00:00:00 2001 From: Sherin Thomas Date: Tue, 29 Jul 2025 16:41:49 +0530 Subject: [PATCH 2/2] Some changes in update achievements test --- tests/Achievement.test.ts | 196 +++++++++++++++++++++++++++----------- 1 file changed, 140 insertions(+), 56 deletions(-) diff --git a/tests/Achievement.test.ts b/tests/Achievement.test.ts index ddafbbb..726a2ed 100644 --- a/tests/Achievement.test.ts +++ b/tests/Achievement.test.ts @@ -292,53 +292,7 @@ describe('updateAchievementById', () => { updatedAt: new Date(), createdBy: { id: 'admin_123', name: 'Admin' }, updatedBy: null, - members: [ - { - member: { - id: 'user_1', - name: 'User One', - email: 'user1@example.com', - profilePhoto: null, - }, - }, - ], - }; - - const updatedAchievement = { - ...baseAchievement, - title: 'Updated Title', - updatedById: 'admin_456', - updatedBy: { - id: 'admin_456', - name: 'Admin Two', - email: 'admin2@example.com', - }, - imageUrl: 'https://example.com/uploaded/updated.png', - members: [ - { - member: { - id: 'user_2', - name: 'User Two', - email: 'user2@example.com', - profilePhoto: null, - }, - }, - ], - }; - - const mockReq: any = { - params: { achievementId: '1' }, - file: { - originalname: 'updated.png', - buffer: Buffer.from('test-image'), - }, - body: { - achievementData: JSON.stringify({ - title: 'Updated Title', - updatedById: 'admin_456', - memberIds: ['user_2'], - }), - }, + members: [], }; const mockRes: any = { @@ -346,8 +300,25 @@ describe('updateAchievementById', () => { json: jest.fn(), }; - it('should return 200 and updated achievement', async () => { - mockedUploadImage.mockResolvedValue('https://example.com/uploaded/updated.png'); + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should update all fields successfully (200)', async () => { + const updatedAchievement = { ...baseAchievement, title: 'Updated', updatedById: 'admin_456' }; + const mockReq: any = { + params: { achievementId: '1' }, + file: { originalname: 'img.png', buffer: Buffer.from('123') }, + body: { + achievementData: JSON.stringify({ + title: 'Updated', + updatedById: 'admin_456', + memberIds: ['user_2'], + }), + }, + }; + + mockedUploadImage.mockResolvedValue('https://updated.com/img.png'); jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(baseAchievement); jest.spyOn(achievementService, 'updateAchievementById').mockResolvedValue(updatedAchievement); jest.spyOn(achievementService, 'addMembersToAchievement').mockResolvedValue(undefined); @@ -355,19 +326,132 @@ describe('updateAchievementById', () => { await updateAchievementById(mockReq, mockRes); expect(mockedUploadImage).toHaveBeenCalled(); - expect(achievementService.getAchievementById).toHaveBeenCalledWith(1); - expect(achievementService.updateAchievementById).toHaveBeenCalledWith(1, expect.objectContaining({ - title: 'Updated Title', - updatedById: 'admin_456', - imageUrl: 'https://example.com/uploaded/updated.png', - })); - expect(achievementService.addMembersToAchievement).toHaveBeenCalledWith(1, ['user_2']); expect(mockRes.status).toHaveBeenCalledWith(200); expect(mockRes.json).toHaveBeenCalledWith({ success: true, data: updatedAchievement, }); }); + + it('should update only title', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + body: { + achievementData: JSON.stringify({ title: 'New Title', updatedById: 'admin_456' }), + }, + }; + + jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(baseAchievement); + jest.spyOn(achievementService, 'updateAchievementById').mockResolvedValue({ + ...baseAchievement, + title: 'New Title', + }); + + await updateAchievementById(mockReq, mockRes); + expect(achievementService.updateAchievementById).toHaveBeenCalledWith(1, expect.objectContaining({ + title: 'New Title', + updatedById: 'admin_456', + })); + }); + + it('should update only image', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + file: { originalname: 'img.png', buffer: Buffer.from('abc') }, + body: { + achievementData: JSON.stringify({ updatedById: 'admin_456' }), + }, + }; + + mockedUploadImage.mockResolvedValue('https://updated.com/img.png'); + jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(baseAchievement); + jest.spyOn(achievementService, 'updateAchievementById').mockResolvedValue({ + ...baseAchievement, + imageUrl: 'https://updated.com/img.png', + updatedById: 'admin_456', + }); + + await updateAchievementById(mockReq, mockRes); + expect(mockedUploadImage).toHaveBeenCalled(); + }); + + it('should update only memberIds', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + body: { + achievementData: JSON.stringify({ updatedById: 'admin_456', memberIds: ['user_3'] }), + }, + }; + + jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(baseAchievement); + jest.spyOn(achievementService, 'updateAchievementById').mockResolvedValue({ + ...baseAchievement, + updatedById: 'admin_456', + }); + jest.spyOn(achievementService, 'addMembersToAchievement').mockResolvedValue(undefined); + + await updateAchievementById(mockReq, mockRes); + expect(achievementService.addMembersToAchievement).toHaveBeenCalledWith(1, ['user_3']); + }); + + it('should return 400 if updatedById is missing', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + body: { + achievementData: JSON.stringify({ title: 'No Updater' }), + }, + }; + + await expect(updateAchievementById(mockReq, mockRes)).rejects.toThrow(ApiError); + }); + + it('should return 400 if achievementId is invalid', async () => { + const mockReq: any = { + params: { achievementId: 'abc' }, + body: { + achievementData: JSON.stringify({ title: 'Updated', updatedById: 'admin_456' }), + }, + }; + + await expect(updateAchievementById(mockReq, mockRes)).rejects.toThrow(ApiError); + }); + + it('should return 404 if achievement not found', async () => { + const mockReq: any = { + params: { achievementId: '999' }, + body: { + achievementData: JSON.stringify({ title: 'Missing', updatedById: 'admin_456' }), + }, + }; + + jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(null); + + await expect(updateAchievementById(mockReq, mockRes)).rejects.toThrow(ApiError); + }); + + it('should return 400 if no fields are provided for update', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + body: { + achievementData: JSON.stringify({ updatedById: 'admin_456' }), + }, + }; + + jest.spyOn(achievementService, 'getAchievementById').mockResolvedValue(baseAchievement); + + await expect(updateAchievementById(mockReq, mockRes)).rejects.toThrow(ApiError); + }); + + it('should return 400 if achievementData is invalid JSON', async () => { + const mockReq: any = { + params: { achievementId: '1' }, + body: { + achievementData: '{ invalid JSON }', + }, + }; + + await expect(updateAchievementById(mockReq, mockRes)).rejects.toThrow(ApiError); + }); }); const mockedDeleteImage = deleteImage as jest.Mock;