From 425b8ffa8ba20843c22470724ddd53ccb23dce78 Mon Sep 17 00:00:00 2001 From: oyedeletemitope Date: Wed, 14 May 2025 12:56:53 -0700 Subject: [PATCH 1/4] added unit test for the first part of the user module --- src/users/users.controller.spec.ts | 288 ++++++++++++++++++++++++++++- src/users/users.service.ts | 7 +- 2 files changed, 288 insertions(+), 7 deletions(-) diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index a44f5a0..8ae8db9 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -545,7 +545,6 @@ describe('UsersAdminController', () => { it('should request new verification token', async () => { const mockReq = { user: createUserMock() }; - // Mocking the requestVerification method to resolve successfully jest .spyOn(usersService, 'requestVerification') .mockResolvedValue(undefined); // or just don't mock return value @@ -653,7 +652,6 @@ describe('UsersAdminController', () => { const statusDto = { status: UserStatus.ACTIVE }; const mockReq = { user: createUserMock() }; - // Mocking the service to throw an error when the user is not found jest .spyOn(usersService, 'updateStatus') .mockRejectedValue(new NotFoundException('User not found')); @@ -785,4 +783,290 @@ describe('UsersAdminController', () => { }); }); }); + + describe('Deactivate User Account', () => { + const userId = new Types.ObjectId().toString(); + const deactivateDto = { reason: 'Taking a break' }; + + it('should deactivate a user account', async () => { + const mockReq = { user: createUserMock() }; + const expectedResult = createUserMock({ status: UserStatus.DEACTIVATED }); + + jest + .spyOn(usersService, 'deactivateAccount') + .mockResolvedValue(expectedResult); + + const result = await controller.deactivateAccount( + mockReq, + userId, + deactivateDto, + ); + + expect(result).toEqual(expectedResult); + expect(usersService.deactivateAccount).toHaveBeenCalledWith(userId); + }); + + it('should throw NotFoundException when user not found', async () => { + const mockReq = { user: createUserMock() }; + + jest + .spyOn(usersService, 'deactivateAccount') + .mockRejectedValue( + new NotFoundException(`User with ID ${userId} not found`), + ); + + await expect( + controller.deactivateAccount(mockReq, userId, deactivateDto), + ).rejects.toThrow(NotFoundException); + }); + + it('should throw BadRequestException when user cannot be deactivated', async () => { + const mockReq = { user: createUserMock() }; + + jest + .spyOn(usersService, 'deactivateAccount') + .mockRejectedValue( + new BadRequestException( + 'User account cannot be deactivated at this time', + ), + ); + + await expect( + controller.deactivateAccount(mockReq, userId, deactivateDto), + ).rejects.toThrow(BadRequestException); + }); + }); + + describe('Request Reactivation', () => { + const userId = new Types.ObjectId().toString(); + const reactivationDto = { message: 'Ready to come back' }; + + it('should request reactivation of a user account', async () => { + const expectedResult = createUserMock({ status: UserStatus.ACTIVE }); + + jest + .spyOn(usersService, 'requestReactivation') + .mockResolvedValue(expectedResult); + + const result = await controller.requestReactivation( + userId, + reactivationDto, + ); + + expect(result).toEqual(expectedResult); + expect(usersService.requestReactivation).toHaveBeenCalledWith(userId); + }); + + it('should throw NotFoundException when user not found', async () => { + jest + .spyOn(usersService, 'requestReactivation') + .mockRejectedValue( + new NotFoundException(`User with ID ${userId} not found`), + ); + + await expect( + controller.requestReactivation(userId, reactivationDto), + ).rejects.toThrow(NotFoundException); + }); + + it('should throw BadRequestException when user cannot be reactivated', async () => { + jest + .spyOn(usersService, 'requestReactivation') + .mockRejectedValue( + new BadRequestException( + 'User account cannot be reactivated at this time', + ), + ); + + await expect( + controller.requestReactivation(userId, reactivationDto), + ).rejects.toThrow(BadRequestException); + }); + }); + + describe('Lead Registration', () => { + const tempLeadDto = { + email: 'lead@example.com', + leadPosition: 'Senior Developer', + firstName: 'John', + lastName: 'Doe', + createdAt: new Date(), + }; + + it('should successfully register a temporary lead', async () => { + const expectedResult = 'Application sent'; + + jest + .spyOn(usersService, 'createTempRegistration') + .mockResolvedValue(expectedResult); + + const result = await controller.createLead(tempLeadDto); + + expect(result).toEqual(expectedResult); + expect(usersService.createTempRegistration).toHaveBeenCalledWith( + tempLeadDto.email, + tempLeadDto.leadPosition, + ); + }); + + it('should throw BadRequestException when lead registration is not allowed yet', async () => { + const errorMessage = + 'The next time you can apply as a lead is Monday, January 1st, 10:00 am'; + + jest + .spyOn(usersService, 'createTempRegistration') + .mockRejectedValue(new BadRequestException(errorMessage)); + + await expect(controller.createLead(tempLeadDto)).rejects.toThrow( + new BadRequestException(errorMessage), + ); + }); + + it('should handle errors during lead registration', async () => { + jest + .spyOn(usersService, 'createTempRegistration') + .mockRejectedValue(new Error('Error updating user')); + + await expect(controller.createLead(tempLeadDto)).rejects.toThrow(Error); + }); + }); + + describe('Register New User Form', () => { + const encryptedData = 'encryptedString'; + const userId = new Types.ObjectId().toString(); + const email = 'user@example.com'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should redirect to new user form when userId is missing', async () => { + jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ + userId: '', + email, + }); + + const result = await controller.register(encryptedData); + + expect(result).toEqual({ + url: `/leads/new-user-form?email=${encodeURIComponent(email)}`, + }); + }); + + it('should redirect to create lead page when user exists', async () => { + const mockUser = createUserMock({ + _id: new Types.ObjectId(userId), + email, + }); + + jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ + userId, + email, + }); + + jest.spyOn(usersService, 'findById').mockResolvedValue(mockUser); + + const result = await controller.register(encryptedData); + + expect(result).toEqual({ + url: `/leads/createLead?email=${email}`, + }); + expect(usersService.findById).toHaveBeenCalledWith(userId); + }); + + it('should throw NotFoundException when user does not exist', async () => { + jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ + userId, + email, + }); + + jest + .spyOn(usersService, 'findById') + .mockRejectedValue( + new NotFoundException(`User with ID ${userId} not found`), + ); + + await expect(controller.register(encryptedData)).rejects.toThrow( + new NotFoundException('Invalid link'), + ); + }); + + it('should throw NotFoundException when decryption fails', async () => { + jest + .spyOn(usersService, 'paraseEncryptedParams') + .mockImplementation(() => { + throw new Error('Decryption failed'); + }); + + await expect(controller.register(encryptedData)).rejects.toThrow( + new NotFoundException('Invalid link'), + ); + }); + }); + + describe('User Invite Link', () => { + const encryptedData = 'encryptedInviteString'; + const userId = new Types.ObjectId().toString(); + const email = 'invite@example.com'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should process an invite link and redirect to the appropriate page', async () => { + const mockUser = createUserMock({ + _id: new Types.ObjectId(userId), + email, + }); + + jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ + userId, + email, + }); + + jest.spyOn(usersService, 'findById').mockResolvedValue(mockUser); + + const result = await controller.register(encryptedData); + + expect(result).toEqual({ + url: `/leads/createLead?email=${email}`, + }); + expect(usersService.paraseEncryptedParams).toHaveBeenCalledWith( + encryptedData, + ); + }); + + it('should handle invite link with missing parameters correctly', async () => { + jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ + userId: '', + email, + }); + + const result = await controller.register(encryptedData); + + expect(result).toEqual({ + url: `/leads/new-user-form?email=${encodeURIComponent(email)}`, + }); + }); + + it('should test the parameter parsing function', () => { + // Since this is a specific function used by the endpoint, we should test it + // This would typically be in a separate test for the service, but including here for completeness + + // Mock the decrypt function since it's an external dependency + const decryptMock = jest + .fn() + .mockReturnValue('userId=123&email=test@example.com'); + global.decrypt = decryptMock; + + const encryptedParams = 'encoded%20string'; + + // We would need to actually inject the real service to test this + // const result = usersService.paraseEncryptedParams(encryptedParams); + + // Instead, we can verify the mock was called correctly + // expect(result).toEqual({ userId: '123', email: 'test@example.com' }); + // expect(decryptMock).toHaveBeenCalledWith('encoded string'); + }); + }); }); diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 2918c29..53bdf76 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -467,7 +467,6 @@ export class UsersService { async createUser(userData: CreateUserDto) { return (this.userModel as any).signUp(userData); } - async requestVerification(req: ApiReq, userId: string) { const user = await this.userModel.findById(userId); if (!user) { @@ -490,12 +489,10 @@ export class UsersService { throw new BadRequestException('You are already verified.'); } - // Update user state user.applicationStatus = ApplicationStatus.PENDING; - user.nextVerificationRequestDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days later (this was initially 3 months) + user.nextVerificationRequestDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days later await user.save(); - // Send confirmation to user await sendMail({ to: user.email, from: EmailFromType.HELLO, @@ -506,7 +503,7 @@ export class UsersService { nextTryDate: format(user.nextVerificationRequestDate, 'PPPP'), }, }); - + return { message: 'Verification request sent.', nextAllowedRequest: user.nextVerificationRequestDate, From f0586eef3e12969fce515d147164fba86b8b72f3 Mon Sep 17 00:00:00 2001 From: oyedeletemitope Date: Wed, 21 May 2025 09:00:53 -0700 Subject: [PATCH 2/4] added the comments back --- src/users/users.controller.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index 8ae8db9..3b8cfc9 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -544,7 +544,7 @@ describe('UsersAdminController', () => { it('should request new verification token', async () => { const mockReq = { user: createUserMock() }; - + // Mocking the requestVerification method to resolve successfully jest .spyOn(usersService, 'requestVerification') .mockResolvedValue(undefined); // or just don't mock return value @@ -651,7 +651,7 @@ describe('UsersAdminController', () => { it('should throw error when updating non-existent user', async () => { const statusDto = { status: UserStatus.ACTIVE }; const mockReq = { user: createUserMock() }; - + // Mocking the service to throw an error when the user is not found jest .spyOn(usersService, 'updateStatus') .mockRejectedValue(new NotFoundException('User not found')); From 6f5334afa0aa45b556169f6d8f3764f78e123bda Mon Sep 17 00:00:00 2001 From: oyedeletemitope Date: Thu, 22 May 2025 00:50:46 -0700 Subject: [PATCH 3/4] removed unwanted comments --- src/users/users.controller.spec.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index 3b8cfc9..8f8c8b9 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -1050,23 +1050,12 @@ describe('UsersAdminController', () => { }); it('should test the parameter parsing function', () => { - // Since this is a specific function used by the endpoint, we should test it - // This would typically be in a separate test for the service, but including here for completeness - - // Mock the decrypt function since it's an external dependency const decryptMock = jest .fn() .mockReturnValue('userId=123&email=test@example.com'); global.decrypt = decryptMock; const encryptedParams = 'encoded%20string'; - - // We would need to actually inject the real service to test this - // const result = usersService.paraseEncryptedParams(encryptedParams); - - // Instead, we can verify the mock was called correctly - // expect(result).toEqual({ userId: '123', email: 'test@example.com' }); - // expect(decryptMock).toHaveBeenCalledWith('encoded string'); }); }); }); From 162649f58162842f284eda230aa079e932d8cf37 Mon Sep 17 00:00:00 2001 From: oyedeletemitope Date: Wed, 9 Jul 2025 10:12:11 -0700 Subject: [PATCH 4/4] CODE:Made chnages to how i tested the endpoints --- src/users/users.controller.spec.ts | 236 ++++------------------------- 1 file changed, 32 insertions(+), 204 deletions(-) diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index 8f8c8b9..2219b49 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -493,15 +493,11 @@ describe('UsersAdminController', () => { describe('addPhoto', () => { const userId = new Types.ObjectId().toString(); - const photoDto = { - userId, - photo: 'new-photo-url.jpg', - }; + const photoDto = { userId, photo: 'new-photo-url.jpg' }; + const mockReq = { user: createUserMock() }; it('should add a photo to user profile', async () => { - const mockReq = { user: createUserMock() }; const expectedResult = createUserMock({ photo: photoDto.photo }); - jest.spyOn(usersService, 'addPhoto').mockResolvedValue(expectedResult); const result = await adminController.addPhoto(mockReq, userId, photoDto); @@ -513,25 +509,10 @@ describe('UsersAdminController', () => { ); }); - it('should throw error when invalid photo format', async () => { - const mockReq = { user: createUserMock() }; - const invalidPhotoDto = { ...photoDto, photo: 'invalid-format' }; - - jest - .spyOn(usersService, 'addPhoto') - .mockRejectedValue(new BadRequestException('Invalid photo format')); - - await expect( - adminController.addPhoto(mockReq, userId, invalidPhotoDto), - ).rejects.toThrow(BadRequestException); - }); - - it('should throw error when photo size exceeds limit', async () => { - const mockReq = { user: createUserMock() }; - + it('should handle invalid photo format', async () => { jest .spyOn(usersService, 'addPhoto') - .mockRejectedValue(new BadRequestException('Photo size exceeds limit')); + .mockRejectedValue(new BadRequestException()); await expect( adminController.addPhoto(mockReq, userId, photoDto), @@ -541,13 +522,12 @@ describe('UsersAdminController', () => { describe('RequestVerificationToken', () => { const userId = new Types.ObjectId().toString(); + const mockReq = { user: createUserMock() }; it('should request new verification token', async () => { - const mockReq = { user: createUserMock() }; - // Mocking the requestVerification method to resolve successfully jest .spyOn(usersService, 'requestVerification') - .mockResolvedValue(undefined); // or just don't mock return value + .mockResolvedValue(undefined); await adminController.RequestVerificationToken(mockReq, userId); @@ -557,56 +537,14 @@ describe('UsersAdminController', () => { ); }); - it('should throw error when user not found', async () => { - const mockReq = { user: createUserMock() }; - - jest - .spyOn(usersService, 'requestVerification') - .mockRejectedValue(new Error('User not found')); - - await expect( - adminController.RequestVerificationToken(mockReq, userId), - ).rejects.toThrow('User not found'); - }); - - it('should throw error when verification request not allowed', async () => { - const mockReq = { user: createUserMock() }; - - jest - .spyOn(usersService, 'requestVerification') - .mockRejectedValue( - new Error('Verification request not allowed at this time'), - ); - - await expect( - adminController.RequestVerificationToken(mockReq, userId), - ).rejects.toThrow('Verification request not allowed at this time'); - }); - - it('should throw error when user already verified', async () => { - const mockReq = { - user: createUserMock({ applicationStatus: ApplicationStatus.APPROVED }), - }; - - jest - .spyOn(usersService, 'requestVerification') - .mockRejectedValue(new Error('User is already verified')); - - await expect( - adminController.RequestVerificationToken(mockReq, userId), - ).rejects.toThrow('User is already verified'); - }); - - it('should throw error when too many requests', async () => { - const mockReq = { user: createUserMock() }; - + it('should handle verification errors', async () => { jest .spyOn(usersService, 'requestVerification') - .mockRejectedValue(new Error('Too many verification requests')); + .mockRejectedValue(new Error('User not found')); await expect( adminController.RequestVerificationToken(mockReq, userId), - ).rejects.toThrow('Too many verification requests'); + ).rejects.toThrow('User not found'); }); }); @@ -787,11 +725,10 @@ describe('UsersAdminController', () => { describe('Deactivate User Account', () => { const userId = new Types.ObjectId().toString(); const deactivateDto = { reason: 'Taking a break' }; + const mockReq = { user: createUserMock() }; it('should deactivate a user account', async () => { - const mockReq = { user: createUserMock() }; const expectedResult = createUserMock({ status: UserStatus.DEACTIVATED }); - jest .spyOn(usersService, 'deactivateAccount') .mockResolvedValue(expectedResult); @@ -806,35 +743,15 @@ describe('UsersAdminController', () => { expect(usersService.deactivateAccount).toHaveBeenCalledWith(userId); }); - it('should throw NotFoundException when user not found', async () => { - const mockReq = { user: createUserMock() }; - + it('should handle deactivation errors', async () => { jest .spyOn(usersService, 'deactivateAccount') - .mockRejectedValue( - new NotFoundException(`User with ID ${userId} not found`), - ); + .mockRejectedValue(new NotFoundException()); await expect( controller.deactivateAccount(mockReq, userId, deactivateDto), ).rejects.toThrow(NotFoundException); }); - - it('should throw BadRequestException when user cannot be deactivated', async () => { - const mockReq = { user: createUserMock() }; - - jest - .spyOn(usersService, 'deactivateAccount') - .mockRejectedValue( - new BadRequestException( - 'User account cannot be deactivated at this time', - ), - ); - - await expect( - controller.deactivateAccount(mockReq, userId, deactivateDto), - ).rejects.toThrow(BadRequestException); - }); }); describe('Request Reactivation', () => { @@ -843,7 +760,6 @@ describe('UsersAdminController', () => { it('should request reactivation of a user account', async () => { const expectedResult = createUserMock({ status: UserStatus.ACTIVE }); - jest .spyOn(usersService, 'requestReactivation') .mockResolvedValue(expectedResult); @@ -857,31 +773,15 @@ describe('UsersAdminController', () => { expect(usersService.requestReactivation).toHaveBeenCalledWith(userId); }); - it('should throw NotFoundException when user not found', async () => { + it('should handle reactivation errors', async () => { jest .spyOn(usersService, 'requestReactivation') - .mockRejectedValue( - new NotFoundException(`User with ID ${userId} not found`), - ); + .mockRejectedValue(new NotFoundException()); await expect( controller.requestReactivation(userId, reactivationDto), ).rejects.toThrow(NotFoundException); }); - - it('should throw BadRequestException when user cannot be reactivated', async () => { - jest - .spyOn(usersService, 'requestReactivation') - .mockRejectedValue( - new BadRequestException( - 'User account cannot be reactivated at this time', - ), - ); - - await expect( - controller.requestReactivation(userId, reactivationDto), - ).rejects.toThrow(BadRequestException); - }); }); describe('Lead Registration', () => { @@ -895,7 +795,6 @@ describe('UsersAdminController', () => { it('should successfully register a temporary lead', async () => { const expectedResult = 'Application sent'; - jest .spyOn(usersService, 'createTempRegistration') .mockResolvedValue(expectedResult); @@ -909,26 +808,15 @@ describe('UsersAdminController', () => { ); }); - it('should throw BadRequestException when lead registration is not allowed yet', async () => { - const errorMessage = - 'The next time you can apply as a lead is Monday, January 1st, 10:00 am'; - + it('should throw BadRequestException when registration not allowed', async () => { jest .spyOn(usersService, 'createTempRegistration') - .mockRejectedValue(new BadRequestException(errorMessage)); + .mockRejectedValue(new BadRequestException()); await expect(controller.createLead(tempLeadDto)).rejects.toThrow( - new BadRequestException(errorMessage), + BadRequestException, ); }); - - it('should handle errors during lead registration', async () => { - jest - .spyOn(usersService, 'createTempRegistration') - .mockRejectedValue(new Error('Error updating user')); - - await expect(controller.createLead(tempLeadDto)).rejects.toThrow(Error); - }); }); describe('Register New User Form', () => { @@ -936,62 +824,23 @@ describe('UsersAdminController', () => { const userId = new Types.ObjectId().toString(); const email = 'user@example.com'; - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should redirect to new user form when userId is missing', async () => { - jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ - userId: '', - email, - }); - - const result = await controller.register(encryptedData); - - expect(result).toEqual({ - url: `/leads/new-user-form?email=${encodeURIComponent(email)}`, - }); - }); - it('should redirect to create lead page when user exists', async () => { const mockUser = createUserMock({ _id: new Types.ObjectId(userId), email, }); - jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ - userId, - email, - }); - + jest + .spyOn(usersService, 'paraseEncryptedParams') + .mockReturnValue({ userId, email }); jest.spyOn(usersService, 'findById').mockResolvedValue(mockUser); const result = await controller.register(encryptedData); - expect(result).toEqual({ - url: `/leads/createLead?email=${email}`, - }); - expect(usersService.findById).toHaveBeenCalledWith(userId); - }); - - it('should throw NotFoundException when user does not exist', async () => { - jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ - userId, - email, - }); - - jest - .spyOn(usersService, 'findById') - .mockRejectedValue( - new NotFoundException(`User with ID ${userId} not found`), - ); - - await expect(controller.register(encryptedData)).rejects.toThrow( - new NotFoundException('Invalid link'), - ); + expect(result).toEqual({ url: `/leads/createLead?email=${email}` }); }); - it('should throw NotFoundException when decryption fails', async () => { + it('should handle invalid links', async () => { jest .spyOn(usersService, 'paraseEncryptedParams') .mockImplementation(() => { @@ -999,7 +848,7 @@ describe('UsersAdminController', () => { }); await expect(controller.register(encryptedData)).rejects.toThrow( - new NotFoundException('Invalid link'), + NotFoundException, ); }); }); @@ -1009,38 +858,26 @@ describe('UsersAdminController', () => { const userId = new Types.ObjectId().toString(); const email = 'invite@example.com'; - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should process an invite link and redirect to the appropriate page', async () => { + it('should redirect to create lead page when user exists', async () => { const mockUser = createUserMock({ _id: new Types.ObjectId(userId), email, }); - jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ - userId, - email, - }); - + jest + .spyOn(usersService, 'paraseEncryptedParams') + .mockReturnValue({ userId, email }); jest.spyOn(usersService, 'findById').mockResolvedValue(mockUser); const result = await controller.register(encryptedData); - expect(result).toEqual({ - url: `/leads/createLead?email=${email}`, - }); - expect(usersService.paraseEncryptedParams).toHaveBeenCalledWith( - encryptedData, - ); + expect(result).toEqual({ url: `/leads/createLead?email=${email}` }); }); - it('should handle invite link with missing parameters correctly', async () => { - jest.spyOn(usersService, 'paraseEncryptedParams').mockReturnValue({ - userId: '', - email, - }); + it('should redirect to new user form when userId is missing', async () => { + jest + .spyOn(usersService, 'paraseEncryptedParams') + .mockReturnValue({ userId: '', email }); const result = await controller.register(encryptedData); @@ -1048,14 +885,5 @@ describe('UsersAdminController', () => { url: `/leads/new-user-form?email=${encodeURIComponent(email)}`, }); }); - - it('should test the parameter parsing function', () => { - const decryptMock = jest - .fn() - .mockReturnValue('userId=123&email=test@example.com'); - global.decrypt = decryptMock; - - const encryptedParams = 'encoded%20string'; - }); }); });