From 0655991285ef403553f267b7c52e0daa7d48e85d Mon Sep 17 00:00:00 2001
From: Bastien DUMONT <bdumont@grandlyon.com>
Date: Wed, 15 May 2024 13:22:19 +0000
Subject: [PATCH] tests: add tests for users.controller

---
 .../controllers/users.controller.spec.ts      | 376 ++++++++++++------
 src/users/controllers/users.controller.ts     |  28 +-
 test/mock/data/users.mock.data.ts             |  14 +-
 3 files changed, 286 insertions(+), 132 deletions(-)

diff --git a/src/users/controllers/users.controller.spec.ts b/src/users/controllers/users.controller.spec.ts
index 39bc7a051..8b2e72beb 100644
--- a/src/users/controllers/users.controller.spec.ts
+++ b/src/users/controllers/users.controller.spec.ts
@@ -4,7 +4,7 @@ import { JwtService } from '@nestjs/jwt';
 import { getModelToken } from '@nestjs/mongoose';
 import { Test } from '@nestjs/testing';
 import { Types } from 'mongoose';
-import { usersMockData } from '../../../test/mock/data/users.mock.data';
+import { mockUser, usersMockData } from '../../../test/mock/data/users.mock.data';
 import { ConfigurationModule } from '../../configuration/configuration.module';
 import { MailerService } from '../../mailer/mailer.service';
 import { SearchModule } from '../../search/search.module';
@@ -26,6 +26,9 @@ import { UsersService } from '../services/users.service';
 import { PasswordResetApplyDto } from './../dto/reset-password-apply.dto';
 import { PasswordResetDto } from './../dto/reset-password.dto';
 import { UsersController } from './users.controller';
+import { PasswordResetCheckDto } from '../dto/reset-password-check.dto';
+import { mockStructure } from '../../../test/mock/data/structures.mock.data';
+import { IPendingStructureToken } from '../interfaces/pending-structure-token.interface';
 
 describe('UsersController', () => {
   let usersController: UsersController;
@@ -55,6 +58,12 @@ describe('UsersController', () => {
     validateUser: jest.fn(),
     verifyAndUpdateUserEmail: jest.fn(),
     verifyUserExist: jest.fn(),
+    checkPasswordResetToken: jest.fn(),
+    updatePendingStructureLinked: jest.fn(),
+    updateStructureLinked: jest.fn(),
+    removeFromPendingStructureLinked: jest.fn(),
+    sendStructureClaimApproval: jest.fn(),
+    sendAdminStructureNotification: jest.fn(),
   };
 
   const structureServiceMock = {
@@ -62,6 +71,7 @@ describe('UsersController', () => {
     findOne: jest.fn(),
     getAllDataConsentPendingStructures: jest.fn(),
     sendAdminStructureNotification: jest.fn(),
+    sendStructureJoinRequest: jest.fn(),
   };
 
   const tempUserServiceMock = {
@@ -70,7 +80,7 @@ describe('UsersController', () => {
   };
   const mockJwtService = {
     sign: jest.fn(),
-    decode: jest.fn(),
+    decode: jest.fn<IPendingStructureToken, any>(),
   };
 
   beforeEach(async () => {
@@ -138,25 +148,21 @@ describe('UsersController', () => {
   });
 
   describe('setProfile', () => {
+    const setProfileData: ProfileDto = {
+      employerName: 'Metro',
+      jobName: 'Dev',
+    };
     it('should return employer does not exist', async () => {
-      const profile: ProfileDto = {
-        employerName: 'Metro',
-        jobName: 'Dev',
-      };
       employerServiceMock.findByName.mockResolvedValueOnce(null);
       try {
-        await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, profile);
+        await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, setProfileData);
         expect(true).toBe(false);
       } catch (e) {
         expect(e.message).toBe('Employer does not exist');
-        expect(e.status).toBe(400);
+        expect(e.status).toBe(HttpStatus.BAD_REQUEST);
       }
     });
     it('should return job does not exist', async () => {
-      const profile: ProfileDto = {
-        employerName: 'Metro',
-        jobName: 'Dev',
-      };
       employerServiceMock.findByName.mockResolvedValueOnce({
         _id: new Types.ObjectId('6231aefe76598527c8d0b5a7'),
         name: 'CAF',
@@ -164,18 +170,14 @@ describe('UsersController', () => {
       });
       jobServiceMock.findByName.mockResolvedValue(null);
       try {
-        await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, profile);
+        await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, setProfileData);
         expect(true).toBe(false);
       } catch (e) {
         expect(e.message).toBe('Job does not exist');
-        expect(e.status).toBe(400);
+        expect(e.status).toBe(HttpStatus.BAD_REQUEST);
       }
     });
     it('should update profile', async () => {
-      const profile: ProfileDto = {
-        employerName: 'Metro',
-        jobName: 'Dev',
-      };
       employerServiceMock.findByName.mockResolvedValueOnce({
         _id: new Types.ObjectId('6231aefe76598527c8d0b5a7'),
         name: 'CAF',
@@ -187,28 +189,8 @@ describe('UsersController', () => {
         validated: false,
         hasPersonalOffer: false,
       });
-      userServiceMock.findOne.mockResolvedValueOnce({
-        _id: new Types.ObjectId('620e4ffa72389045b02ac854'),
-        structuresLink: [new Types.ObjectId('620e5236f25755550cb86dfd')],
-        structureOutdatedMailSent: [],
-        pendingStructuresLink: [new Types.ObjectId('61e926192ac971550065e275')],
-        phone: '06 06 06 06 06',
-        newEmail: null,
-        changeEmailToken: null,
-        role: 1,
-        resetPasswordToken: null,
-        surname: 'ADMIN',
-        name: 'Admin',
-        validationToken: null,
-        emailVerified: true,
-        email: 'admin@admin.com',
-        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
-        __v: 11,
-        employer: new Types.ObjectId('6231aefe76598527c8d0b5a7'),
-        job: new Types.ObjectId('6231aefe76598527c8d0b5be'),
-      });
-      const reply = await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, profile);
-      expect(reply).toBeTruthy();
+      await usersController.setProfile({ user: { _id: '36', email: 'a@a.com' } }, setProfileData);
+      expect(userServiceMock.updateUserProfile).toHaveBeenCalledTimes(1);
     });
   });
 
@@ -219,177 +201,132 @@ describe('UsersController', () => {
         surname: 'Jacque',
         phone: '0605040302',
       };
-      userServiceMock.updateUserDetails.mockResolvedValueOnce({
-        _id: new Types.ObjectId('620e4ffa72389045b02ac854'),
-        structuresLink: [new Types.ObjectId('620e5236f25755550cb86dfd')],
-        structureOutdatedMailSent: [],
-        pendingStructuresLink: [new Types.ObjectId('61e926192ac971550065e275')],
-        phone: '06 06 06 06 06',
-        newEmail: null,
-        changeEmailToken: null,
-        role: 1,
-        resetPasswordToken: null,
-        surname: 'ADMIN',
-        name: 'Admin',
-        validationToken: null,
-        emailVerified: true,
-        email: 'admin@admin.com',
-        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
-        __v: 11,
-        employer: new Types.ObjectId('6231aefe76598527c8d0b5a7'),
-        job: new Types.ObjectId('6231aefe76598527c8d0b5be'),
-      });
-      const reply = await usersController.updateDetails({ user: { _id: '36', email: 'a@a.com' } }, newDetails);
-      expect(reply).toBeTruthy();
+      await usersController.updateDetails({ user: { _id: '36', email: 'a@a.com' } }, newDetails);
+      expect(userServiceMock.updateUserDetails).toHaveBeenCalled();
     });
   });
 
   describe('create', () => {
-    it('should create user without structure', async () => {
-      const userCreateSpyer = jest.spyOn(userServiceMock, 'create');
-      const structureFindOneSpyer = jest.spyOn(structureServiceMock, 'findOne');
-      const updateStructureLinkedClaimSpyer = jest.spyOn(userServiceMock, 'updateStructureLinkedClaim');
-      const sendAdminStructureNotificationSpyer = jest.spyOn(structureServiceMock, 'sendAdminStructureNotification');
-      const tempUserFindOneSpyer = jest.spyOn(tempUserServiceMock, 'findOne');
-      const tempUserDeleteSpyer = jest.spyOn(tempUserServiceMock, 'delete');
-
+    it('should create user', async () => {
       const createUserDto = new CreateUserDto();
+
       userServiceMock.create.mockResolvedValueOnce(usersMockData[0]);
       const result = await usersController.create(createUserDto);
 
-      expect(userCreateSpyer).toHaveBeenCalledTimes(1);
-      expect(structureFindOneSpyer).toHaveBeenCalledTimes(0);
-      expect(updateStructureLinkedClaimSpyer).toHaveBeenCalledTimes(0);
-      expect(sendAdminStructureNotificationSpyer).toHaveBeenCalledTimes(0);
-      expect(tempUserFindOneSpyer).toHaveBeenCalledTimes(1);
-      expect(tempUserDeleteSpyer).toHaveBeenCalledTimes(0);
+      expect(userServiceMock.create).toHaveBeenCalledTimes(1);
+      expect(tempUserServiceMock.findOne).toHaveBeenCalledTimes(1);
+      expect(tempUserServiceMock.delete).toHaveBeenCalledTimes(0);
       expect(result).toEqual(usersMockData[0]);
     });
 
     it('should create user with temp user', async () => {
-      const userCreateSpyer = jest.spyOn(userServiceMock, 'create');
-      const structureFindOneSpyer = jest.spyOn(structureServiceMock, 'findOne');
-      const updateStructureLinkedClaimSpyer = jest.spyOn(userServiceMock, 'updateStructureLinkedClaim');
-      const sendAdminStructureNotificationSpyer = jest.spyOn(structureServiceMock, 'sendAdminStructureNotification');
-      const tempUserFindOneSpyer = jest.spyOn(tempUserServiceMock, 'findOne');
-      const tempUserDeleteSpyer = jest.spyOn(tempUserServiceMock, 'delete');
-
       const createUserDto = new CreateUserDto();
       userServiceMock.create.mockResolvedValueOnce(usersMockData[0]);
       tempUserServiceMock.findOne.mockResolvedValueOnce({ email: 'test@test.com', pendingStructuresLink: [] });
       const result = await usersController.create(createUserDto);
 
-      expect(userCreateSpyer).toHaveBeenCalledTimes(1);
-      expect(structureFindOneSpyer).toHaveBeenCalledTimes(0);
-      expect(updateStructureLinkedClaimSpyer).toHaveBeenCalledTimes(0);
-      expect(sendAdminStructureNotificationSpyer).toHaveBeenCalledTimes(0);
-      expect(tempUserFindOneSpyer).toHaveBeenCalledTimes(1);
-      expect(tempUserDeleteSpyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.create).toHaveBeenCalledTimes(1);
+
+      expect(tempUserServiceMock.findOne).toHaveBeenCalledTimes(1);
+      expect(tempUserServiceMock.delete).toHaveBeenCalledTimes(1);
       expect(result).toEqual(usersMockData[0]);
     });
   });
 
   describe('validateUser', () => {
     it('should call validateUser', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'validateUser');
       await usersController.validateUser({ id: 1 }, 'token');
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.validateUser).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('changePassword', () => {
     it('should call changeUserPassword', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'changeUserPassword');
       await usersController.changePassword({ user: { _id: '36', email: 'a@a.com' } }, new PasswordChangeDto());
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.changeUserPassword).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('changeEmail', () => {
     it('should call changeUserEmail', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'changeUserEmail');
       await usersController.changeEmail(new EmailChangeDto());
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.changeUserEmail).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('verifyAndUpdateEmail', () => {
     it('should call verifyAndUpdateUserEmail', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'verifyAndUpdateUserEmail');
       await usersController.verifyAndUpdateEmail('token');
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.verifyAndUpdateUserEmail).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('resetPassword', () => {
     it('should call sendResetPasswordEmail', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'sendResetPasswordEmail');
       await usersController.resetPassword(new PasswordResetDto());
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.sendResetPasswordEmail).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('resetPasswordApply', () => {
     it('should call resetPassword', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'resetPassword');
       await usersController.resetPasswordApply(new PasswordResetApplyDto());
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.resetPassword).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe('resetPasswordCheck', () => {
+    it('should call checkPasswordResetToken', async () => {
+      await usersController.resetPasswordCheck(new PasswordResetCheckDto());
+      expect(userServiceMock.checkPasswordResetToken).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('verifyUserExist', () => {
     it('should call verifyUserExist', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'verifyUserExist');
       await usersController.verifyUserExist({ user: { _id: '36', email: 'a@a.com' } }, { newMail: 'test@test.com' });
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.verifyUserExist).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('delete', () => {
     it('should not call isStructureClaimed if no structures are linked', async () => {
-      const deleteOneSpyer = jest.spyOn(userServiceMock, 'deleteOne');
-      const isStructureClaimedSpyer = jest.spyOn(userServiceMock, 'isStructureClaimed');
       const userWithoutStructure = usersMockData[2];
       userServiceMock.deleteOne.mockResolvedValueOnce(userWithoutStructure);
       await usersController.delete({ user: { _id: '36', email: 'a@a.com' } });
-      expect(deleteOneSpyer).toHaveBeenCalledTimes(1);
-      expect(isStructureClaimedSpyer).toHaveBeenCalledTimes(0);
+      expect(userServiceMock.deleteOne).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.isStructureClaimed).toHaveBeenCalledTimes(0);
     });
 
     it('should call isStructureClaimed for each structure linked', async () => {
-      const userDeleteOneSpyer = jest.spyOn(userServiceMock, 'deleteOne');
-      const isStructureClaimedSpyer = jest.spyOn(userServiceMock, 'isStructureClaimed');
       const structureFindOne = jest.spyOn(structureServiceMock, 'findOne');
       const userWithThreeStructures = usersMockData[3];
       userServiceMock.deleteOne.mockResolvedValueOnce(userWithThreeStructures);
       userServiceMock.isStructureClaimed.mockResolvedValue(null);
       userServiceMock.isStructureClaimed.mockResolvedValueOnce(userWithThreeStructures);
       await usersController.delete({ user: { _id: '36', email: 'a@a.com' } });
-      expect(userDeleteOneSpyer).toHaveBeenCalledTimes(1);
-      expect(isStructureClaimedSpyer).toHaveBeenCalledTimes(3);
+      expect(userServiceMock.deleteOne).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.isStructureClaimed).toHaveBeenCalledTimes(3);
       expect(structureFindOne).toHaveBeenCalledTimes(2);
     });
   });
 
   describe('dataConsentValidation', () => {
     it('should call getAllDataConsentPendingStructures', async () => {
-      const spyer = jest.spyOn(structureServiceMock, 'getAllDataConsentPendingStructures');
       await usersController.dataConsentValidation({ user: { _id: '36', email: 'a@a.com' } });
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(structureServiceMock.getAllDataConsentPendingStructures).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('get user', () => {
     it('should return user', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'findById');
       userServiceMock.findById.mockResolvedValueOnce({ _id: '36', email: 'a@a.com' });
       const result = await usersController.getUser(1);
       expect(result).toEqual({ _id: '36', email: 'a@a.com' });
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.findById).toHaveBeenCalledTimes(1);
     });
 
     it('should throw error if no user found', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'findById');
       userServiceMock.findById.mockResolvedValueOnce(null);
 
       try {
@@ -399,15 +336,216 @@ describe('UsersController', () => {
         expect(error.message).toBe('User does not exist');
         expect(error.status).toBe(HttpStatus.NOT_FOUND);
       }
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.findById).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('update description', () => {
     it('should call updateDescription', async () => {
-      const spyer = jest.spyOn(userServiceMock, 'updateDescription');
       await usersController.updateDescription({ user: { _id: '36', email: 'a@a.com' } }, new DescriptionDto());
-      expect(spyer).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.updateDescription).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe('join', () => {
+    it('should not find structure', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(null);
+      try {
+        await usersController.join('structureId', mockUser);
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid Structure');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should not find user', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findOne.mockResolvedValueOnce(null);
+      try {
+        await usersController.join('structureId', mockUser);
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid User');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should call join', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findOne.mockResolvedValueOnce(mockUser);
+      userServiceMock.updatePendingStructureLinked.mockResolvedValueOnce([]);
+      await usersController.join('structureId', mockUser);
+
+      expect(structureServiceMock.sendStructureJoinRequest).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe('joinValidation', () => {
+    it('should have wrong parameters', async () => {
+      try {
+        await usersController.joinValidation('', 'status');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Wrong parameters');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should have expired token', async () => {
+      mockJwtService.decode.mockReturnValue({ expiresAt: '', idStructure: '', userId: '' });
+      try {
+        await usersController.joinValidation('token', 'true');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Expired or invalid token');
+        expect(e.status).toBe(HttpStatus.FORBIDDEN);
+      }
+    });
+    it('should not be able to find structure', async () => {
+      mockJwtService.decode.mockReturnValue({ expiresAt: '2999-12-31T00:00:00.000Z', idStructure: '', userId: '' });
+      structureServiceMock.findOne.mockResolvedValueOnce(null);
+      try {
+        await usersController.joinValidation('token', 'true');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid Structure');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+    it('should not be able to find user', async () => {
+      mockJwtService.decode.mockReturnValue({ expiresAt: '2999-12-31T00:00:00.000Z', idStructure: '', userId: '' });
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce(null);
+      try {
+        await usersController.joinValidation('token', 'true');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid User');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should not be linked to structure', async () => {
+      mockJwtService.decode.mockReturnValue({
+        expiresAt: '2999-12-31T00:00:00.000Z',
+        idStructure: '61e926192ac971550065e275',
+        userId: '',
+      });
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce(mockUser);
+      try {
+        await usersController.joinValidation('token', 'true');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('User not linked to structure');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should accept and return id & name', async () => {
+      mockJwtService.decode.mockReturnValue({
+        expiresAt: '2999-12-31T00:00:00.000Z',
+        idStructure: '620e5236f25755550cb86dfd',
+        userId: '',
+      });
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce({
+        ...mockUser,
+        pendingStructuresLink: [
+          {
+            id: new Types.ObjectId('620e5236f25755550cb86dfd'),
+            token: 'token',
+            structureName: 'a',
+            createdAt: '2021-01-01T00:00:00.000Z',
+          },
+        ],
+      });
+
+      const result = await usersController.joinValidation('token', 'true');
+      expect(userServiceMock.updateStructureLinked).toHaveBeenCalledTimes(1);
+      expect(userServiceMock.removeFromPendingStructureLinked).toHaveBeenCalledTimes(1);
+      expect(result).toEqual({ id: '620e5236f25755550cb86dfd', name: 'a' });
+    });
+
+    it('should refuse and return id & name', async () => {
+      mockJwtService.decode.mockReturnValue({
+        expiresAt: '2999-12-31T00:00:00.000Z',
+        idStructure: '620e5236f25755550cb86dfd',
+        userId: '',
+      });
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce({
+        ...mockUser,
+        pendingStructuresLink: [
+          {
+            id: new Types.ObjectId('620e5236f25755550cb86dfd'),
+            token: 'token',
+            structureName: 'a',
+            createdAt: '2021-01-01T00:00:00.000Z',
+          },
+        ],
+      });
+
+      const result = await usersController.joinValidation('token', 'false');
+      expect(userServiceMock.updateStructureLinked).toHaveBeenCalledTimes(0);
+      expect(userServiceMock.removeFromPendingStructureLinked).toHaveBeenCalledTimes(1);
+      expect(result).toEqual({ id: '620e5236f25755550cb86dfd', name: 'a' });
+    });
+  });
+
+  describe('joinCancel', () => {
+    it('should fail to find structure', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(null);
+      try {
+        await usersController.joinCancel('structureId', 'token');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid Structure');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should fail to find user', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce(null);
+      try {
+        await usersController.joinCancel('structureId', 'token');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('Invalid User');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should throw an error pending state', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce(mockUser);
+      try {
+        await usersController.joinCancel('00e926192ac971550065e000', 'token');
+        expect(true).toBe(false);
+      } catch (e) {
+        expect(e.message).toBe('This structure is in pending state');
+        expect(e.status).toBe(HttpStatus.NOT_FOUND);
+      }
+    });
+
+    it('should call removeFromPendingStructureLinked', async () => {
+      structureServiceMock.findOne.mockResolvedValueOnce(mockStructure);
+      userServiceMock.findById.mockResolvedValueOnce({
+        ...mockUser,
+        pendingStructuresLink: [
+          {
+            id: new Types.ObjectId('620e5236f25755550cb86dfd'),
+            token: 'token',
+            structureName: 'a',
+            createdAt: '2021-01-01T00:00:00.000Z',
+          },
+        ],
+      });
+      userServiceMock.removeFromPendingStructureLinked.mockResolvedValueOnce([]);
+      await usersController.joinCancel('620e5236f25755550cb86dfd', 'token');
+      expect(userServiceMock.removeFromPendingStructureLinked).toHaveBeenCalledTimes(1);
     });
   });
 });
diff --git a/src/users/controllers/users.controller.ts b/src/users/controllers/users.controller.ts
index 1a6634b2b..2be10cb18 100644
--- a/src/users/controllers/users.controller.ts
+++ b/src/users/controllers/users.controller.ts
@@ -53,7 +53,7 @@ export class UsersController {
   @UseGuards(JwtAuthGuard)
   @ApiBearerAuth('JWT')
   @ApiOperation({ description: 'Get user profile' })
-  @ApiResponse({ status: HttpStatus.OK, description: 'Return user profil' })
+  @ApiResponse({ status: HttpStatus.OK, description: 'Return user profile' })
   @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'User does not have sufficient rights' })
   @Get('profile')
   public getProfile(@Request() req) {
@@ -63,7 +63,8 @@ export class UsersController {
   @UseGuards(JwtAuthGuard)
   @ApiBearerAuth('JWT')
   @ApiOperation({ description: 'Set user profile with employer and job' })
-  @ApiResponse({ status: HttpStatus.OK, description: 'Return user profil' })
+  @ApiBody({ type: ProfileDto })
+  @ApiResponse({ status: HttpStatus.OK, description: 'Return user profile' })
   @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Job does not exist' })
   @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Employer does not exist' })
   @Post('profile')
@@ -79,17 +80,22 @@ export class UsersController {
       this.logger.warn(`Job does not exist: ${profile.jobName}`);
       throw new HttpException('Job does not exist', HttpStatus.BAD_REQUEST);
     }
-    await this.usersService.updateUserProfile(req.user._id, employerDocument, jobDocument, profile.withAppointment);
-    return this.usersService.findOne(req.user.email);
+    const user = await this.usersService.updateUserProfile(
+      req.user._id,
+      employerDocument,
+      jobDocument,
+      profile.withAppointment
+    );
+    return user;
   }
 
   @UseGuards(JwtAuthGuard)
   @ApiBearerAuth('JWT')
   @ApiOperation({ description: 'Updates name, surname and phone number of a user' })
   @ApiBody({ type: UpdateDetailsDto })
-  @ApiResponse({ status: HttpStatus.CREATED, description: 'Return user profil' })
+  @ApiResponse({ status: HttpStatus.CREATED, description: 'Return user profile' })
   @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'User not found' })
-  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Missing parametter' })
+  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Missing parameter' })
   @Post('details')
   public async updateDetails(@Request() req, @Body() details: UpdateDetailsDto): Promise<User | HttpException> {
     this.logger.debug('updateProfile details');
@@ -239,7 +245,7 @@ export class UsersController {
     this.structureService.sendStructureJoinRequest(userFromDb, structure, token[0]);
   }
 
-  //add token in route + add infos in token
+  /** add token in route + add infos in token */
   @Get('join-validate/:token/:status')
   @ApiParam({ name: 'token', type: String, required: true })
   @ApiParam({ name: 'status', type: String, required: true })
@@ -250,7 +256,7 @@ export class UsersController {
     id: string;
     name: string;
   }> {
-    const decoded: IPendingStructureToken = this.jwtService.decode(token) as IPendingStructureToken;
+    const decoded = this.jwtService.decode(token) as IPendingStructureToken;
     const today = DateTime.local().setZone('utc', { keepLocalTime: true });
 
     if (!token || !status) {
@@ -272,7 +278,7 @@ export class UsersController {
     if (
       !userFromDb.pendingStructuresLink
         .map((pending) => pending.id)
-        .filter((id) => id.equals(new Types.ObjectId(decoded.idStructure)))
+        .some((id) => id.equals(new Types.ObjectId(decoded.idStructure)))
     ) {
       throw new HttpException('User not linked to structure', HttpStatus.NOT_FOUND);
     }
@@ -289,7 +295,7 @@ export class UsersController {
     return { id: decoded.idStructure, name: structure.structureName };
   }
 
-  // Cancel a user's join request
+  /** Cancel a user's join request */
   @Get('join-cancel/:idStructure/:idUser')
   @ApiParam({ name: 'idStructure', type: String, required: true })
   @ApiParam({ name: 'idUser', type: String, required: true })
@@ -307,7 +313,7 @@ export class UsersController {
     if (
       !userFromDb.pendingStructuresLink
         .map((pending) => pending.id)
-        .filter((id) => id.equals(new Types.ObjectId(idStructure)))
+        .some((id) => id.equals(new Types.ObjectId(idStructure)))
     ) {
       throw new HttpException('This structure is in pending state', HttpStatus.NOT_FOUND);
     }
diff --git a/test/mock/data/users.mock.data.ts b/test/mock/data/users.mock.data.ts
index 06ee4518a..2dfe3fde3 100644
--- a/test/mock/data/users.mock.data.ts
+++ b/test/mock/data/users.mock.data.ts
@@ -1,6 +1,7 @@
 import { Types } from 'mongoose';
 import { IUser } from '../../../src/users/interfaces/user.interface';
 import { IUserRegistry } from '../../../src/users/interfaces/userRegistry.interface';
+import { User } from '../../../src/users/schemas/user.schema';
 
 export const usersMockData: IUser[] = [
   {
@@ -222,8 +223,8 @@ export const userRegistryMockData: IUserRegistry = {
   save: jest.fn(),
 } as any;
 
-export const mockUser = {
-  _id: 'id',
+export const mockUser: User = {
+  // _id: 'id',
   validationToken:
     'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
   emailVerified: true,
@@ -234,4 +235,13 @@ export const mockUser = {
   surname: 'Pauline',
   personalOffers: [],
   structuresLink: [new Types.ObjectId('6093ba0e2ab5775cfc01ed3e')],
+  createdAt: new Date(2024, 1, 1),
+  phone: '',
+  resetPasswordToken: '',
+  changeEmailToken: '',
+  newEmail: '',
+  pendingStructuresLink: [],
+  structureOutdatedMailSent: [],
+  unattachedSince: new Date(2024, 1, 1),
+  lastLoginDate: new Date(2024, 1, 1),
 };
-- 
GitLab