diff --git a/.gitignore b/.gitignore index 7b29b8d26bb183b46db6f01f59e6f3f95587410f..36681a2a1181af5fe6c64fd1a4c6a9d51e3ce76a 100644 --- a/.gitignore +++ b/.gitignore @@ -385,7 +385,6 @@ Network Trash Folder Temporary Items .apdisk -======= # Local .env dist @@ -394,5 +393,8 @@ dist .migrate src/migrations/data/ +# Tests +junit.xml + # Documentation generated with compodoc -documentation \ No newline at end of file +documentation diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8396a9ed144b0d4185a8bfd0dd085c5ceac7eade..6807e1a45c58a92adc1b16276d1ef780513e9bb5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,7 @@ test: - merge_requests code_analysis: - image: skilldlabs/sonar-scanner:4.0.0 + image: registry.forge.grandlyon.com/apoyen2/sonnar-scanner-gl:master services: - docker:18.09-dind stage: sonar-analysis diff --git a/package.json b/package.json index 1497f941af26c5210397961feb817d3220dfcc59..3a185a1aa134f4dcfd07cf6e7710cc879dfda30d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "release": "standard-version", "init-db": "node ./scripts/init-db.js", "test": "jest", - "test:watch": "jest --watch", + "test:watch": "jest --config ./test/jest.json --watch", "test:cov": "jest --config ./test/jest.json --coverage --ci --reporters=default --reporters=jest-junit", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", diff --git a/src/admin/admin.controller.spec.ts b/src/admin/admin.controller.spec.ts index dc0bdef307a30973ed3864fa428e46aca34c8dea..26b7d5b8b9ecdf2aa5acde316dd5abfecda90638 100644 --- a/src/admin/admin.controller.spec.ts +++ b/src/admin/admin.controller.spec.ts @@ -12,7 +12,13 @@ import { StructuresSearchService } from '../structures/services/structures-searc import { User } from '../users/schemas/user.schema'; import { UsersService } from '../users/users.service'; import { AdminController } from './admin.controller'; -import { PendingStructureDto } from './dto/pending-structure.dto'; +import { mockJwtAuthGuard } from '../../test/mock/guards/jwt-auth.mock.guard'; +import { mockRoleGuard } from '../../test/mock/guards/role.mock.guard'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { RolesGuard } from '../users/guards/roles.guard'; +import { UsersServiceMock } from '../../test/mock/services/user.mock.service'; +import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service'; +import { NewsletterServiceMock } from '../../test/mock/services/newsletter.mock.service'; describe('AdminController', () => { let controller: AdminController; @@ -21,10 +27,19 @@ describe('AdminController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ConfigurationModule, HttpModule, SearchModule], providers: [ - UsersService, - StructuresService, + { + provide: UsersService, + useClass: UsersServiceMock, + }, + { + provide: StructuresService, + useClass: StructuresServiceMock, + }, + { + provide: NewsletterService, + useClass: NewsletterServiceMock, + }, StructuresSearchService, - NewsletterService, MailerService, { provide: getModelToken('User'), @@ -40,7 +55,12 @@ describe('AdminController', () => { }, ], controllers: [AdminController], - }).compile(); + }) + .overrideGuard(JwtAuthGuard) + .useValue(mockJwtAuthGuard) + .overrideGuard(RolesGuard) + .useValue(mockRoleGuard) + .compile(); controller = module.get<AdminController>(AdminController); }); @@ -50,36 +70,124 @@ describe('AdminController', () => { }); it('should get pending attachments', async () => { - const result = [ - { name: 'MJC Route de vienne', address: '14 chemin des platanes' }, - { name: 'Mairie Lyon 7eme', address: '21 boulevard martin' }, - ]; - jest.spyOn(controller, 'getPendingAttachments').mockImplementation(async (): Promise<any> => result); - expect(await controller.getPendingAttachments()).toBe(result); + expect((await controller.getPendingAttachments()).length).toBe(2); + expect(Object.keys((await controller.getPendingAttachments())[0]).length).toBe(3); + }); + + describe('Pending structures validation', () => { + it('should validate pending structure', async () => { + const pendingStructureTest = { + structureId: '6093ba0e2ab5775cfc01ed3e', + structureName: 'test', + userEmail: 'jean.paul@mii.com', + }; + expect((await controller.validatePendingStructure(pendingStructureTest)).length).toBe(2); + expect(Object.keys((await controller.validatePendingStructure(pendingStructureTest))[0]).length).toBe(3); + }); + + it('should get structure does not exist', async () => { + const pendingStructureTest = { + structureId: '1093ba0e2ab5775cfc01z2ki', + structureName: 'test', + userEmail: 'jean.paul@mii.com', + }; + try { + await controller.validatePendingStructure(pendingStructureTest); + } catch (e) { + expect(e.message).toBe('Structure does not exist'); + expect(e.status).toBe(404); + } + }); + }); + + describe('Pending structures cancel', () => { + it('should refuse pending structure', async () => { + const pendingStructureTest = { + structureId: '6093ba0e2ab5775cfc01ed3e', + structureName: 'test', + userEmail: 'jean.paul@mii.com', + }; + expect((await controller.refusePendingStructure(pendingStructureTest)).length).toBe(2); + expect(Object.keys((await controller.refusePendingStructure(pendingStructureTest))[0]).length).toBe(3); + }); + + it('should get structure does not exist', async () => { + const pendingStructureTest = { + structureId: '1093ba0e2ab5775cfc01z2ki', + structureName: 'test', + userEmail: 'jean.paul@mii.com', + }; + try { + await controller.refusePendingStructure(pendingStructureTest); + } catch (e) { + expect(e.message).toBe('Structure does not exist'); + expect(e.status).toBe(404); + } + }); + }); + + describe('Delete user', () => { + it('should delete user', async () => { + expect((await controller.deleteUser({ id: 'tsfsf6296' })).email).toBe('pauline.dupont@mii.com'); + }); + it('should return unexisting user', async () => { + try { + await controller.deleteUser({ id: 'userdoesnotexist' }); + } catch (e) { + expect(e.message).toBe('Invalid user id'); + expect(e.status).toBe(400); + } + }); + }); + + describe('Search user', () => { + it('should return all users, empty string', async () => { + expect((await controller.searchUsers({ searchString: '' })).length).toBe(2); + }); + it('should return all users, null input', async () => { + expect((await controller.searchUsers({ searchString: null })).length).toBe(2); + }); + it('should one user', async () => { + expect((await controller.searchUsers({ searchString: 'a@a.com' })).length).toBe(1); + expect((await controller.searchUsers({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com'); + }); + it('should no user', async () => { + expect((await controller.searchUsers({ searchString: 'dfqfqsfqfqfa@a.com' })).length).toBe(0); + }); + }); + + describe('Search user newleetter subscription', () => { + it('should return all subscribed users, empty string', async () => { + expect((await controller.getNewsletterSubscriptions({ searchString: '' })).length).toBe(3); + }); + it('should return all subscribed users, null input', async () => { + expect((await controller.getNewsletterSubscriptions({ searchString: null })).length).toBe(3); + }); + it('should find one user', async () => { + expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' })).length).toBe(1); + expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com'); + }); + it('should find no user', async () => { + expect((await controller.getNewsletterSubscriptions({ searchString: 'adgdgsdg@a.com' })).length).toBe(0); + }); }); - it('should validate pending structure', async () => { - const result = [{ name: 'MJC Route de vienne', address: '14 chemin des platanes' }]; - const structure: PendingStructureDto = { - userEmail: 'martin@mjc.fr', - structureId: '1', - structureName: 'MJC Route de vienne', - }; - jest.spyOn(controller, 'validatePendingStructure').mockImplementation(async (): Promise<any> => result); - expect(await controller.validatePendingStructure(structure)).toBe(result); + it('should count user subscribed to newsletter', async () => { + expect(await controller.countNewsletterSubscriptions()).toBe(246); }); - it('should refuse pending structure', async () => { - const result = [ - { name: 'MJC Route de vienne', address: '14 chemin des platanes' }, - { name: 'Mairie Lyon 7eme', address: '21 boulevard martin' }, - ]; - const structure: PendingStructureDto = { - userEmail: 'martin@mjc.fr', - structureId: '1', - structureName: 'MJC Route de vienne', - }; - jest.spyOn(controller, 'refusePendingStructure').mockImplementation(async (): Promise<any> => result); - expect(await controller.refusePendingStructure(structure)).toBe(result); + describe('Search delete a user subscription', () => { + it('should return a deleted object', async () => { + expect((await controller.unsubscribeUserFromNewsletter('a@a.com')).email).toBe('a@a.com'); + expect(Object.keys(await controller.unsubscribeUserFromNewsletter('a@a.com')).length).toBe(4); + }); + it('should throw an error', async () => { + try { + await controller.unsubscribeUserFromNewsletter('test@test.com'); + } catch (e) { + expect(e.message).toBe('Invalid email'); + expect(e.status).toBe(401); + } + }); }); }); diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts index f35cae88243539fc12daa9aa0adf6c9f47499cb3..ea3fb601bd80e8a72b7e46d2305e04f79dbf3264 100644 --- a/src/admin/admin.controller.ts +++ b/src/admin/admin.controller.ts @@ -1,6 +1,6 @@ -import { Body, Delete, Param, Controller, Get, Post, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiParam } from '@nestjs/swagger'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { Body, Delete, Param, Controller, Get, Post, UseGuards, HttpStatus } from '@nestjs/common'; import { NewsletterSubscription } from '../newsletter/newsletter-subscription.schema'; import { NewsletterService } from '../newsletter/newsletter.service'; import { StructuresService } from '../structures/services/structures.service'; @@ -63,6 +63,9 @@ export class AdminController { @ApiOperation({ description: 'Validate structure ownership' }) public async validatePendingStructure(@Body() pendingStructureDto: PendingStructureDto) { const structure = await this.structuresService.findOne(pendingStructureDto.structureId); + if (!structure || structure.deletedAt) { + throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND); + } await this.usersService.validatePendingStructure( pendingStructureDto.userEmail, pendingStructureDto.structureId, @@ -86,6 +89,9 @@ export class AdminController { @ApiOperation({ description: 'Refuse structure ownership' }) public async refusePendingStructure(@Body() pendingStructureDto: PendingStructureDto) { const structure = await this.structuresService.findOne(pendingStructureDto.structureId); + if (!structure || structure.deletedAt) { + throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND); + } await this.usersService.validatePendingStructure( pendingStructureDto.userEmail, pendingStructureDto.structureId, @@ -123,7 +129,7 @@ export class AdminController { @Roles('admin') @Post('searchUsers') public async searchUsers(@Body() searchString: { searchString: string }) { - if (searchString && searchString.searchString.length > 0) + if (searchString && searchString.searchString && searchString.searchString.length > 0) return this.usersService.searchUsers(searchString.searchString); else return this.usersService.findAll(); } @@ -132,7 +138,7 @@ export class AdminController { @Roles('admin') @Post('searchNewsletterSubscriptions') public async getNewsletterSubscriptions(@Body() searchString: { searchString: string }) { - if (searchString && searchString.searchString.length > 0) + if (searchString && searchString.searchString && searchString.searchString.length > 0) return this.newsletterService.searchNewsletterSubscription(searchString.searchString); else return this.newsletterService.findAll(); } diff --git a/src/admin/dto/pending-structure.dto.ts b/src/admin/dto/pending-structure.dto.ts index e07fd91f860cc5468be1e2449ce05897a26b1465..1f735be0f4256472524dba43286ab53cba836fcc 100644 --- a/src/admin/dto/pending-structure.dto.ts +++ b/src/admin/dto/pending-structure.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; +import { IsEmail, IsMongoId, IsNotEmpty, IsString } from 'class-validator'; export class PendingStructureDto { @IsNotEmpty() @@ -8,7 +8,7 @@ export class PendingStructureDto { readonly userEmail: string; @IsNotEmpty() - @IsString() + @IsMongoId() @ApiProperty({ type: String }) readonly structureId: string; diff --git a/src/auth/auth.controller.spec.ts b/src/auth/auth.controller.spec.ts index 184f7449c5b02fa817051b043378f086ae9c8f79..c92f1b8b66947641325ba1b79cd8d0c5f266f542 100644 --- a/src/auth/auth.controller.spec.ts +++ b/src/auth/auth.controller.spec.ts @@ -2,6 +2,7 @@ import { JwtModule } from '@nestjs/jwt'; import { getModelToken } from '@nestjs/mongoose'; import { PassportModule } from '@nestjs/passport'; import { Test, TestingModule } from '@nestjs/testing'; +import { AuthServiceMock } from '../../test/mock/services/auth.mock.service'; import { ConfigurationModule } from '../configuration/configuration.module'; import { MailerModule } from '../mailer/mailer.module'; import { User } from '../users/schemas/user.schema'; @@ -9,11 +10,9 @@ import { UsersService } from '../users/users.service'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { LoginDto } from './login-dto'; -import { HttpException, HttpStatus } from '@nestjs/common'; describe('AuthController', () => { let controller: AuthController; - // let service: AuthService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -30,6 +29,10 @@ describe('AuthController', () => { providers: [ AuthService, UsersService, + { + provide: AuthService, + useClass: AuthServiceMock, + }, { provide: getModelToken('User'), useValue: User, @@ -45,16 +48,29 @@ describe('AuthController', () => { }); it('should login valid user', async () => { - const result = { username: 'paula.dubois@mii.com', token: 'tok3N', name: 'paula', surname: 'dubois' }; - jest.spyOn(controller, 'login').mockImplementation(async (): Promise<{ username; name; surname; token }> => result); const loginCredentials: LoginDto = { email: 'paula.dubois@mii.com', password: process.env.USER_PWD }; - expect(await controller.login(loginCredentials)).toBe(result); + const result = await controller.login(loginCredentials); + expect(result).toStrictEqual({ + _id: 'tsfsf6296', + email: 'pauline.dupont@mii.com', + emailVerified: true, + name: 'DUBOIS', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + surname: 'Paula', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + }); }); - it('should not login valid user', async () => { - const result: HttpException = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); - jest.spyOn(controller, 'login').mockImplementation(async (): Promise<any> => result); - const loginCredentials: LoginDto = { email: 'paula.dubois@mii.com', password: process.env.USER_PWD }; - expect(await controller.login(loginCredentials)).toBe(result); + it('should not login invalid user', async () => { + const loginCredentials: LoginDto = { email: 'jacques.dupont@mii.com', password: process.env.USER_PWD }; + try { + await controller.login(loginCredentials); + } catch (e) { + expect(e.response).toBe('Invalid credentials'); + expect(e.message).toBe('Invalid credentials'); + expect(e.status).toBe(401); + } }); }); diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index 2e85b13e99460b761605d520d20d936392dc2d9c..188c44616d058d7ca507edd84ae321f047653bb0 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -1,11 +1,10 @@ -import { HttpException, HttpStatus } from '@nestjs/common'; -import { JwtModule } from '@nestjs/jwt'; -import { getModelToken } from '@nestjs/mongoose'; +import { JwtService } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { Test, TestingModule } from '@nestjs/testing'; +import { JwtServiceMock } from '../../test/mock/services/jwt.mock.service'; +import { UsersServiceMock } from '../../test/mock/services/user.mock.service'; import { ConfigurationModule } from '../configuration/configuration.module'; import { MailerModule } from '../mailer/mailer.module'; -import { User } from '../users/schemas/user.schema'; import { UsersService } from '../users/users.service'; import { AuthService } from './auth.service'; import { LoginDto } from './login-dto'; @@ -15,21 +14,16 @@ describe('AuthService', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ - PassportModule, - MailerModule, - ConfigurationModule, - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: '86400s' }, // 24h validity - }), - ], + imports: [PassportModule, MailerModule, ConfigurationModule], providers: [ AuthService, - UsersService, { - provide: getModelToken('User'), - useValue: User, + provide: UsersService, + useClass: UsersServiceMock, + }, + { + provide: JwtService, + useClass: JwtServiceMock, }, ], }).compile(); @@ -52,45 +46,95 @@ describe('AuthService', () => { role: 0, }; const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result); - expect(await service.validateUser(loginDto)).toBe(result); + expect(await service.validateUser(loginDto)).toStrictEqual(result); }); it('should not validateUser', async () => { - const result = null; - const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result); - expect(await service.validateUser(loginDto)).toBe(result); + const loginDto: LoginDto = { email: 'tom.dupont@mii.com', password: 'test1A!!!' }; //NOSONAR + expect(await service.validateUser(loginDto)).toBe(null); }); }); describe('login', () => { - it('should login user jacques.dupont@mii.com', async () => { - const result = { username: ' jacques.dupont@mii.com', token: 'tok3n!1sfq' }; - const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<{ username; token }> => result); - expect(await service.validateUser(loginDto)).toBe(result); + it('should login user pauline.dupont@mii.com', async () => { + // Token creation mock + const token = { + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', + expiresAt: '2021-05-04T15:35:06.663+02:00', + }; + const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); + _createToken.mockImplementation(() => token); + + const loginDto: LoginDto = { email: 'pauline.dupont@mii.com', password: 'test1A!!' }; //NOSONAR + expect(await service.login(loginDto)).toStrictEqual({ + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', + expiresAt: '2021-05-04T15:35:06.663+02:00', + username: 'pauline.dupont@mii.com', + name: 'DUPONT', + surname: 'Pauline', + }); }); - it('should not login jacques.dupont@mii.com, email not verified', async () => { - const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); + it('should login not login user jacques.dupont@mii.com because email is not verified', async () => { + // Token creation mock + const token = { + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', + expiresAt: '2021-05-04T15:35:06.663+02:00', + }; + const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); + _createToken.mockImplementation(() => token); + const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result); - expect(await service.validateUser(loginDto)).toBe(result); + try { + await service.login(loginDto); + } catch (e) { + expect(e.response).toBe('Invalid credentials'); + expect(e.message).toBe('Invalid credentials'); + expect(e.status).toBe(401); + } }); - it('should not login jacques.dupont@mii.com, bad password', async () => { - const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); - const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result); - expect(await service.validateUser(loginDto)).toBe(result); + it('should login not login user toto@mii.com because email does not exist', async () => { + // Token creation mock + const token = { + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', + expiresAt: '2021-05-04T15:35:06.663+02:00', + }; + const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); + _createToken.mockImplementation(() => token); + + const loginDto: LoginDto = { email: 'toto@mii.com', password: 'test1A!!' }; //NOSONAR + try { + await service.login(loginDto); + } catch (e) { + expect(e.response).toBe('Invalid credentials'); + expect(e.message).toBe('Invalid credentials'); + expect(e.status).toBe(401); + } }); - it('should not login jacques.dupont@mii.com, username does not exist', async () => { - const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); - const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR - jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result); - expect(await service.validateUser(loginDto)).toBe(result); + it('should login not login user toto@mii.com because password is invalid', async () => { + // Token creation mock + const token = { + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', + expiresAt: '2021-05-04T15:35:06.663+02:00', + }; + const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); + _createToken.mockImplementation(() => token); + + const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: '1' }; //NOSONAR + try { + await service.login(loginDto); + } catch (e) { + expect(e.response).toBe('Invalid credentials'); + expect(e.message).toBe('Invalid credentials'); + expect(e.status).toBe(401); + } }); }); }); diff --git a/src/categories/controllers/categories-accompagnement.controller.spec.ts b/src/categories/controllers/categories-accompagnement.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3035f99d088bc9a6cf36db92b231f3f792eac7b --- /dev/null +++ b/src/categories/controllers/categories-accompagnement.controller.spec.ts @@ -0,0 +1,65 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { mockJwtAuthGuard } from '../../../test/mock/guards/jwt-auth.mock.guard'; +import { mockRoleGuard } from '../../../test/mock/guards/role.mock.guard'; +import { CategoriesAccompagnementServiceMock } from '../../../test/mock/services/categoriesAccompagnement.mock.service'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { RolesGuard } from '../../users/guards/roles.guard'; +import { CreateCategoriesAccompagnement } from '../dto/create-categoriesAccompagnement.dto'; +import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema'; +import { CategoriesAccompagnementService } from '../services/categories-accompagnement.service'; +import { CategoriesAccompagnementController } from './categories-accompagnement.controller'; + +describe('CategoriesAccompagnementController', () => { + let controller: CategoriesAccompagnementController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + { + provide: CategoriesAccompagnementService, + useClass: CategoriesAccompagnementServiceMock, + }, + { + provide: getModelToken('CategoriesAccompagnement'), + useValue: CategoriesAccompagnement, + }, + ], + controllers: [CategoriesAccompagnementController], + }) + .overrideGuard(JwtAuthGuard) + .useValue(mockJwtAuthGuard) + .overrideGuard(RolesGuard) + .useValue(mockRoleGuard) + .compile(); + + controller = module.get<CategoriesAccompagnementController>(CategoriesAccompagnementController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + it('should find all accompagnements', async () => { + expect((await controller.findAll()).length).toBe(1); + expect((await controller.findAll())[0].modules.length).toBe(8); + }); + + it('should create accompagnements', async () => { + const data: CreateCategoriesAccompagnement = { + name: 'test', + modules: [ + { + id: 'tst', + text: 'Test', + }, + { + id: 'antst', + text: 'Another test', + }, + ], + }; + expect((await controller.create(data)).modules.length).toBe(2); + }); +}); diff --git a/src/categories/controllers/categories-accompagnement.controller.ts b/src/categories/controllers/categories-accompagnement.controller.ts index 66ea90536b84624505ba92e826f7b3cbe9fa2b9d..51d7f482339ed2ff952db8e456ef72f8b6012556 100644 --- a/src/categories/controllers/categories-accompagnement.controller.ts +++ b/src/categories/controllers/categories-accompagnement.controller.ts @@ -1,15 +1,20 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; +import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; import { CategoriesAccompagnementService } from '../services/categories-accompagnement.service'; import { CreateCategoriesAccompagnement } from '../dto/create-categoriesAccompagnement.dto'; import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { Roles } from '../../users/decorators/roles.decorator'; +import { RolesGuard } from '../../users/guards/roles.guard'; @Controller('categories/categoriesAccompagnement') export class CategoriesAccompagnementController { constructor(private readonly categoriesAccompagnementService: CategoriesAccompagnementService) {} @Post() - public async create(@Body() createStructureDto: CreateCategoriesAccompagnement) { - await this.categoriesAccompagnementService.create(createStructureDto); + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + public async create(@Body() createStructureDto: CreateCategoriesAccompagnement): Promise<CategoriesAccompagnement> { + return this.categoriesAccompagnementService.create(createStructureDto); } @Get() diff --git a/src/categories/controllers/categories-formations.controller.spec.ts b/src/categories/controllers/categories-formations.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c9a1d3099db52bcb7c9f21e23b70e463a4746a3 --- /dev/null +++ b/src/categories/controllers/categories-formations.controller.spec.ts @@ -0,0 +1,55 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesFormationsServiceMock } from '../../../test/mock/services/categoriesFormations.mock.service'; +import { CreateCategoriesFormations } from '../dto/create-categoriesFormations.dto'; +import { CategoriesFormations } from '../schemas/categoriesFormations.schema'; +import { CategoriesFormationsService } from '../services/categories-formations.service'; +import { CategoriesFormationsController } from './categories-formations.controller'; + +describe('CategoriesFormationsController', () => { + let controller: CategoriesFormationsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + { + provide: CategoriesFormationsService, + useClass: CategoriesFormationsServiceMock, + }, + { + provide: getModelToken('CategoriesFormations'), + useValue: CategoriesFormations, + }, + ], + controllers: [CategoriesFormationsController], + }).compile(); + + controller = module.get<CategoriesFormationsController>(CategoriesFormationsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + it('should find all formations', async () => { + expect((await controller.findAll()).length).toBe(6); + expect((await controller.findAll())[0].modules.length).toBe(8); + }); + + it('should create a formation', async () => { + const data: CreateCategoriesFormations = { + name: 'test', + modules: [ + { + id: '74', + display_id: '74', + display_name: 'Modules APTIC - n°74', + digest: 'Smartphones et Tablettes sous Androïd', + text: 'Smartphones et Tablettes sous Androïd', + }, + ], + }; + expect((await controller.create(data)).modules.length).toBe(2); + }); +}); diff --git a/src/categories/controllers/categories-formations.controller.ts b/src/categories/controllers/categories-formations.controller.ts index 7b1ca152bc86bc15adcd48baa8e7dd4442678589..527a7ba9f33deddbcf5743dc29697f3261cd2ccf 100644 --- a/src/categories/controllers/categories-formations.controller.ts +++ b/src/categories/controllers/categories-formations.controller.ts @@ -1,15 +1,20 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; +import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; import { CategoriesFormationsService } from '../services/categories-formations.service'; import { CreateCategoriesFormations } from '../dto/create-categoriesFormations.dto'; import { CategoriesFormations } from '../schemas/categoriesFormations.schema'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { Roles } from '../../users/decorators/roles.decorator'; +import { RolesGuard } from '../../users/guards/roles.guard'; @Controller('categories/categoriesFormations') export class CategoriesFormationsController { constructor(private readonly categoriesFormationsService: CategoriesFormationsService) {} @Post() - public async create(@Body() createStructureDto: CreateCategoriesFormations) { - await this.categoriesFormationsService.create(createStructureDto); + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + public async create(@Body() createStructureDto: CreateCategoriesFormations): Promise<CategoriesFormations> { + return this.categoriesFormationsService.create(createStructureDto); } @Get() diff --git a/src/categories/controllers/categories-others.controller.spec.ts b/src/categories/controllers/categories-others.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a7acc20479fe13a2740cc7d09b92459497b96ea --- /dev/null +++ b/src/categories/controllers/categories-others.controller.spec.ts @@ -0,0 +1,51 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesOthersServiceMock } from '../../../test/mock/services/categoriesOthers.mock.service'; +import { CategoriesOthers } from '../schemas/categoriesOthers.schema'; +import { CategoriesOthersService } from '../services/categories-others.service'; +import { CategoriesOthersController } from './categories-others.controller'; + +describe('CategoriesOthersController', () => { + let controller: CategoriesOthersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + { + provide: CategoriesOthersService, + useClass: CategoriesOthersServiceMock, + }, + { + provide: getModelToken('CategoriesOthers'), + useValue: CategoriesOthers, + }, + ], + controllers: [CategoriesOthersController], + }).compile(); + + controller = module.get<CategoriesOthersController>(CategoriesOthersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + it('should find all formations', async () => { + expect((await controller.findAll()).length).toBe(1); + expect((await controller.findAll())[0].modules.length).toBe(6); + }); + + it('should create a formation', async () => { + const data: CategoriesOthers = { + name: 'test', + modules: [ + { + id: 'toutPublic', + text: 'Tout public', + }, + ], + }; + expect((await controller.create(data)).modules.length).toBe(1); + }); +}); diff --git a/src/categories/controllers/categories-others.controller.ts b/src/categories/controllers/categories-others.controller.ts index 6e284e397b877f14417040ba36d36516bb15e1eb..b45f1530ed2ef443f52ba5a9f9b5cee6b720fc66 100644 --- a/src/categories/controllers/categories-others.controller.ts +++ b/src/categories/controllers/categories-others.controller.ts @@ -1,15 +1,20 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; +import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; import { CategoriesOthersService } from '../services/categories-others.service'; import { CreateCategoriesOthers } from '../dto/create-categoriesOthers.dto'; import { CategoriesOthers } from '../schemas/categoriesOthers.schema'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { Roles } from '../../users/decorators/roles.decorator'; +import { RolesGuard } from '../../users/guards/roles.guard'; @Controller('categories/categoriesOthers') export class CategoriesOthersController { constructor(private readonly categoriesOthersService: CategoriesOthersService) {} @Post() - public async create(@Body() createStructureDto: CreateCategoriesOthers) { - await this.categoriesOthersService.create(createStructureDto); + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + public async create(@Body() createStructureDto: CreateCategoriesOthers): Promise<CategoriesOthers> { + return this.categoriesOthersService.create(createStructureDto); } @Get() diff --git a/src/categories/schemas/categoriesFormationsModule.schema.ts b/src/categories/schemas/categoriesFormationsModule.schema.ts index 6be322e9c34eb02ca06e497d7d54ab98830dde44..4eaedd6da48c2012e6e63684db7febaf75102816 100644 --- a/src/categories/schemas/categoriesFormationsModule.schema.ts +++ b/src/categories/schemas/categoriesFormationsModule.schema.ts @@ -4,14 +4,11 @@ import { Document } from 'mongoose'; export type CategoriesFormationsModuleDocument = CategoriesFormationsModule & Document; export class CategoriesFormationsModule { - id: number; + id: string; display_id: string; display_name: string; digest: string; text: string; - url: string; - receipt_time: string; - last_update_time: string; } export const CategoriesFormationsModuleSchema = SchemaFactory.createForClass(CategoriesFormationsModule); diff --git a/src/categories/services/categories-accompagnement.service.spec.ts b/src/categories/services/categories-accompagnement.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2690850884d95e003009cd4395d8d7125889d03b --- /dev/null +++ b/src/categories/services/categories-accompagnement.service.spec.ts @@ -0,0 +1,27 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema'; +import { CategoriesAccompagnementService } from './categories-accompagnement.service'; + +describe('CategoriesAccompagnementService', () => { + let service: CategoriesAccompagnementService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + CategoriesAccompagnementService, + { + provide: getModelToken('CategoriesAccompagnement'), + useValue: CategoriesAccompagnement, + }, + ], + }).compile(); + + service = module.get<CategoriesAccompagnementService>(CategoriesAccompagnementService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/categories/services/categories-formations.service.spec.ts b/src/categories/services/categories-formations.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f079fdd4611597a976174206557113ead3b3194 --- /dev/null +++ b/src/categories/services/categories-formations.service.spec.ts @@ -0,0 +1,27 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesFormations } from '../schemas/categoriesFormations.schema'; +import { CategoriesFormationsService } from './categories-formations.service'; + +describe('CategoriesFormationsService', () => { + let service: CategoriesFormationsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + CategoriesFormationsService, + { + provide: getModelToken('CategoriesFormations'), + useValue: CategoriesFormations, + }, + ], + }).compile(); + + service = module.get<CategoriesFormationsService>(CategoriesFormationsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/categories/services/categories-others.service.spec.ts b/src/categories/services/categories-others.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c25eb3618f8bd75287bb29c7ebc52ba3256f33c --- /dev/null +++ b/src/categories/services/categories-others.service.spec.ts @@ -0,0 +1,27 @@ +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesOthers } from '../schemas/categoriesOthers.schema'; +import { CategoriesOthersService } from './categories-others.service'; + +describe('CategoriesFormationsService', () => { + let service: CategoriesOthersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + CategoriesOthersService, + { + provide: getModelToken('CategoriesOthers'), + useValue: CategoriesOthers, + }, + ], + }).compile(); + + service = module.get<CategoriesOthersService>(CategoriesOthersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/configuration/configuration.service.spec.ts b/src/configuration/configuration.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5ddba0b94c2d6b95b5846ca549160d01bf42275 --- /dev/null +++ b/src/configuration/configuration.service.spec.ts @@ -0,0 +1,88 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigurationService } from './configuration.service'; + +describe('ConfigurationService', () => { + let service: ConfigurationService; + + process.env.NODE_ENV = 'local'; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ConfigurationService], + }).compile(); + + service = module.get<ConfigurationService>(ConfigurationService); + }); + + describe('initialisation', () => { + const OLD_ENV = process.env; + beforeEach(async () => { + jest.resetModules(); // Most important - it clears the cache + process.env = { ...OLD_ENV }; // Make a copy + process.env.NODE_ENV = 'dev'; + }); + + afterAll(() => { + process.env = OLD_ENV; // Restore old environment + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + it('should init with dev conf', () => { + process.env.NODE_ENV = 'dev'; + expect(service.config.host).toBe('resin-dev.grandlyon.com'); + }); + }); + + describe('initialisation production', () => { + const OLD_ENV = process.env; + beforeEach(async () => { + jest.resetModules(); // Most important - it clears the cache + process.env = { ...OLD_ENV }; // Make a copy + process.env.NODE_ENV = 'production'; + }); + + afterAll(() => { + process.env = OLD_ENV; // Restore old environment + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + it('should init with prod conf', () => { + process.env.NODE_ENV = 'production'; + expect(service.config.host).toBe('resin.grandlyon.com'); + }); + }); + + describe('validateUser', () => { + it('should be local conf', () => { + process.env.NODE_ENV = 'local'; + expect(service.isLocalConf()).toBe(true); + }); + }); + + describe('get config', () => { + it('should get config', () => { + const config = service.config; + expect(Object.keys(config).length).toBe(9); + expect(Object.keys(config)).toEqual( + expect.arrayContaining([ + 'url', + 'token', + 'host', + 'protocol', + 'port', + 'from', + 'from_name', + 'replyTo', + 'templates', + ]) + ); + }); + }); +}); diff --git a/src/mailer/mailer.service.spec.ts b/src/mailer/mailer.service.spec.ts index 2fd12e46668bdc0cc4a32940715c2a7fc9043cdf..f3e369fba79fd3b542e6d4e5865b05db6103e402 100644 --- a/src/mailer/mailer.service.spec.ts +++ b/src/mailer/mailer.service.spec.ts @@ -1,10 +1,15 @@ -import { HttpModule } from '@nestjs/common'; +import { HttpModule, HttpService } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; +import { of, throwError } from 'rxjs'; +import { AxiosResponse } from 'axios'; import { ConfigurationService } from '../configuration/configuration.service'; import { MailerService } from './mailer.service'; +import * as fs from 'fs'; +import * as path from 'path'; describe('MailerService', () => { let service: MailerService; + let httpService: HttpService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -13,33 +18,104 @@ describe('MailerService', () => { }).compile(); service = module.get<MailerService>(MailerService); + httpService = module.get<HttpService>(HttpService); }); it('should be defined', () => { expect(service).toBeDefined(); }); - - it('should send email', async () => { - const result = "AxiosResponse" - jest.spyOn(service, 'send').mockImplementation(async (): Promise<any> => result); - expect(await service.send("to", "subject", "html")).toBe(result); + + describe('email sending', () => { + it('should send email', async () => { + const result: AxiosResponse = { + data: { + status: 200, + content: { + success: true, + response: [7010], + }, + }, + status: 200, + statusText: 'OK', + headers: {}, + config: {}, + }; + jest.spyOn(httpService, 'post').mockImplementationOnce(() => of(result)); + expect(await service.send('a@a.com', 'test', '<p>This is a test</p>')).toBe(result); + }); + + it('should not send email', async () => { + const result: AxiosResponse = { + data: { + errors: ['Subject cannot be blank'], + detail: 'There was a validation error', + status: 400, + type: 'validation_error', + title: 'There was a validation error', + }, + status: 400, + statusText: 'KO', + headers: {}, + config: {}, + }; + jest.spyOn(httpService, 'post').mockImplementationOnce(() => throwError(result)); + try { + await service.send('a@a.com', 'test', '<p>This is a test</p>'); + } catch (e) { + expect(e.data.detail).toBe('There was a validation error'); + expect(e.status).toBe(400); + } + }); }); - it('should get template location', async () => { - const result = "/path/to/template"; - jest.spyOn(service, 'getTemplateLocation').mockImplementation(() => {return result}); - expect(await service.getTemplateLocation("filename")).toBe(result); + describe('template location', () => { + it('should get template location', async () => { + jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/template'); + jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true); + expect(service.getTemplateLocation('filename')).toBe('/path/to/template'); + }); + + it('should not get template location', async () => { + const filename = 'templateFile'; + jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/filename'); + jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false); + try { + expect(service.getTemplateLocation(filename)).toBe('/path/to/template'); + } catch (e) { + expect(e.message).toBe(`Email template '${filename}' cannot be found in ./src/mailer/mail-templates`); + } + }); }); - it('should load Json Config', async () => { - const result = null; - jest.spyOn(service, 'loadJsonConfig').mockImplementation(async (): Promise<any> => result); - expect(await service.loadJsonConfig("filename")).toBe(result); + describe('json config', () => { + it('should get template location', async () => { + jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/template'); + jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true); + const data = { test: 'test value', value: 'this is the value' }; + const buf = Buffer.from(JSON.stringify(data), 'utf8'); + jest.spyOn(fs, 'readFileSync').mockImplementationOnce(() => buf); + jest.spyOn(JSON, 'parse').mockImplementationOnce(() => JSON.stringify(data)); + expect(service.loadJsonConfig('filename')).toStrictEqual(JSON.stringify(data)); + }); + + it('should not get template location', async () => { + const filename = 'templateFile'; + jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/filename'); + jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false); + try { + expect(service.loadJsonConfig(filename)).toBe('/path/to/template'); + } catch (e) { + expect(e.message).toBe( + `Email json definition file '${filename}' cannot be found in ./src/mailer/mail-templates` + ); + } + }); }); it('should add signature', async () => { - const result = "signed html"; - jest.spyOn(service, 'addSignature').mockImplementation(() => {return result}); - expect(await service.addSignature("html")).toBe(result); + const test = '<p>test email</p>'; + expect(service.addSignature(test)).toBe( + '<p>test email</p><br /><br /><p>L’équipe projet inclusion numérique.</p><img src="http://localhost:4200/assets/logos/resin.jpg" alt="Logo resin" width="168" height="58"><br /><br /><p>Ce mail est automatique. Merci de ne pas y répondre.</p>' + ); }); }); diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts index c19d4b2b2830a1ba83c34669ab195df78c90a9ff..2914fd1383297e0f862b4405020ca8ed4b61e903 100644 --- a/src/mailer/mailer.service.ts +++ b/src/mailer/mailer.service.ts @@ -71,6 +71,7 @@ export class MailerService { } return ejsPath; } + /** * Load email json config file from config directory and given filename. Also, check if file exists * diff --git a/src/newsletter/newsletter.service.ts b/src/newsletter/newsletter.service.ts index 57dab32d153c9d0843eca5e005a49da16123853f..89020eddbaa0808f25f0cedd9c30fba7bc29d1f0 100644 --- a/src/newsletter/newsletter.service.ts +++ b/src/newsletter/newsletter.service.ts @@ -1,6 +1,6 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Model, Types } from 'mongoose'; +import { Model } from 'mongoose'; import { INewsletterSubscription } from './interface/newsletter-subscription.interface'; import { NewsletterSubscription, NewsletterSubscriptionDocument } from './newsletter-subscription.schema'; diff --git a/src/structures/structures.controller.spec.ts b/src/structures/structures.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1eee0eaaa2d68dcb9b33924f6c2c58ad684808e2 --- /dev/null +++ b/src/structures/structures.controller.spec.ts @@ -0,0 +1,67 @@ +import { HttpService } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { CategoriesAccompagnementServiceMock } from '../../test/mock/services/categoriesAccompagnement.mock.service'; +import { CategoriesFormationsServiceMock } from '../../test/mock/services/categoriesFormations.mock.service'; +import { CategoriesOthersServiceMock } from '../../test/mock/services/categoriesOthers.mock.service'; +import { HttpServiceMock } from '../../test/mock/services/http.mock.service'; +import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service'; +import { TempUserServiceMock } from '../../test/mock/services/tempUser.mock.service'; +import { UsersServiceMock } from '../../test/mock/services/user.mock.service'; +import { CategoriesAccompagnementService } from '../categories/services/categories-accompagnement.service'; +import { CategoriesFormationsService } from '../categories/services/categories-formations.service'; +import { CategoriesOthersService } from '../categories/services/categories-others.service'; +import { TempUserService } from '../temp-user/temp-user.service'; +import { UsersService } from '../users/users.service'; +import { StructuresService } from './services/structures.service'; +import { StructuresController } from './structures.controller'; +describe('AuthController', () => { + let controller: StructuresController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + controllers: [StructuresController], + providers: [ + { + provide: HttpService, + useClass: HttpServiceMock, + }, + { + provide: StructuresService, + useClass: StructuresServiceMock, + }, + { + provide: UsersService, + useClass: UsersServiceMock, + }, + { + provide: TempUserService, + useClass: TempUserServiceMock, + }, + { + provide: CategoriesAccompagnementService, + useClass: CategoriesAccompagnementServiceMock, + }, + { + provide: CategoriesOthersService, + useClass: CategoriesOthersServiceMock, + }, + { + provide: CategoriesFormationsService, + useClass: CategoriesFormationsServiceMock, + }, + ], + }).compile(); + + controller = module.get<StructuresController>(StructuresController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + //TODO: test structure controler endpoint + //create, search, updateAccountVerified, update, findAll, findAllFormated, isClaimed + //updateStructureLinkedClaim, countCategories, searchAddress, find, findWithOwners + //delete, addOwner, join, joinValidation, removeOwner, reportStructureError +}); diff --git a/test/mock/guards/jwt-auth.mock.guard.ts b/test/mock/guards/jwt-auth.mock.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7392d9b9b5274778a22d37dc1b683ae0802ab1e --- /dev/null +++ b/test/mock/guards/jwt-auth.mock.guard.ts @@ -0,0 +1,2 @@ +import { CanActivate } from '@nestjs/common'; +export const mockJwtAuthGuard: CanActivate = { canActivate: jest.fn(() => true) }; diff --git a/test/mock/guards/role.mock.guard.ts b/test/mock/guards/role.mock.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f93ccdc0a52c5af9006e81442e00e6327664d3c --- /dev/null +++ b/test/mock/guards/role.mock.guard.ts @@ -0,0 +1,2 @@ +import { CanActivate } from '@nestjs/common'; +export const mockRoleGuard: CanActivate = { canActivate: jest.fn(() => true) }; diff --git a/test/mock/services/auth.mock.service.ts b/test/mock/services/auth.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..07d1d02da19bdb23dbcc9091f19daa557bfdc7be --- /dev/null +++ b/test/mock/services/auth.mock.service.ts @@ -0,0 +1,30 @@ +import { LoginDto } from '../../../src/auth/login-dto'; + +export class AuthServiceMock { + login(loginDto: LoginDto) { + if (loginDto.email === 'paula.dubois@mii.com') { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: true, + email: 'pauline.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + name: 'DUBOIS', + surname: 'Paula', + }; + } + if (loginDto.email === 'jacques.dupont@mii.com') { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: false, + email: 'jacques.dupont@mii.com', + role: 0, + }; + } + return null; + } +} diff --git a/test/mock/services/categoriesAccompagnement.mock.service.ts b/test/mock/services/categoriesAccompagnement.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..00856ec270f151f9441339379269f479933c996f --- /dev/null +++ b/test/mock/services/categoriesAccompagnement.mock.service.ts @@ -0,0 +1,63 @@ +export class CategoriesAccompagnementServiceMock { + create() { + return { + modules: [ + { + id: 'tst', + text: 'Test', + }, + { + id: 'antst', + text: 'Another test', + }, + ], + name: 'test', + id: 'test', + __v: 0, + }; + } + + findAll() { + return [ + { + modules: [ + { + id: 'accompagnantCaf', + text: 'Accompagnant CAF', + }, + { + id: 'poleEmploi', + text: 'Pôle Emploi', + }, + { + id: 'cpam', + text: 'CPAM', + }, + { + id: 'impots', + text: 'Impôts', + }, + { + id: 'logement', + text: 'Logement', + }, + { + id: 'carsat', + text: 'CARSAT', + }, + { + id: 'demarcheMetropolitaine', + text: 'Démarches Métropolitaines', + }, + { + id: 'autres', + text: 'Autres', + }, + ], + name: 'Accompagnement des démarches', + id: 'proceduresAccompaniment', + __v: 0, + }, + ]; + } +} diff --git a/test/mock/services/categoriesFormations.mock.service.ts b/test/mock/services/categoriesFormations.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..da95bfec9da60a4845517c113d0a3d9a55dbfb98 --- /dev/null +++ b/test/mock/services/categoriesFormations.mock.service.ts @@ -0,0 +1,424 @@ +export class CategoriesFormationsServiceMock { + create() { + return { + modules: [ + { + id: 'tst', + text: 'Test', + }, + { + id: 'antst', + text: 'Another test', + }, + ], + name: 'test', + id: 'test', + __v: 0, + }; + } + + findAll() { + return [ + { + _id: '5fbb934180a5c257dc0161f6', + modules: [ + { + id: '84', + display_id: '84', + display_name: 'Modules APTIC - n°84', + digest: 'Pôle Emploi : faire ses déclarations en ligne', + text: 'Pôle Emploi : faire ses déclarations en ligne', + }, + { + id: '85', + display_id: '85', + display_name: 'Modules APTIC - n°85', + digest: 'Déclarer ses revenus en ligne et découvertes des services proposés', + text: 'Déclarer ses revenus en ligne et découvertes des services proposés', + }, + { + id: '86', + display_id: '86', + display_name: 'Modules APTIC - n°86', + digest: 'Accéder à ses droits sociaux et les gérer en ligne (RSA…)', + text: 'Accéder à ses droits sociaux et les gérer en ligne (RSA…)', + }, + { + id: '87', + display_id: '87', + display_name: 'Modules APTIC - n°87', + digest: 'Ouvrir et gérer son dossier de retraite (CNAF/CARSAT) en ligne', + text: 'Ouvrir et gérer son dossier de retraite (CNAF/CARSAT) en ligne', + }, + { + id: '88', + display_id: '88', + display_name: 'Modules APTIC - n°88', + digest: "Gérer son abonnement et ses factures d'électricité/gaz en ligne", + text: "Gérer son abonnement et ses factures d'électricité/gaz en ligne", + }, + { + id: '89', + display_id: '89', + display_name: 'Modules APTIC - n°89', + digest: "Gérer ses droits d'assuré social en ligne/sur internet", + text: "Gérer ses droits d'assuré social en ligne/sur internet", + }, + { + id: '93', + display_id: '93', + display_name: 'Modules APTIC - n°93', + digest: 'Plateforme Ameli.fr : la sécurité sociale en ligne', + text: 'Plateforme Ameli.fr : la sécurité sociale en ligne', + }, + { + id: '95', + display_id: '95', + display_name: 'Modules APTIC - n°95', + digest: 'Plateforme France Connect', + text: 'Plateforme France Connect', + }, + ], + name: 'Accès aux droits', + id: 'accessRight', + __v: 0, + }, + { + modules: [ + { + id: '260', + display_id: '260', + display_name: 'Modules APTIC - n°260', + digest: 'Maitrise de l’environnement d’un ordinateur (clavier, souris)', + text: 'Maitrise de l’environnement d’un ordinateur (clavier, souris)', + }, + { + id: '1', + display_id: '1', + display_name: 'Modules APTIC - n°1', + digest: 'Composantes et facettes de l’identité numérique', + text: 'Composantes et facettes de l’identité numérique', + }, + { + id: '11', + display_id: '11', + display_name: 'Modules APTIC - n°11', + digest: 'Internet : fonctionnement et outils de navigation web', + text: 'Internet : fonctionnement et outils de navigation web', + }, + { + id: '38', + display_id: '38', + display_name: 'Modules APTIC - n°38', + digest: 'Le smartphone : principes de fonctionnement', + text: 'Le smartphone : principes de fonctionnement', + }, + { + id: '48', + display_id: '48', + display_name: 'Modules APTIC - n°48', + digest: 'Internet : envoyer, recevoir, gérer ses emails', + text: 'Internet : envoyer, recevoir, gérer ses emails', + }, + { + id: '74', + display_id: '74', + display_name: 'Modules APTIC - n°74', + digest: 'Smartphones et Tablettes sous Androïd', + text: 'Smartphones et Tablettes sous Androïd', + }, + { + id: '77', + display_id: '77', + display_name: 'Modules APTIC - n°77', + digest: "Smartphone : Les principaux gestes pour l'écran tactile", + text: "Smartphone : Les principaux gestes pour l'écran tactile", + }, + ], + name: 'Les compétences de base', + id: 'baseSkills', + __v: 0, + }, + { + modules: [ + { + id: '3', + display_id: '3', + display_name: 'Modules APTIC - n°3', + digest: "Être parent à l'ère numérique: connaître les usages, jouer son rôle de parent", + text: "Être parent à l'ère numérique: connaître les usages, jouer son rôle de parent", + }, + { + id: '22', + display_id: '22', + display_name: 'Modules APTIC - n°22', + digest: 'Découvrir l’univers des jeux vidéo - 22', + text: 'Découvrir l’univers des jeux vidéo', + }, + { + id: '82', + display_id: '82', + display_name: 'Modules APTIC - n°82', + digest: 'Suivre la scolarité de son enfant - 82', + text: 'Suivre la scolarité de son enfant', + }, + { + id: '94', + display_id: '94', + display_name: 'Modules APTIC - n°94', + digest: "Découvrir les services en ligne de l'enfance de votre commune - 94", + text: "Découvrir les services en ligne de l'enfance de votre commune", + }, + ], + name: 'Aide à la parentalité', + id: 'parentingHelp', + __v: 0, + }, + { + modules: [ + { + id: '6', + display_id: '6', + display_name: 'Modules APTIC - n°6', + digest: "Utiliser les réseaux sociaux pour sa recherche d'emploi", + text: "Utiliser les réseaux sociaux pour sa recherche d'emploi", + }, + { + id: '20', + display_id: '20', + display_name: 'Modules APTIC - n°20', + digest: 'Panorama des plateformes de recherche d’emploi', + text: 'Panorama des plateformes de recherche d’emploi', + }, + { + id: '66', + display_id: '66', + display_name: 'Modules APTIC - n°66', + digest: 'Traitement de texte : découverte', + text: 'Traitement de texte : découverte', + }, + { + id: '67', + display_id: '67', + display_name: 'Modules APTIC - n°67', + digest: 'Traitement de texte : utilisation de base', + text: 'Traitement de texte : utilisation de base', + }, + { + id: '68', + display_id: '68', + display_name: 'Modules APTIC - n°68', + digest: 'Traitement de texte : utilisation avancée', + text: 'Traitement de texte : utilisation avancée', + }, + { + id: '69', + display_id: '69', + display_name: 'Modules APTIC - n°69', + digest: 'Tableur : découverte', + text: 'Tableur : découverte', + }, + { + id: '124', + display_id: '124', + display_name: 'Modules APTIC - n°124', + digest: 'Réalisation CV', + text: 'Réalisation CV', + }, + { + id: '125', + display_id: '125', + display_name: 'Modules APTIC - n°125', + digest: 'Diffuser son CV en ligne', + text: 'Diffuser son CV en ligne', + }, + { + id: '127', + display_id: '127', + display_name: 'Modules APTIC - n°127', + digest: "Organiser sa recherche d'emploi", + text: "Organiser sa recherche d'emploi", + }, + ], + name: 'Insertion sociale et professionnelle', + id: 'socialAndProfessional', + __v: 0, + }, + { + modules: [ + { + id: '6', + display_id: '6', + display_name: 'Modules APTIC - n°6', + digest: "Utiliser les réseaux sociaux pour sa recherche d'emploi", + text: "Utiliser les réseaux sociaux pour sa recherche d'emploi", + }, + { + id: '20', + display_id: '20', + display_name: 'Modules APTIC - n°20', + digest: 'Panorama des plateformes de recherche d’emploi', + text: 'Panorama des plateformes de recherche d’emploi', + }, + { + id: '66', + display_id: '66', + display_name: 'Modules APTIC - n°66', + digest: 'Traitement de texte : découverte', + text: 'Traitement de texte : découverte', + }, + { + id: '67', + display_id: '67', + display_name: 'Modules APTIC - n°67', + digest: 'Traitement de texte : utilisation de base', + text: 'Traitement de texte : utilisation de base', + }, + { + id: '68', + display_id: '68', + display_name: 'Modules APTIC - n°68', + digest: 'Traitement de texte : utilisation avancée', + text: 'Traitement de texte : utilisation avancée', + }, + { + id: '69', + display_id: '69', + display_name: 'Modules APTIC - n°69', + digest: 'Tableur : découverte', + text: 'Tableur : découverte', + }, + { + id: '124', + display_id: '124', + display_name: 'Modules APTIC - n°124', + digest: 'Réalisation CV', + text: 'Réalisation CV', + }, + { + id: '125', + display_id: '125', + display_name: 'Modules APTIC - n°125', + digest: 'Diffuser son CV en ligne', + text: 'Diffuser son CV en ligne', + }, + { + id: '127', + display_id: '127', + display_name: 'Modules APTIC - n°127', + digest: "Organiser sa recherche d'emploi", + text: "Organiser sa recherche d'emploi", + }, + ], + name: 'Insertion sociale et professionnelle', + id: 'socialAndProfessional', + __v: 0, + }, + { + modules: [ + { + id: '2', + display_id: '2', + display_name: 'Modules APTIC - n°2', + digest: 'Les conduites à risques et les bons usages du numérique - 02', + text: 'Les conduites à risques et les bons usages du numérique', + }, + { + id: '5', + display_id: '5', + display_name: 'Modules APTIC - n°5', + digest: 'Découvrir les réseaux sociaux : définition, fonctionnement - 05', + text: 'Découvrir les réseaux sociaux : définition, fonctionnement', + }, + { + id: '9', + display_id: '9', + display_name: 'Modules APTIC - n°9', + digest: 'Fablab : charte, valeurs et panorama des outils numériques - 09', + text: 'Fablab : charte, valeurs et panorama des outils numériques', + }, + { + id: '28', + display_id: '28', + display_name: 'Modules APTIC - n°28', + digest: 'Les paiements en ligne - 28', + text: 'Les paiements en ligne', + }, + { + id: '34', + display_id: '34', + display_name: 'Modules APTIC - n°34', + digest: 'Internet : comprendre les principes de fonctionnement - 35', + text: 'Internet : comprendre les principes de fonctionnement', + }, + { + id: '39', + display_id: '39', + display_name: 'Modules APTIC - n°39', + digest: 'Techniques de vérification de l’information - 39', + text: 'Techniques de vérification de l’information', + }, + { + id: '42', + display_id: '42', + display_name: 'Modules APTIC - n°42', + digest: 'Créer et paramétrer un compte Google - 42', + text: 'Créer et paramétrer un compte Google', + }, + { + id: '51', + display_id: '51', + display_name: 'Modules APTIC - n°51', + digest: 'Gérer ses données : sauvegarde en ligne (dans le cloud) - 51', + text: 'Gérer ses données : sauvegarde en ligne (dans le cloud)', + }, + { + id: '52', + display_id: '52', + display_name: 'Modules APTIC - n°52', + digest: 'Gérer ses données : sauvegardes locales (disques durs externes, clé USB) - 52', + text: 'Gérer ses données : sauvegardes locales (disques durs externes, clé USB)', + }, + { + id: '54', + display_id: '54', + display_name: 'Modules APTIC - n°54', + digest: 'Classer, gérer et partager ses photos - 54', + text: 'Classer, gérer et partager ses photos', + }, + { + id: '65', + display_id: '65', + display_name: 'Modules APTIC - n°65', + digest: 'Skype et autres outils de visioconférence - 65', + text: 'Skype et autres outils de visioconférence', + }, + { + id: '96', + display_id: '96', + display_name: 'Modules APTIC - n°96', + digest: 'Connaitre et gérer son identité numérique - 96', + text: 'Connaitre et gérer son identité numérique', + }, + { + id: '97', + display_id: '97', + display_name: 'Modules APTIC - n°97', + digest: 'Nettoyer son identité numérique - 97', + text: 'Nettoyer son identité numérique', + }, + { + id: '98', + display_id: '98', + display_name: 'Modules APTIC - n°98', + digest: 'Effacer ses traces sur le web, protéger ses données personnelles - 98', + text: 'Effacer ses traces sur le web, protéger ses données personnelles', + }, + ], + name: 'Culture et sécurité numérique', + id: 'digitalCultureSecurity', + __v: 0, + }, + ]; + } +} diff --git a/test/mock/services/categoriesOthers.mock.service.ts b/test/mock/services/categoriesOthers.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b952c68df7b252f1ef99110583fdba025724bce --- /dev/null +++ b/test/mock/services/categoriesOthers.mock.service.ts @@ -0,0 +1,49 @@ +export class CategoriesOthersServiceMock { + create() { + return { + modules: [ + { + id: 'tst', + text: 'Test', + }, + ], + name: 'test', + id: 'test', + __v: 0, + }; + } + + findAll() { + return [ + { + modules: [ + { + id: "Personnes en situation d'illetrisme", + text: "Personnes en situation d'illetrisme", + }, + { + id: 'Langue étrangère (anglais)', + text: 'Langue étrangère (anglais)', + }, + { + id: 'Langues étrangères (autres)', + text: 'Langues étrangères (autres)', + }, + { + id: 'Surdité', + text: 'Surdité', + }, + { + id: 'Déficience visuelle', + text: 'Déficience visuelle', + }, + { + id: 'Handicap moteur', + text: 'Handicap moteur', + }, + ], + name: 'Accompagnement des publics spécifique', + }, + ]; + } +} diff --git a/test/mock/services/http.mock.service.ts b/test/mock/services/http.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..2295a70104969be97a1218df189c2e5a1f145d8e --- /dev/null +++ b/test/mock/services/http.mock.service.ts @@ -0,0 +1,8 @@ +import { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { Observable } from 'rxjs'; + +export class HttpServiceMock { + get<T = any>(url: string, config?: AxiosRequestConfig): Observable<AxiosResponse<T>> { + return; + } +} diff --git a/test/mock/services/jwt.mock.service.ts b/test/mock/services/jwt.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5c81b3344c930122a768d11f9db8ca49c55f67c --- /dev/null +++ b/test/mock/services/jwt.mock.service.ts @@ -0,0 +1,5 @@ +export class JwtServiceMock { + sign() { + return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4'; + } +} diff --git a/test/mock/services/newsletter.mock.service.ts b/test/mock/services/newsletter.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7b4d640c0d9cd298961fe88a660aeebbb158b1d --- /dev/null +++ b/test/mock/services/newsletter.mock.service.ts @@ -0,0 +1,58 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; + +export class NewsletterServiceMock { + findAll() { + return [ + { + _id: '607063e22b016303ad97f367', + email: 'xavier@mii.fr', + createdAt: '2021-04-09T10:14:01.939Z', + updatedAt: '2021-04-09T10:14:01.939Z', + }, + { + _id: '607063e22b016303ad97f368', + email: 'paula.dubois@mii.com', + createdAt: '2021-04-09T10:14:01.939Z', + updatedAt: '2021-04-09T10:14:01.939Z', + }, + { + _id: '607063e22b016303ad97f369', + email: 'a@a.com', + createdAt: '2021-04-09T10:14:01.939Z', + updatedAt: '2021-04-09T10:14:01.939Z', + }, + ]; + } + + searchNewsletterSubscription(search: string) { + if (search === 'a@a.com') { + return [ + { + _id: '607063e22b016303ad97f369', + email: 'a@a.com', + createdAt: '2021-04-09T10:14:01.939Z', + updatedAt: '2021-04-09T10:14:01.939Z', + }, + ]; + } + return []; + } + + countNewsletterSubscriptions() { + return new Promise((resolve) => { + resolve(246); + }); + } + + deleteOneEmail(email: string) { + if (email === 'test@test.com') { + throw new HttpException('Invalid email', HttpStatus.BAD_REQUEST); + } + return { + _id: '607063e22b016303ad97f397', + email: 'a@a.com', + createdAt: '2021-04-09T10:14:01.939Z', + updatedAt: '2021-04-09T10:14:01.939Z', + }; + } +} diff --git a/test/mock/services/structures.mock.service.ts b/test/mock/services/structures.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..276dfd05e723b0aaf0e74b3d991818fb55f7f007 --- /dev/null +++ b/test/mock/services/structures.mock.service.ts @@ -0,0 +1,332 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; + +export class StructuresServiceMock { + findOne(id) { + if (id === '6093ba0e2ab5775cfc01ed3e') { + return { + _id: '6093ba0e2ab5775cfc01ed3e', + coord: [4.8498155, 45.7514817], + equipmentsAndServices: ['wifiEnAccesLibre'], + digitalCultureSecurity: [], + parentingHelp: [], + socialAndProfessional: [], + accessRight: [], + baseSkills: [], + proceduresAccompaniment: [], + publicsAccompaniment: [], + publics: ['adultes'], + labelsQualifications: [], + accessModality: ['telephoneVisio'], + structureType: null, + structureName: 'a', + description: null, + lockdownActivity: null, + address: { + numero: null, + street: 'Rue Alphonse Daudet', + commune: 'Lyon 7ème Arrondissement', + }, + contactMail: '', + contactPhone: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + linkedin: null, + hours: { + monday: { + open: false, + time: [], + }, + tuesday: { + open: false, + time: [], + }, + wednesday: { + open: false, + time: [], + }, + thursday: { + open: false, + time: [], + }, + friday: { + open: false, + time: [], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + pmrAccess: false, + exceptionalClosures: null, + otherDescription: null, + nbComputers: 1, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + nbScanners: 1, + freeWorkShop: false, + accountVerified: true, + createdAt: '2021-05-06T09:42:38.000Z', + updatedAt: '2021-05-06T09:42:50.000Z', + __v: 0, + }; + } + + if (id === '6903ba0e2ab5775cfc01ed4d') { + return { + _id: '6903ba0e2ab5775cfc01ed4d', + structureType: null, + digitalCultureSecurity: ['2', '5', '9', '28', '34', '39', '42', '51', '52', '54', '65', '96', '97', '98'], + parentingHelp: ['3', '22', '82', '94'], + socialAndProfessional: ['6', '20', '66', '67', '68', '69', '124', '125', '127'], + accessRight: ['84', '85', '86', '87', '88', '89', '93', '95'], + baseSkills: ['260', '1', '11', '38', '48', '74', '77'], + proceduresAccompaniment: ['cpam', 'impots', 'carsat', 'poleEmploi'], + publics: ['toutPublic'], + labelsQualifications: ['passNumerique', 'espacePublicNumeriqueepn'], + accessModality: ['accesLibre', 'telephoneVisio', 'surRdv'], + freeWorkShop: false, + createdAt: '2020-11-16T09:30:00.000Z', + updatedAt: '2021-04-12T08:48:00.000Z', + structureName: "L'Atelier Numérique", + description: + "L'Atelier Numérique est l'Espace Public Numérique des Centres Sociaux de Rillieux-la-Pape, ayant pour mission la médiation numérique pour toutes et tous.", + lockdownActivity: + 'accesLibres, permanences numériques téléphoniques, cours et ateliers à distance, formations professionnelles.', + contactPhone: '', + contactMail: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + pmrAccess: true, + exceptionalClosures: '', + jaccompagneLesUsagersDansLeursDemarchesEnLigne: true, + publicsAccompaniment: [], + autresAccompagnements: '', + equipmentsAndServices: ['ordinateurs', 'tablettes'], + nbComputers: 16, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + hours: { + monday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + tuesday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + wednesday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + thursday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + friday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + ], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + __v: 0, + address: { + numero: '30 bis', + street: 'Avenue Leclerc', + commune: 'Rillieux-la-Pape', + }, + coord: [4.9036773, 45.8142196], + accountVerified: true, + linkedin: null, + nbScanners: 1, + otherDescription: null, + }; + } + + return null; + } + + deleteOne(id) { + if (id === '6903ba0e2ab5775cfc01ed4d') { + return { + _id: '6903ba0e2ab5775cfc01ed4d', + structureType: null, + digitalCultureSecurity: ['2', '5', '9', '28', '34', '39', '42', '51', '52', '54', '65', '96', '97', '98'], + parentingHelp: ['3', '22', '82', '94'], + socialAndProfessional: ['6', '20', '66', '67', '68', '69', '124', '125', '127'], + accessRight: ['84', '85', '86', '87', '88', '89', '93', '95'], + baseSkills: ['260', '1', '11', '38', '48', '74', '77'], + proceduresAccompaniment: ['cpam', 'impots', 'carsat', 'poleEmploi'], + publics: ['toutPublic'], + labelsQualifications: ['passNumerique', 'espacePublicNumeriqueepn'], + accessModality: ['accesLibre', 'telephoneVisio', 'surRdv'], + freeWorkShop: false, + createdAt: '2020-11-16T09:30:00.000Z', + updatedAt: '2021-04-12T08:48:00.000Z', + structureName: "L'Atelier Numérique", + description: + "L'Atelier Numérique est l'Espace Public Numérique des Centres Sociaux de Rillieux-la-Pape, ayant pour mission la médiation numérique pour toutes et tous.", + lockdownActivity: + 'accesLibres, permanences numériques téléphoniques, cours et ateliers à distance, formations professionnelles.', + contactPhone: '', + contactMail: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + pmrAccess: true, + exceptionalClosures: '', + jaccompagneLesUsagersDansLeursDemarchesEnLigne: true, + publicsAccompaniment: [], + autresAccompagnements: '', + equipmentsAndServices: ['ordinateurs', 'tablettes'], + nbComputers: 16, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + hours: { + monday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + tuesday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + wednesday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + thursday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + { + closing: '17:00', + opening: '13:30', + }, + ], + }, + friday: { + open: true, + time: [ + { + closing: '12:30', + opening: '9:00', + }, + ], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + __v: 0, + address: { + numero: '30 bis', + street: 'Avenue Leclerc', + commune: 'Rillieux-la-Pape', + }, + coord: [4.9036773, 45.8142196], + accountVerified: true, + linkedin: null, + nbScanners: 1, + otherDescription: null, + }; + } + + throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST); + } +} diff --git a/test/mock/services/tempUser.mock.service.ts b/test/mock/services/tempUser.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..8769fa94f3b52596b1a366312475df0f08b05830 --- /dev/null +++ b/test/mock/services/tempUser.mock.service.ts @@ -0,0 +1 @@ +export class TempUserServiceMock {} diff --git a/test/mock/services/user.mock.service.ts b/test/mock/services/user.mock.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..1b44c701fded2d30f041bf14f3ce1c6624f77018 --- /dev/null +++ b/test/mock/services/user.mock.service.ts @@ -0,0 +1,190 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; +import { PendingStructureDto } from '../../../src/admin/dto/pending-structure.dto'; +import { LoginDto } from '../../../src/auth/login-dto'; + +export class UsersServiceMock { + findOne(mail: string, passwordQuery?: boolean) { + if (mail === 'pauline.dupont@mii.com') { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: true, + email: 'pauline.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + name: 'DUPONT', + surname: 'Pauline', + }; + } + if (mail === 'jacques.dupont@mii.com') { + if (passwordQuery) { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: false, + email: 'jacques.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + }; + } + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: false, + email: 'jacques.dupont@mii.com', + role: 0, + }; + } + return null; + } + + findByLogin({ email, password }: LoginDto) { + const user = this.findOne(email, true); + + if (!user) { + throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); + } + + let areEqual = true; + if (password === '1') { + areEqual = false; + } + + if (!areEqual) { + throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); + } + + return user; + } + + getPendingStructures() { + return [ + { + structureId: '6093ba0e2ab5775cfc01ed3e', + structureName: 'a', + userEmail: 'paula.dubois@mii.com', + }, + { + structureId: '6903ba0e2ab5775cfc01ed4d', + structureName: "L'Atelier Numérique", + userEmail: 'jacques.dupont@mii.com', + }, + ]; + } + + validatePendingStructure(): PendingStructureDto[] { + return this.getPendingStructures(); + } + + deleteOneId(id: string) { + if (id === 'tsfsf6296') { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: true, + email: 'pauline.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + name: 'DUPONT', + surname: 'Pauline', + structuresLink: ['abcdefgh', '18sfqfq'], + }; + } + throw new HttpException('Invalid user id', HttpStatus.BAD_REQUEST); + } + + isStructureClaimed(id: string) { + if (id === 'abcdefgh') { + return new Promise((resolve) => { + resolve({ + _id: 'abcdefgh', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: true, + email: 'pauline.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + name: 'DUPONT', + surname: 'Pauline', + structuresLink: ['abcdefgh', '18sfqfq'], + }); + }); + } + return new Promise((resolve) => { + resolve(null); + }); + } + + searchUsers(searchString: string) { + if (searchString === 'a@a.com') { + return [ + { + structureOutdatedMailSent: [], + pendingStructuresLink: ['6001a48e16b08100062e4180'], + structuresLink: [], + newEmail: null, + changeEmailToken: null, + role: 0, + resetPasswordToken: null, + validationToken: + 'b2b6caca1d38ca26d203b5f12b0d925df2928fab8ee7ccf9bbe78802ffa625f5abce825783bc62d0b11be5a90132cf5045a9a7776f01694c63b60bf64b0f680f', + emailVerified: false, + _id: '6036721022462b001334c4bb', + email: 'a@a.com', + name: 'Xavier', + surname: 'NIEL', + phone: '06 11 11 11 11', + password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + __v: 1, + }, + ]; + } + return []; + } + + findAll() { + return [ + { + structureOutdatedMailSent: [], + pendingStructuresLink: ['6001a48e16b08100062e4180'], + structuresLink: [], + newEmail: null, + changeEmailToken: null, + role: 0, + resetPasswordToken: null, + validationToken: + 'b2b6caca1d38ca26d203b5f12b0d925df2928fab8ee7ccf9bbe78802ffa625f5abce825783bc62d0b11be5a90132cf5045a9a7776f01694c63b60bf64b0f680f', + emailVerified: false, + _id: '6036721022462b001334c4bb', + email: 'a@a.com', + name: 'Xavier', + surname: 'NIEL', + phone: '06 11 11 11 11', + password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + __v: 1, + }, + { + structureOutdatedMailSent: [], + pendingStructuresLink: ['6001a3c216b08100062e4169', '601d6aa2c94cf895c4e23860'], + structuresLink: ['6037aa17d8189c0014f62421'], + newEmail: null, + changeEmailToken: null, + role: 0, + resetPasswordToken: null, + validationToken: null, + emailVerified: true, + _id: '6037aa16d8189c0014f6241f', + email: 'ptitduf@mii.com', + name: 'hjhj', + surname: 'hh', + phone: '05 79 87 65 78', + password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.3KdcYb.cLqZzkX2rstcpc2QTgbJ0FlC', + __v: 3, + }, + ]; + } +}