Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/controllers/member.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,18 @@ 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;
}

await memberService.updateMember(memberId, body);

const updatedData = await memberService.getDetails(memberId);
res
.status(200)
.json({ success: true, message: "Updated member details successfully" });
.json({ success: true, user: updatedData });
};

// Get all unapproved members
Expand Down
30 changes: 15 additions & 15 deletions src/routes/members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -113,37 +113,37 @@ 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
*
* @apiParam (URL Params) {String} memberId Member ID.
*
* @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
* @api {get} /api/members/:memberId/projects Get member's projects
* @apiName GetUserProjects
* @apiGroup Member
*
* @apiParam (URL Params) {String} memberId Member ID.
*
* @apiSuccess {Object[]} projects List of projects.
*/
router.get("/projects/:memberId", memberCtrl.getUserProjects);
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
*
* @apiParam (URL Params) {String} memberId Member ID.
*
* @apiSuccess {Object[]} interviews List of interviews.
*/
router.get("/interviews/:memberId", memberCtrl.getUserInterviews);
router.get("/:memberId/interviews", memberCtrl.getUserInterviews);

return router;
}
74 changes: 53 additions & 21 deletions tests/Member.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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' },
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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)
.mockResolvedValueOnce('https://new.url/image.png');

jest.spyOn(memberService, 'getDetails')
.mockResolvedValueOnce(oldMember)
.mockResolvedValueOnce(updatedMember);

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,
});
});
});
});