From b187312a336c6da72516380f94c09da86efb38f6 Mon Sep 17 00:00:00 2001 From: Neoprot Date: Wed, 28 Aug 2024 22:13:00 -0300 Subject: [PATCH 1/8] =?UTF-8?q?feat(#79):Mundando=20schema=20Mudando=20o?= =?UTF-8?q?=20schema=20do=20usu=C3=A1rio=20para=20aceitar=20Start=20Points?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joa0V Co-authored-by: DaviMatheus Co-authored-by: natangoatoso --- src/users/interface/user.interface.ts | 1 + src/users/interface/user.schema.ts | 1 + src/users/users.service.ts | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/users/interface/user.interface.ts b/src/users/interface/user.interface.ts index b3066aa..09ab7a1 100644 --- a/src/users/interface/user.interface.ts +++ b/src/users/interface/user.interface.ts @@ -10,5 +10,6 @@ export interface User extends Document { isVerified?: boolean; role?: UserRole; journeys?: mongoose.Types.ObjectId[]; + points?: mongoose.Types.ObjectId[]; subscribedJourneys?: mongoose.Types.ObjectId[]; } diff --git a/src/users/interface/user.schema.ts b/src/users/interface/user.schema.ts index 07abbfb..b4e4e14 100644 --- a/src/users/interface/user.schema.ts +++ b/src/users/interface/user.schema.ts @@ -17,6 +17,7 @@ export const UserSchema = new mongoose.Schema( default: UserRole.ALUNO, }, journeys: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }], + points: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Point' }], subscribedJourneys: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }, ], diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 79f0f18..b91e4cc 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import { Model, Types } from 'mongoose'; import { EmailService } from './email.service'; import { CreateUserDto } from './dtos/create-user.dto'; @@ -80,6 +81,24 @@ export class UsersService { return user; } + async addPointToUser(userId: string, pointId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + const objectId = new Types.ObjectId(pointId); + + if (!user.points) { + user.points = []; + } + + if (!user.points.includes(objectId)) { + user.points.push(objectId); + } + + return user.save(); + } async addJourneyToUser(userId: string, journeyId: string): Promise { const user = await this.userModel.findById(userId).exec(); if (!user) { @@ -98,6 +117,7 @@ export class UsersService { return user.save(); } + async deleteUserById(_id: string): Promise { const result = await this.userModel.deleteOne({ _id }).exec(); if (result.deletedCount === 0) { From 10f9aad2158ddb015a18c448fa941750131789f8 Mon Sep 17 00:00:00 2001 From: DaviMatheus Date: Wed, 28 Aug 2024 23:33:40 -0300 Subject: [PATCH 2/8] [feat#79]-fixing_bug_point_not_getting_added_in_user Co-authored-by: natangoatoso Co-authored-by: Neoprot Co-authored-by: Joa0V --- src/users/users.controller.ts | 12 ++++++++++++ src/users/users.service.ts | 25 +++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index b584cbb..2f8e322 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -72,6 +72,18 @@ export class UsersController { } } + @Patch(':id/add-point') + async addPointToUser( + @Param('id') id: string, + @Body() body: { pointId: string }, + ) { + try { + return await this.usersService.addPointToUser(id, body.pointId); + } catch (error) { + throw error; + } + } + @UseGuards(JwtAuthGuard) @Post(':userId/subscribe/:journeyId') async subscribeJourney( diff --git a/src/users/users.service.ts b/src/users/users.service.ts index b91e4cc..c5bd2c0 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -81,38 +81,39 @@ export class UsersService { return user; } - async addPointToUser(userId: string, pointId: string): Promise { + async addJourneyToUser(userId: string, journeyId: string): Promise { const user = await this.userModel.findById(userId).exec(); if (!user) { throw new NotFoundException(`User with ID ${userId} not found`); } - const objectId = new Types.ObjectId(pointId); + const objectId = new Types.ObjectId(journeyId); - if (!user.points) { - user.points = []; + if (!user.journeys) { + user.journeys = []; } - if (!user.points.includes(objectId)) { - user.points.push(objectId); + if (!user.journeys.includes(objectId)) { + user.journeys.push(objectId); } return user.save(); } - async addJourneyToUser(userId: string, journeyId: string): Promise { + + async addPointToUser(userId: string, pointId: string): Promise { const user = await this.userModel.findById(userId).exec(); if (!user) { throw new NotFoundException(`User with ID ${userId} not found`); } - const objectId = new Types.ObjectId(journeyId); + const objectId = new Types.ObjectId(pointId); - if (!user.journeys) { - user.journeys = []; + if (!user.points) { + user.points = []; } - if (!user.journeys.includes(objectId)) { - user.journeys.push(objectId); + if (!user.points.includes(objectId)) { + user.points.push(objectId); } return user.save(); From 6b7e4e82c58aa5934a60098cd2c291abedf56ea3 Mon Sep 17 00:00:00 2001 From: DaviMatheus Date: Thu, 29 Aug 2024 14:59:55 -0300 Subject: [PATCH 3/8] [feat#79]-refatoting_user_refering_points Co-authored-by: natangoatoso Co-authored-by: Neoprot Co-authored-by: Joa0V --- src/users/interface/user.interface.ts | 1 - src/users/interface/user.schema.ts | 1 - src/users/users.controller.ts | 11 ----------- src/users/users.service.ts | 19 ------------------- 4 files changed, 32 deletions(-) diff --git a/src/users/interface/user.interface.ts b/src/users/interface/user.interface.ts index 09ab7a1..7589e0e 100644 --- a/src/users/interface/user.interface.ts +++ b/src/users/interface/user.interface.ts @@ -9,7 +9,6 @@ export interface User extends Document { verificationToken?: string; isVerified?: boolean; role?: UserRole; - journeys?: mongoose.Types.ObjectId[]; points?: mongoose.Types.ObjectId[]; subscribedJourneys?: mongoose.Types.ObjectId[]; } diff --git a/src/users/interface/user.schema.ts b/src/users/interface/user.schema.ts index b4e4e14..2f23af6 100644 --- a/src/users/interface/user.schema.ts +++ b/src/users/interface/user.schema.ts @@ -16,7 +16,6 @@ export const UserSchema = new mongoose.Schema( enum: Object.values(UserRole), default: UserRole.ALUNO, }, - journeys: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }], points: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Point' }], subscribedJourneys: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }, diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 2f8e322..8979678 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -60,17 +60,6 @@ export class UsersController { async getUsers() { return await this.usersService.getUsers(); } - @Patch(':id/add-journey') - async addJourneyToUser( - @Param('id') id: string, - @Body() body: { journeyId: string }, - ) { - try { - return await this.usersService.addJourneyToUser(id, body.journeyId); - } catch (error) { - throw error; - } - } @Patch(':id/add-point') async addPointToUser( diff --git a/src/users/users.service.ts b/src/users/users.service.ts index c5bd2c0..0c85a8d 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -81,25 +81,6 @@ export class UsersService { return user; } - async addJourneyToUser(userId: string, journeyId: string): Promise { - const user = await this.userModel.findById(userId).exec(); - if (!user) { - throw new NotFoundException(`User with ID ${userId} not found`); - } - - const objectId = new Types.ObjectId(journeyId); - - if (!user.journeys) { - user.journeys = []; - } - - if (!user.journeys.includes(objectId)) { - user.journeys.push(objectId); - } - - return user.save(); - } - async addPointToUser(userId: string, pointId: string): Promise { const user = await this.userModel.findById(userId).exec(); if (!user) { From a3d4fad34d2c72e35a2d9c097c13378402f74c4b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 31 Aug 2024 00:40:32 -0300 Subject: [PATCH 4/8] feat(#80): Visualizar-conteudo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit modificando user schema e adicionando objeto de trilha ao usuário --- src/users/interface/user.interface.ts | 1 + src/users/interface/user.schema.ts | 3 +++ src/users/users.controller.ts | 17 ++++++++++++++ src/users/users.service.ts | 34 +++++++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/src/users/interface/user.interface.ts b/src/users/interface/user.interface.ts index 7589e0e..4e380fb 100644 --- a/src/users/interface/user.interface.ts +++ b/src/users/interface/user.interface.ts @@ -11,4 +11,5 @@ export interface User extends Document { role?: UserRole; points?: mongoose.Types.ObjectId[]; subscribedJourneys?: mongoose.Types.ObjectId[]; + completedTrails?: mongoose.Types.ObjectId[]; } diff --git a/src/users/interface/user.schema.ts b/src/users/interface/user.schema.ts index 2f23af6..764ddee 100644 --- a/src/users/interface/user.schema.ts +++ b/src/users/interface/user.schema.ts @@ -20,6 +20,9 @@ export const UserSchema = new mongoose.Schema( subscribedJourneys: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }, ], + completedTrails: [ + { type: mongoose.Schema.Types.ObjectId, ref: 'Trail'}, + ], }, { timestamps: true, collection: 'users' }, ); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 8979678..e27d59c 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -56,6 +56,13 @@ export class UsersController { return await this.usersService.getSubscribedJourneys(userId); } + @Get(':userId/completedTrails') + async getCompletedTrails( + @Param('userId') userId: string, + ): Promise { + return await this.usersService.getCompletedTrails(userId); + } + @Get() async getUsers() { return await this.usersService.getUsers(); @@ -82,6 +89,7 @@ export class UsersController { return this.usersService.subscribeJourney(userId, journeyId); } + @UseGuards(JwtAuthGuard) @Delete(':userId/unsubscribe/:journeyId') async unsubscribeJourney( @@ -90,6 +98,15 @@ export class UsersController { ) { return this.usersService.unsubscribeJourney(userId, journeyId); } + + @UseGuards(JwtAuthGuard) + @Post(':userId/complete/:trailId') + async completeTrail( + @Param('userId') userId: string, + @Param('trailId') trailId: string, + ) { + return this.usersService.completeTrail(userId, trailId); + } @Get('/:id') async getUserById(@Param('id') id: string) { diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 0c85a8d..fb4dd9e 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -157,6 +157,40 @@ export class UsersService { return user.subscribedJourneys; } + async getCompletedTrails(userId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + return user.completedTrails; + } + + async completeTrail(userId: string, trailId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + const objectId = new Types.ObjectId(trailId); + if (user.completedTrails && user.completedTrails.includes(objectId)) { + throw new ConflictException( + `User already completed trail with ID ${trailId}`, + ); + } + + if (!user.completedTrails) { + user.completedTrails = []; + } + + if (!user.completedTrails.includes(objectId)) { + user.completedTrails.push(objectId); + } + + return user.save(); + } + + async findByEmail(email: string): Promise { return this.userModel.findOne({ email }).exec(); } From d12660245e283ff4c76462dc550117aff4134904 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Sat, 31 Aug 2024 00:52:25 -0300 Subject: [PATCH 5/8] Correcao de conflitos --- src/users/interface/user.interface.ts | 1 + src/users/users.service.ts | 86 ++++++++++++++++----------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/users/interface/user.interface.ts b/src/users/interface/user.interface.ts index 4e380fb..14f5646 100644 --- a/src/users/interface/user.interface.ts +++ b/src/users/interface/user.interface.ts @@ -10,6 +10,7 @@ export interface User extends Document { isVerified?: boolean; role?: UserRole; points?: mongoose.Types.ObjectId[]; + journeys?: mongoose.Types.ObjectId[]; subscribedJourneys?: mongoose.Types.ObjectId[]; completedTrails?: mongoose.Types.ObjectId[]; } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index fb4dd9e..3a56874 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ import { Model, Types } from 'mongoose'; import { EmailService } from './email.service'; import { CreateUserDto } from './dtos/create-user.dto'; @@ -99,6 +98,57 @@ export class UsersService { return user.save(); } + async addJourneyToUser(userId: string, journeyId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + const objectId = new Types.ObjectId(journeyId); + + if (!user.journeys) { + user.journeys = []; + } + + if (!user.journeys.includes(objectId)) { + user.journeys.push(objectId); + } + + return user.save(); + } + + async getCompletedTrails(userId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + return user.completedTrails; + } + + async completeTrail(userId: string, trailId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + const objectId = new Types.ObjectId(trailId); + if (user.completedTrails && user.completedTrails.includes(objectId)) { + throw new ConflictException( + `User already completed trail with ID ${trailId}`, + ); + } + + if (!user.completedTrails) { + user.completedTrails = []; + } + + if (!user.completedTrails.includes(objectId)) { + user.completedTrails.push(objectId); + } + + return user.save(); + } async deleteUserById(_id: string): Promise { const result = await this.userModel.deleteOne({ _id }).exec(); @@ -157,40 +207,6 @@ export class UsersService { return user.subscribedJourneys; } - async getCompletedTrails(userId: string): Promise { - const user = await this.userModel.findById(userId).exec(); - if (!user) { - throw new NotFoundException(`User with ID ${userId} not found`); - } - - return user.completedTrails; - } - - async completeTrail(userId: string, trailId: string): Promise { - const user = await this.userModel.findById(userId).exec(); - if (!user) { - throw new NotFoundException(`User with ID ${userId} not found`); - } - - const objectId = new Types.ObjectId(trailId); - if (user.completedTrails && user.completedTrails.includes(objectId)) { - throw new ConflictException( - `User already completed trail with ID ${trailId}`, - ); - } - - if (!user.completedTrails) { - user.completedTrails = []; - } - - if (!user.completedTrails.includes(objectId)) { - user.completedTrails.push(objectId); - } - - return user.save(); - } - - async findByEmail(email: string): Promise { return this.userModel.findOne({ email }).exec(); } From 58b84be6ff4f13db417dffdab50a6158c3ccc9c9 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Sat, 31 Aug 2024 01:12:08 -0300 Subject: [PATCH 6/8] Adicionando testes para os metodos de completude da trilha Co-authored-by: Nanashii76 --- src/users/users.controller.ts | 14 +++---- src/users/users.service.ts | 23 ++++++----- test/user.service.spec.ts | 72 ++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index e27d59c..ef18211 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -56,13 +56,6 @@ export class UsersController { return await this.usersService.getSubscribedJourneys(userId); } - @Get(':userId/completedTrails') - async getCompletedTrails( - @Param('userId') userId: string, - ): Promise { - return await this.usersService.getCompletedTrails(userId); - } - @Get() async getUsers() { return await this.usersService.getUsers(); @@ -108,6 +101,13 @@ export class UsersController { return this.usersService.completeTrail(userId, trailId); } + @Get(':userId/completedTrails') + async getCompletedTrails( + @Param('userId') userId: string, + ): Promise { + return await this.usersService.getCompletedTrails(userId); + } + @Get('/:id') async getUserById(@Param('id') id: string) { try { diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 3a56874..aa87948 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -131,24 +131,23 @@ export class UsersService { if (!user) { throw new NotFoundException(`User with ID ${userId} not found`); } - + const objectId = new Types.ObjectId(trailId); - if (user.completedTrails && user.completedTrails.includes(objectId)) { + + const isTrailCompleted = user.completedTrails.some( + (completedTrailId) => completedTrailId.equals(objectId), + ); + + if (isTrailCompleted) { throw new ConflictException( `User already completed trail with ID ${trailId}`, ); } - - if (!user.completedTrails) { - user.completedTrails = []; - } - - if (!user.completedTrails.includes(objectId)) { - user.completedTrails.push(objectId); - } - + + user.completedTrails.push(objectId); + return user.save(); - } + } async deleteUserById(_id: string): Promise { const result = await this.userModel.deleteOne({ _id }).exec(); diff --git a/test/user.service.spec.ts b/test/user.service.spec.ts index 1e0a6e5..ff60968 100644 --- a/test/user.service.spec.ts +++ b/test/user.service.spec.ts @@ -6,7 +6,7 @@ import { Model, Types } from 'mongoose'; import { User } from '../src/users/interface/user.interface'; import { UserRole } from '../src/users/dtos/user-role.enum'; import { UpdateRoleDto } from '../src/users/dtos/update-role.dto'; -import { NotFoundException } from '@nestjs/common'; +import { ConflictException, NotFoundException } from '@nestjs/common'; describe('UsersService', () => { let service: UsersService; @@ -22,7 +22,8 @@ describe('UsersService', () => { verificationToken: 'mockToken', isVerified: false, subscribedJourneys: [new Types.ObjectId(), new Types.ObjectId()], - save: jest.fn().mockResolvedValue(this), // Mock da instância + completedTrails: [new Types.ObjectId(), new Types.ObjectId()], + save: jest.fn().mockResolvedValue(this), }; const mockUserList = [ @@ -261,4 +262,71 @@ describe('UsersService', () => { NotFoundException, ); }); + + describe('UsersService - Trail Management', () => { + it('should get completed trails for a user', async () => { + jest.spyOn(model, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(mockUser), + } as any); + + const result = await service.getCompletedTrails('mockId'); + expect(result).toEqual(mockUser.completedTrails); + }); + + it('should throw NotFoundException if user is not found when getting completed trails', async () => { + jest.spyOn(model, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(null), + } as any); + + await expect(service.getCompletedTrails('invalidId')).rejects.toThrow( + NotFoundException, + ); + }); + + it('should mark a trail as completed for a user', async () => { + const trailId = new Types.ObjectId().toHexString(); + + jest.spyOn(model, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(mockUser), + } as any); + + const userWithCompletedTrail = { + ...mockUser, + completedTrails: [new Types.ObjectId(trailId)], + }; + + jest.spyOn(mockUser, 'save').mockResolvedValue(userWithCompletedTrail as any); + + const result = await service.completeTrail('mockId', trailId); + expect(result.completedTrails).toContainEqual(new Types.ObjectId(trailId)); + expect(mockUser.save).toHaveBeenCalled(); + }); + + it('should throw NotFoundException if user is not found when marking a trail as completed', async () => { + jest.spyOn(model, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(null), + } as any); + + await expect( + service.completeTrail('invalidId', new Types.ObjectId().toHexString()), + ).rejects.toThrow(NotFoundException); + }); + + it('should throw ConflictException if trail is already completed by the user', async () => { + const trailId = new Types.ObjectId().toHexString(); + + const userWithCompletedTrail = { + ...mockUser, + completedTrails: [new Types.ObjectId(trailId)], + }; + + jest.spyOn(model, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(userWithCompletedTrail), + } as any); + + await expect(service.completeTrail('mockId', trailId)).rejects.toThrow( + ConflictException, + ); + }); + }); }); From 4d5ce7a5c633e1b11e8cdb9f5111be61463b9bfc Mon Sep 17 00:00:00 2001 From: DaviMatheus Date: Sun, 1 Sep 2024 21:21:33 -0300 Subject: [PATCH 7/8] [feat#80]-fixng_lint --- src/users/interface/user.schema.ts | 4 +-- src/users/users.controller.ts | 3 +-- src/users/users.service.ts | 16 ++++++------ test/user.service.spec.ts | 40 ++++++++++++++++-------------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/users/interface/user.schema.ts b/src/users/interface/user.schema.ts index 764ddee..7a4f547 100644 --- a/src/users/interface/user.schema.ts +++ b/src/users/interface/user.schema.ts @@ -20,9 +20,7 @@ export const UserSchema = new mongoose.Schema( subscribedJourneys: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }, ], - completedTrails: [ - { type: mongoose.Schema.Types.ObjectId, ref: 'Trail'}, - ], + completedTrails: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Trail' }], }, { timestamps: true, collection: 'users' }, ); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index ef18211..1679533 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -82,7 +82,6 @@ export class UsersController { return this.usersService.subscribeJourney(userId, journeyId); } - @UseGuards(JwtAuthGuard) @Delete(':userId/unsubscribe/:journeyId') async unsubscribeJourney( @@ -91,7 +90,7 @@ export class UsersController { ) { return this.usersService.unsubscribeJourney(userId, journeyId); } - + @UseGuards(JwtAuthGuard) @Post(':userId/complete/:trailId') async completeTrail( diff --git a/src/users/users.service.ts b/src/users/users.service.ts index aa87948..a0d3cea 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -131,23 +131,23 @@ export class UsersService { if (!user) { throw new NotFoundException(`User with ID ${userId} not found`); } - + const objectId = new Types.ObjectId(trailId); - - const isTrailCompleted = user.completedTrails.some( - (completedTrailId) => completedTrailId.equals(objectId), + + const isTrailCompleted = user.completedTrails.some((completedTrailId) => + completedTrailId.equals(objectId), ); - + if (isTrailCompleted) { throw new ConflictException( `User already completed trail with ID ${trailId}`, ); } - + user.completedTrails.push(objectId); - + return user.save(); - } + } async deleteUserById(_id: string): Promise { const result = await this.userModel.deleteOne({ _id }).exec(); diff --git a/test/user.service.spec.ts b/test/user.service.spec.ts index ff60968..79203d5 100644 --- a/test/user.service.spec.ts +++ b/test/user.service.spec.ts @@ -22,8 +22,8 @@ describe('UsersService', () => { verificationToken: 'mockToken', isVerified: false, subscribedJourneys: [new Types.ObjectId(), new Types.ObjectId()], - completedTrails: [new Types.ObjectId(), new Types.ObjectId()], - save: jest.fn().mockResolvedValue(this), + completedTrails: [new Types.ObjectId(), new Types.ObjectId()], + save: jest.fn().mockResolvedValue(this), }; const mockUserList = [ @@ -268,62 +268,66 @@ describe('UsersService', () => { jest.spyOn(model, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(mockUser), } as any); - + const result = await service.getCompletedTrails('mockId'); expect(result).toEqual(mockUser.completedTrails); }); - + it('should throw NotFoundException if user is not found when getting completed trails', async () => { jest.spyOn(model, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(null), } as any); - + await expect(service.getCompletedTrails('invalidId')).rejects.toThrow( NotFoundException, ); }); - + it('should mark a trail as completed for a user', async () => { const trailId = new Types.ObjectId().toHexString(); - + jest.spyOn(model, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(mockUser), } as any); - + const userWithCompletedTrail = { ...mockUser, completedTrails: [new Types.ObjectId(trailId)], }; - - jest.spyOn(mockUser, 'save').mockResolvedValue(userWithCompletedTrail as any); - + + jest + .spyOn(mockUser, 'save') + .mockResolvedValue(userWithCompletedTrail as any); + const result = await service.completeTrail('mockId', trailId); - expect(result.completedTrails).toContainEqual(new Types.ObjectId(trailId)); + expect(result.completedTrails).toContainEqual( + new Types.ObjectId(trailId), + ); expect(mockUser.save).toHaveBeenCalled(); }); - + it('should throw NotFoundException if user is not found when marking a trail as completed', async () => { jest.spyOn(model, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(null), } as any); - + await expect( service.completeTrail('invalidId', new Types.ObjectId().toHexString()), ).rejects.toThrow(NotFoundException); }); - + it('should throw ConflictException if trail is already completed by the user', async () => { const trailId = new Types.ObjectId().toHexString(); - + const userWithCompletedTrail = { ...mockUser, completedTrails: [new Types.ObjectId(trailId)], }; - + jest.spyOn(model, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(userWithCompletedTrail), } as any); - + await expect(service.completeTrail('mockId', trailId)).rejects.toThrow( ConflictException, ); From caa1418eeff89ad4a2690e864893808033450a8c Mon Sep 17 00:00:00 2001 From: DaviMatheus Date: Sun, 1 Sep 2024 21:35:14 -0300 Subject: [PATCH 8/8] [feat#80]-fixng_tests --- test/user.controller.spec.ts | 152 +++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 test/user.controller.spec.ts diff --git a/test/user.controller.spec.ts b/test/user.controller.spec.ts new file mode 100644 index 0000000..30c2e2b --- /dev/null +++ b/test/user.controller.spec.ts @@ -0,0 +1,152 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersController } from 'src/users/users.controller'; +import { UsersService } from 'src/users/users.service'; +import { NotFoundException } from '@nestjs/common'; +import { UserRole } from 'src/users/dtos/user-role.enum'; +import { CreateUserDto } from 'src/users/dtos/create-user.dto'; +import { UpdateRoleDto } from 'src/users/dtos/update-role.dto'; +import { JwtService } from '@nestjs/jwt'; +import { AuthService } from 'src/auth/auth.service'; + +describe('UsersController', () => { + let controller: UsersController; + + const mockUser = { + _id: 'mockUserId', + name: 'Mock User', + email: 'mock@example.com', + role: UserRole.ALUNO, + }; + + const mockUserService = { + createUser: jest.fn().mockResolvedValue(mockUser), + verifyUser: jest.fn().mockResolvedValue(mockUser), + getSubscribedJourneys: jest.fn().mockResolvedValue([]), + getUsers: jest.fn().mockResolvedValue([mockUser]), + addPointToUser: jest.fn().mockResolvedValue(mockUser), + subscribeJourney: jest.fn().mockResolvedValue(mockUser), + unsubscribeJourney: jest.fn().mockResolvedValue(mockUser), + getUserById: jest.fn().mockResolvedValue(mockUser), + deleteUserById: jest.fn().mockResolvedValue(undefined), + updateUserRole: jest.fn().mockResolvedValue(mockUser), + }; + + const mockAuthService = {}; + const mockJwtService = {}; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [ + { provide: UsersService, useValue: mockUserService }, + { provide: AuthService, useValue: mockAuthService }, + { provide: JwtService, useValue: mockJwtService }, + ], + }).compile(); + + controller = module.get(UsersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + it('should create a user', async () => { + const createUserDto: CreateUserDto = { + name: 'New User', + email: 'newuser@example.com', + password: 'password123', + username: '', + }; + await expect(controller.createUser(createUserDto)).resolves.toEqual({ + message: 'User created successfully. Please verify your email.', + }); + }); + + it('should verify a user', async () => { + const token = 'validToken'; + await expect(controller.verifyUser(token)).resolves.toEqual({ + message: 'Account verified successfully', + }); + }); + + it('should get subscribed journeys', async () => { + const userId = 'mockUserId'; + await expect(controller.getSubscribedJourneys(userId)).resolves.toEqual([]); + }); + + it('should get all users', async () => { + await expect(controller.getUsers()).resolves.toEqual([mockUser]); + }); + + it('should add a point to a user', async () => { + const userId = 'mockUserId'; + const pointId = 'mockPointId'; + await expect( + controller.addPointToUser(userId, { pointId }), + ).resolves.toEqual(mockUser); + }); + + it('should handle error when adding a point to a user', async () => { + const userId = 'mockUserId'; + const pointId = 'mockPointId'; + mockUserService.addPointToUser.mockRejectedValueOnce( + new NotFoundException('User not found'), + ); + await expect( + controller.addPointToUser(userId, { pointId }), + ).rejects.toThrow(NotFoundException); + }); + + it('should get a user by ID', async () => { + const userId = 'mockUserId'; + await expect(controller.getUserById(userId)).resolves.toEqual(mockUser); + }); + + it('should handle error when getting a user by ID', async () => { + const userId = 'mockUserId'; + mockUserService.getUserById.mockRejectedValueOnce( + new NotFoundException('User not found'), + ); + await expect(controller.getUserById(userId)).rejects.toThrow( + NotFoundException, + ); + }); + + it('should delete a user by ID', async () => { + const userId = 'mockUserId'; + await expect(controller.deleteUserById(userId)).resolves.toBeUndefined(); + }); + + it('should handle error when deleting a user by ID', async () => { + const userId = 'mockUserId'; + mockUserService.deleteUserById.mockRejectedValueOnce( + new NotFoundException('User not found'), + ); + await expect(controller.deleteUserById(userId)).rejects.toThrow( + NotFoundException, + ); + }); + + it('should update a user role', async () => { + const userId = 'mockUserId'; + const updateRoleDto: UpdateRoleDto = { role: UserRole.ADMIN }; + await expect( + controller.updateUserRole(userId, updateRoleDto), + ).resolves.toEqual({ + message: 'User role updated successfully', + user: mockUser, + }); + }); + + it('should handle error when updating a user role', async () => { + const userId = 'mockUserId'; + const updateRoleDto: UpdateRoleDto = { role: UserRole.ADMIN }; + mockUserService.updateUserRole.mockRejectedValueOnce( + new NotFoundException('User not found'), + ); + await expect( + controller.updateUserRole(userId, updateRoleDto), + ).rejects.toThrow(NotFoundException); + }); +});