From 6dd34ff52acce600e34d420c5870b117cfac817b Mon Sep 17 00:00:00 2001 From: Matheus Sanchez Date: Sun, 4 Feb 2024 19:16:16 -0300 Subject: [PATCH] Edit user pass useCase --- .../in-memory-db/inMemoryUserRepository.ts | 21 +++++- .../prisma/prisma-users-repository.ts | 15 +++- src/repositories/user-repository.ts | 9 +++ .../user/editUserPasswordUseCase.spec.ts | 73 +++++++++++++++++++ src/use-cases/user/editUserPasswordUseCase.ts | 48 ++++++++++++ 5 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 src/use-cases/user/editUserPasswordUseCase.spec.ts create mode 100644 src/use-cases/user/editUserPasswordUseCase.ts diff --git a/src/repositories/in-memory-db/inMemoryUserRepository.ts b/src/repositories/in-memory-db/inMemoryUserRepository.ts index 2b8ce67..7113cf8 100644 --- a/src/repositories/in-memory-db/inMemoryUserRepository.ts +++ b/src/repositories/in-memory-db/inMemoryUserRepository.ts @@ -1,10 +1,27 @@ import { Prisma, User } from '@prisma/client' -import { UserRepository, editUserRequestPrisma } from '../user-repository' +import { + UserRepository, + editUserPasswordRequestPrisma, + editUserRequestPrisma, +} from '../user-repository' import { randomUUID } from 'crypto' export class InMemoryUserRepository implements UserRepository { public db: User[] = [] constructor() {} + async editPassword({ + password_hash, + userId, + }: editUserPasswordRequestPrisma): Promise { + const indexToUpdate = this.db.findIndex((user) => user.id === userId) + + this.db[indexToUpdate] = { + ...this.db[indexToUpdate], + password_hash, + } + + return this.db[indexToUpdate] + } async findByEmail(email: string): Promise { const User = this.db.find((User) => User.email === email) @@ -52,7 +69,6 @@ export class InMemoryUserRepository implements UserRepository { return user } - async edit({ name, surname, @@ -70,6 +86,7 @@ export class InMemoryUserRepository implements UserRepository { return this.db[indexToUpdate] } + async addPhotoUrl(projectId: string, photoUrl: string): Promise { throw new Error('Method not implemented.') } diff --git a/src/repositories/prisma/prisma-users-repository.ts b/src/repositories/prisma/prisma-users-repository.ts index aad6112..acfc514 100644 --- a/src/repositories/prisma/prisma-users-repository.ts +++ b/src/repositories/prisma/prisma-users-repository.ts @@ -1,6 +1,10 @@ import { Prisma, User } from '@prisma/client' import { prisma } from '../../lib/prisma' -import { UserRepository, editUserRequestPrisma } from '../user-repository' +import { + UserRepository, + editUserPasswordRequestPrisma, + editUserRequestPrisma, +} from '../user-repository' export class PrismaUsersRepository implements UserRepository { async findByEmail(email: string): Promise { @@ -31,7 +35,6 @@ export class PrismaUsersRepository implements UserRepository { return user } - async edit({ name, surname, @@ -45,7 +48,13 @@ export class PrismaUsersRepository implements UserRepository { return user } - + + async editPassword({ + password_hash, + userId, + }: editUserPasswordRequestPrisma): Promise { + throw new Error('Method not implemented.') + } async addPhotoUrl(userId: string, photoUrl: string): Promise { const user = await prisma.user.update({ diff --git a/src/repositories/user-repository.ts b/src/repositories/user-repository.ts index a91d34c..fc19bfc 100644 --- a/src/repositories/user-repository.ts +++ b/src/repositories/user-repository.ts @@ -7,10 +7,19 @@ export interface editUserRequestPrisma { userId: string } +export interface editUserPasswordRequestPrisma { + password_hash: string + userId: string +} + export interface UserRepository { create(data: Prisma.UserCreateInput): Promise findByEmail(email: string): Promise findById(id: string): Promise edit({ name, surname, country, userId }: editUserRequestPrisma): Promise addPhotoUrl(userId: string, photoUrl: string): Promise + editPassword({ + password_hash, + userId, + }: editUserPasswordRequestPrisma): Promise } diff --git a/src/use-cases/user/editUserPasswordUseCase.spec.ts b/src/use-cases/user/editUserPasswordUseCase.spec.ts new file mode 100644 index 0000000..5066850 --- /dev/null +++ b/src/use-cases/user/editUserPasswordUseCase.spec.ts @@ -0,0 +1,73 @@ +import { expect, describe, it, beforeEach } from 'vitest' +import { CreateUserUseCase } from './createUserUseCase' +import { compare } from 'bcryptjs' +import { InMemoryUserRepository } from '../../repositories/in-memory-db/inMemoryUserRepository' +import { UserRepository } from '../../repositories/user-repository' +import { EditUserPasswordUseCase } from './editUserPasswordUseCase' +import { InvalidCredentialsError } from '../errors/InvalidCredentialsError' +import { randomUUID } from 'crypto' +import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' + +let usersRepository: UserRepository +let editUserPasswordUseCase: EditUserPasswordUseCase +let createUserUseCase: CreateUserUseCase + +describe('Edit User Pass Use Case', () => { + beforeEach(() => { + usersRepository = new InMemoryUserRepository() + editUserPasswordUseCase = new EditUserPasswordUseCase(usersRepository) + createUserUseCase = new CreateUserUseCase(usersRepository) + }) + + it('should be able to edit a user pass', async () => { + const { user } = await createUserUseCase.execute({ + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + password: '123456', + }) + + const { user: editedPassUser } = await editUserPasswordUseCase.execute({ + userId: user.id, + newPassword: 'newAwesomePass', + oldPassword: '123456', + }) + + expect(editedPassUser.email).toEqual('johndoe@email.com') + expect(editedPassUser.name).toEqual('John') + + const isPasswordCorrectlyHashed = await compare( + 'newAwesomePass', + editedPassUser.password_hash, + ) + + expect(isPasswordCorrectlyHashed).toBe(true) + }) + + it('should not be able to edit an user pass with the wrong old pass', async () => { + const { user } = await createUserUseCase.execute({ + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + password: '123456', + }) + + await expect(() => + editUserPasswordUseCase.execute({ + userId: user.id, + newPassword: 'newAwesomePass', + oldPassword: 'oldWrongPass', + }), + ).rejects.toBeInstanceOf(InvalidCredentialsError) + }) + + it('should not be able to edit an user pass from an user that does not exist', async () => { + await expect(() => + editUserPasswordUseCase.execute({ + userId: randomUUID(), + newPassword: 'newAwesomePass', + oldPassword: 'oldWrongPass', + }), + ).rejects.toBeInstanceOf(ResourceNotFoundError) + }) +}) diff --git a/src/use-cases/user/editUserPasswordUseCase.ts b/src/use-cases/user/editUserPasswordUseCase.ts new file mode 100644 index 0000000..1f14155 --- /dev/null +++ b/src/use-cases/user/editUserPasswordUseCase.ts @@ -0,0 +1,48 @@ +import { compare, hash } from 'bcryptjs' +import { UserRepository } from '../../repositories/user-repository' +import { User } from '@prisma/client' +import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' +import { InvalidCredentialsError } from '../errors/InvalidCredentialsError' + +interface EditUserPasswordUseCaseRequest { + userId: string + oldPassword: string + newPassword: string +} + +interface EditUserPasswordUseCaseResponse { + user: User +} + +export class EditUserPasswordUseCase { + constructor(private userRepository: UserRepository) {} + + async execute({ + userId, + newPassword, + oldPassword, + }: EditUserPasswordUseCaseRequest): Promise { + const user = await this.userRepository.findById(userId) + + if (!user) { + throw new ResourceNotFoundError() + } + + const passwordMatched = await compare(oldPassword, user.password_hash) + + if (!passwordMatched) { + throw new InvalidCredentialsError('Email e/ou senha inválido.') + } + + const password_hash = await hash(newPassword, 6) + + const userUpdated = await this.userRepository.editPassword({ + userId, + password_hash, + }) + + return { + user: userUpdated, + } + } +}