From 83796c6be39775e873365539159d0ab6d76b3597 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 17:04:40 +0530 Subject: [PATCH 1/6] Changed few member routes and updated updateMember route to send user data --- src/controllers/member.controller.ts | 3 ++- src/routes/members.ts | 24 ++++++++++++------------ src/server.ts | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/controllers/member.controller.ts b/src/controllers/member.controller.ts index 642088e..601ead7 100644 --- a/src/controllers/member.controller.ts +++ b/src/controllers/member.controller.ts @@ -90,9 +90,10 @@ export const updateAMember = await memberService.updateMember(memberId, body); + const updatedData = memberService.getDetails(memberId); res .status(200) - .json({ success: true, message: "Updated member details successfully" }); + .json({ success: true, user: updatedData }); }; // Get all unapproved members diff --git a/src/routes/members.ts b/src/routes/members.ts index e1653fb..e7b4b49 100644 --- a/src/routes/members.ts +++ b/src/routes/members.ts @@ -10,6 +10,15 @@ export default function membersRouter( ) { const router = express.Router(); + /** + * @api {get} /members/unapproved Get unapproved members + * @apiName GetUnapprovedMembers + * @apiGroup Member + * + * @apiSuccess {Object[]} unapprovedMembers List of unapproved members. + */ + router.get("/unapproved", memberCtrl.getUnapprovedMembers); + /** * @api {get} /members/:memberId Get a member's details * @apiName GetUserDetails @@ -89,15 +98,6 @@ export default function membersRouter( memberCtrl.updateAMember(supabase), ); - /** - * @api {get} /members/unapproved Get unapproved members - * @apiName GetUnapprovedMembers - * @apiGroup Member - * - * @apiSuccess {Object[]} unapprovedMembers List of unapproved members. - */ - router.get("/unapproved", memberCtrl.getUnapprovedMembers); - /** * @api {patch} /members/approve/:memberId Approve/reject a member * @apiName UpdateApprovalRequest @@ -121,7 +121,7 @@ export default function membersRouter( * * @apiSuccess {Object[]} achievements List of achievements. */ - router.get("/achievements/:memberId", memberCtrl.getUserAchievements); + router.get("/:memberId/achievements", memberCtrl.getUserAchievements); /** * @api {get} /api/members/projects/:memberId Get member's projects @@ -132,7 +132,7 @@ export default function membersRouter( * * @apiSuccess {Object[]} projects List of projects. */ - router.get("/projects/:memberId", memberCtrl.getUserProjects); + router.get("/:memberId/achievements", memberCtrl.getUserProjects); /** * @api {get} /members/interviews/:memberId Get member's interviews @@ -143,7 +143,7 @@ export default function membersRouter( * * @apiSuccess {Object[]} interviews List of interviews. */ - router.get("/interviews/:memberId", memberCtrl.getUserInterviews); + router.get("/:memberId/interviews", memberCtrl.getUserInterviews); return router; } \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 72a3c3f..54dc55e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,7 +2,7 @@ import app from "./app"; import config from "./config"; -const port = config.PORT || 3000; +const port = 8000; app.listen(port, () => { console.log(`🚀 API server listening on http://localhost:${port}/api/v1`); From a03d86b959155beade405f1365c2d5d81c0b7f70 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 17:06:26 +0530 Subject: [PATCH 2/6] Added config port --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index 54dc55e..72a3c3f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,7 +2,7 @@ import app from "./app"; import config from "./config"; -const port = 8000; +const port = config.PORT || 3000; app.listen(port, () => { console.log(`🚀 API server listening on http://localhost:${port}/api/v1`); From 7f059e220bd74b8fcc6649acd4d0a9bc16e32af5 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 17:26:07 +0530 Subject: [PATCH 3/6] Updated tests and optimized image updation --- src/controllers/member.controller.ts | 4 +- tests/Member.test.ts | 74 ++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/controllers/member.controller.ts b/src/controllers/member.controller.ts index 601ead7..267622f 100644 --- a/src/controllers/member.controller.ts +++ b/src/controllers/member.controller.ts @@ -82,7 +82,7 @@ export const updateAMember = const oldData = await memberService.getDetails(memberId); const oldImage = oldData?.profilePhoto; - if(oldImage) await deleteImage(supabase, oldImage); + if(oldImage) await uploadImage(supabase, req.file, "members", oldImage); const imageUrl = await uploadImage(supabase, req.file, "members"); body.profilePhoto = imageUrl; @@ -90,7 +90,7 @@ export const updateAMember = await memberService.updateMember(memberId, body); - const updatedData = memberService.getDetails(memberId); + const updatedData = await memberService.getDetails(memberId); res .status(200) .json({ success: true, user: updatedData }); diff --git a/tests/Member.test.ts b/tests/Member.test.ts index 8a531e1..7b947c9 100644 --- a/tests/Member.test.ts +++ b/tests/Member.test.ts @@ -3,7 +3,7 @@ import { createAMember, updateAMember } from '../src/controllers/member.controll import * as memberService from '../src/services/member.service'; import { ApiError } from '../src/utils/apiError'; import { SupabaseClient } from '@supabase/supabase-js'; -import { uploadImage } from '../src/utils/imageUtils'; +import { uploadImage, deleteImage } from '../src/utils/imageUtils'; jest.mock('../src/db/client', () => ({ prisma: { @@ -68,7 +68,7 @@ describe('Member Controller - updateAMember', () => { jest.clearAllMocks(); }); - it('should update member and respond with 200', async () => { + it('should update member and return updated data (no image)', async () => { const req = { params: { memberId: 'abc-123' }, body: { github: 'https://github.com/shrutii' }, @@ -77,14 +77,14 @@ describe('Member Controller - updateAMember', () => { const res = mockResponse(); - const mockMember = { + const updatedMember = { id: '123', name: 'Test User', email: 'test@example.com', phone: null, bio: null, profilePhoto: null, - github: 'testgithub', + github: 'https://github.com/shrutii', linkedin: null, twitter: null, leetcode: null, @@ -100,38 +100,38 @@ describe('Member Controller - updateAMember', () => { updatedAt: new Date(), }; - const spy = jest.spyOn(memberService, 'updateMember').mockResolvedValue(mockMember); + const spyUpdate = jest.spyOn(memberService, 'updateMember').mockResolvedValue(updatedMember); + const spyGet = jest.spyOn(memberService, 'getDetails').mockResolvedValue(updatedMember); const handler = updateAMember(mockSupabase); await handler(req, res); - expect(spy).toHaveBeenCalledWith('abc-123', req.body); + expect(spyUpdate).toHaveBeenCalledWith('abc-123', req.body); + expect(spyGet).toHaveBeenCalledTimes(1); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, - message: 'Updated member details successfully', + user: updatedMember, }); }); - it('should upload image and update profilePhoto', async () => { + it('should upload new image, handle old image, update member, and return updated data', async () => { const req = { params: { memberId: 'abc-123' }, body: {}, - file: { buffer: Buffer.from('data') }, + file: { buffer: Buffer.from('fake-image-data') }, } as unknown as Request; const res = mockResponse(); - (uploadImage as jest.Mock).mockResolvedValue('http://image.url'); - - const mockMember = { + const oldMember = { id: '123', - name: 'Test User', - email: 'test@example.com', + name: 'Old User', + email: 'old@example.com', phone: null, bio: null, - profilePhoto: 'http://image.url', - github: 'testgithub', + profilePhoto: 'https://old.url/image.png', + github: null, linkedin: null, twitter: null, leetcode: null, @@ -147,17 +147,49 @@ describe('Member Controller - updateAMember', () => { updatedAt: new Date(), }; - const spy = jest.spyOn(memberService, 'updateMember').mockResolvedValue(mockMember); + const updatedMember = { + ...oldMember, + profilePhoto: 'https://new.url/image.png', + }; + + (uploadImage as jest.Mock) + .mockResolvedValueOnce(undefined) // first call with oldImage (does nothing) + .mockResolvedValueOnce('https://new.url/image.png'); // actual upload + + jest.spyOn(memberService, 'getDetails') + .mockResolvedValueOnce(oldMember) // get old data (with image) + .mockResolvedValueOnce(updatedMember); // get updated data + + const spyUpdate = jest + .spyOn(memberService, 'updateMember') + .mockResolvedValue(updatedMember); const handler = updateAMember(mockSupabase); await handler(req, res); - expect(uploadImage).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith('abc-123', { profilePhoto: 'http://image.url' }); + expect(uploadImage).toHaveBeenNthCalledWith( + 1, + mockSupabase, + req.file, + 'members', + 'https://old.url/image.png' + ); + + expect(uploadImage).toHaveBeenNthCalledWith( + 2, + mockSupabase, + req.file, + 'members' + ); + + expect(spyUpdate).toHaveBeenCalledWith('abc-123', { + profilePhoto: 'https://new.url/image.png', + }); + expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, - message: 'Updated member details successfully', + user: updatedMember, }); }); -}); +}); \ No newline at end of file From 8c454fbbf31d82d19f69220c1db411a6b64d5654 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 17:27:36 +0530 Subject: [PATCH 4/6] Fixed project routes --- src/routes/members.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/members.ts b/src/routes/members.ts index e7b4b49..7203a15 100644 --- a/src/routes/members.ts +++ b/src/routes/members.ts @@ -132,7 +132,7 @@ export default function membersRouter( * * @apiSuccess {Object[]} projects List of projects. */ - router.get("/:memberId/achievements", memberCtrl.getUserProjects); + router.get("/:memberId/projects", memberCtrl.getUserProjects); /** * @api {get} /members/interviews/:memberId Get member's interviews From 837d60b97d4a9ac4755fb359b01bf9c255a0a020 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 17:56:08 +0530 Subject: [PATCH 5/6] Updated annotations --- src/routes/members.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/members.ts b/src/routes/members.ts index 7203a15..0d0042e 100644 --- a/src/routes/members.ts +++ b/src/routes/members.ts @@ -113,7 +113,7 @@ export default function membersRouter( router.patch("/approve/:memberId", memberCtrl.updateRequest); /** - * @api {get} /members/achievements/:memberId Get member's achievements + * @api {get} /members/:memberId/achievements Get member's achievements * @apiName GetUserAchievements * @apiGroup Member * @@ -124,7 +124,7 @@ export default function membersRouter( router.get("/:memberId/achievements", memberCtrl.getUserAchievements); /** - * @api {get} /api/members/projects/:memberId Get member's projects + * @api {get} /api/members/:memberId/projects Get member's projects * @apiName GetUserProjects * @apiGroup Member * @@ -135,7 +135,7 @@ export default function membersRouter( router.get("/:memberId/projects", memberCtrl.getUserProjects); /** - * @api {get} /members/interviews/:memberId Get member's interviews + * @api {get} /members/:memberId/interviews Get member's interviews * @apiName GetUserInterviews * @apiGroup Member * From 4a54c93ac923728af61bc4ab2681a257b4056114 Mon Sep 17 00:00:00 2001 From: shrutiii Date: Tue, 29 Jul 2025 18:00:15 +0530 Subject: [PATCH 6/6] removed comments --- tests/Member.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Member.test.ts b/tests/Member.test.ts index 7b947c9..26860df 100644 --- a/tests/Member.test.ts +++ b/tests/Member.test.ts @@ -153,12 +153,12 @@ describe('Member Controller - updateAMember', () => { }; (uploadImage as jest.Mock) - .mockResolvedValueOnce(undefined) // first call with oldImage (does nothing) - .mockResolvedValueOnce('https://new.url/image.png'); // actual upload + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce('https://new.url/image.png'); jest.spyOn(memberService, 'getDetails') - .mockResolvedValueOnce(oldMember) // get old data (with image) - .mockResolvedValueOnce(updatedMember); // get updated data + .mockResolvedValueOnce(oldMember) + .mockResolvedValueOnce(updatedMember); const spyUpdate = jest .spyOn(memberService, 'updateMember')