diff --git a/src/features/account/controllers/account-controller.test.ts b/src/features/account/controllers/account-controller.test.ts index 711dd49..26556e8 100644 --- a/src/features/account/controllers/account-controller.test.ts +++ b/src/features/account/controllers/account-controller.test.ts @@ -1,35 +1,56 @@ import type { NextFunction, Request, Response } from 'express'; import type { MockProxy } from 'vitest-mock-extended'; -import { mock } from 'vitest-mock-extended'; +import { mock, mockDeep } from 'vitest-mock-extended'; import { AccountController } from '@/features/account/controllers/account-controller'; import type { AccountRepository } from '@/features/account/repositories/account-repository/account-repository'; import { DeleteUserAccountsService } from '@/features/account/services/delete-user-accounts-service'; +import { HttpError } from '@/shared/errors/http-error'; +import { HttpStatusCode } from '@/shared/protocols/http-client'; describe('Account Controller', () => { let accountRepository: MockProxy; let deleteAccountByIdService: DeleteUserAccountsService; let accountController: AccountController; + let req: Request; + let res: Response; + let next: NextFunction; + let error: HttpError; + beforeEach(() => { accountRepository = mock(); deleteAccountByIdService = new DeleteUserAccountsService(accountRepository); accountController = new AccountController(deleteAccountByIdService); + + req = mockDeep(); + + res = { + json: vi.fn(), + send: vi.fn(), + status: vi.fn().mockReturnThis(), + } as unknown as Response; + + next = vi.fn() as unknown as NextFunction; + + error = new HttpError(HttpStatusCode.serverError, 'error'); }); it('should be able to delete an account by id', async () => { - const req = mock(); req.params.id = 'f19c169c-5fa2-406d-8de9-4d2c36dc6529'; - const res = mock(); - res.status.mockReturnThis(); - res.send.mockReturnThis(); + await accountController.deleteAccountById(req, res, next); - const next = mock(); + expect(res.send).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(HttpStatusCode.noContent); + expect(res.send).toHaveBeenCalled(); + }); + it('should call next with an error if deleteUserAccountService.execute throws', async () => { + vi.spyOn(deleteAccountByIdService, 'execute').mockRejectedValueOnce(error); await accountController.deleteAccountById(req, res, next); - expect(res.status).toHaveBeenCalledWith(204); - expect(res.send).toHaveBeenCalled(); + expect(next).toHaveBeenCalledWith(error); + expect(next).toBeCalledTimes(1); }); }); diff --git a/src/features/account/repositories/account-repository/account-repository.test.ts b/src/features/account/repositories/account-repository/account-repository.test.ts index 07bac80..639bb01 100644 --- a/src/features/account/repositories/account-repository/account-repository.test.ts +++ b/src/features/account/repositories/account-repository/account-repository.test.ts @@ -1,36 +1,39 @@ +import type { Account } from '@prisma/client'; import { prisma } from 'mocks/prisma'; import { AccountRepository } from '@/features/account/repositories/account-repository/account-repository'; import { database } from '@/shared/infra/database/database'; import { AccountMock } from '@/shared/test-helpers/mocks/account.mock'; -const makeSut = () => { - const repository = new AccountRepository(); +describe('[Repositories] AccountRepository', () => { + let repository: AccountRepository; + let account: Account; - return { repository }; -}; + beforeEach(() => { + repository = new AccountRepository(); + account = AccountMock.create(); + }); -describe('[Repositories] AccountRepository', () => { describe('getAccounts', () => { it('returns user accounts if found', async () => { - const { repository } = makeSut(); - - const account = AccountMock.create(); - const expectedResult = [ { avatarUrl: account.avatarUrl, createdAt: new Date(), + favorite: false, id: account.id, + name: null, socialMediaId: account.socialMediaId, + socialMediaUserId: null, updatedAt: new Date(), userId: account.userId, + username: null, }, ]; prisma.account.findMany.mockResolvedValue(expectedResult); - const result = await repository.getAccounts(account.userId); + const result = await repository.getAccounts(account.userId!); expect(result[0]).toEqual(expectedResult[0]); expect(prisma.account.findMany).toHaveBeenCalledWith({ @@ -41,8 +44,6 @@ describe('[Repositories] AccountRepository', () => { }); it('returns an empty array if user accounts are not found', async () => { - const { repository } = makeSut(); - const userId = 'non_existent_user_id'; prisma.account.findMany.mockResolvedValue([]); @@ -60,8 +61,6 @@ describe('[Repositories] AccountRepository', () => { describe('deleteAccountsBySocialMediaId', () => { it('deletes accounts associated with the given social media id', async () => { - const { repository } = makeSut(); - const socialMediaId = 123; const expectedResult = { @@ -84,70 +83,77 @@ describe('[Repositories] AccountRepository', () => { describe('Create', () => { it('creates an account', async () => { - const { repository } = makeSut(); - - const { avatarUrl, id, socialMediaId, userId } = AccountMock.create(); - prisma.account.create.mockResolvedValue({ - avatarUrl, - createdAt: new Date(), - id, - socialMediaId, - updatedAt: new Date(), - userId, + avatarUrl: 'avatar-url', + createdAt: account.createdAt, + favorite: false, + id: account.id, + name: 'account-name', + socialMediaId: 1, + socialMediaUserId: 'social-media-user-id', + updatedAt: account.updatedAt, + userId: 'account-id', + username: 'account-social-media-username', }); const result = await repository.create({ - avatarUrl, - socialMediaId, - userId, + avatarUrl: 'avatar-url', + socialMediaId: 1, + socialMediaUserId: 'social-media-user-id', + userId: 'account-id', }); expect(result).toEqual({ - avatarUrl, + avatarUrl: 'avatar-url', createdAt: expect.any(Date), - id, - socialMediaId, + favorite: false, + id: account.id, + name: 'account-name', + socialMediaId: 1, + socialMediaUserId: 'social-media-user-id', updatedAt: expect.any(Date), - userId, + userId: 'account-id', + username: 'account-social-media-username', }); }); }); describe('findAccountByUser', () => { it('returns an account if found', async () => { - const { repository } = makeSut(); - - const { avatarUrl, id, socialMediaId, userId } = AccountMock.create(); - prisma.account.findMany.mockResolvedValue([ { - avatarUrl, + avatarUrl: 'avatar-url', createdAt: new Date(), - id, - socialMediaId, + favorite: false, + id: 'account-id', + name: null, + socialMediaId: 1, + socialMediaUserId: null, updatedAt: new Date(), - userId, + userId: 'account-user-id', + username: null, }, ]); - const result = await repository.findAccountsByUserId(userId); + const result = await repository.findAccountsByUserId(account.userId!); expect(result).toEqual([ { - avatarUrl, + avatarUrl: 'avatar-url', createdAt: expect.any(Date), - id, - socialMediaId, + favorite: false, + id: 'account-id', + name: null, + socialMediaId: 1, + socialMediaUserId: null, updatedAt: expect.any(Date), - userId, + userId: 'account-user-id', + username: null, }, ]); }); it('returns null if account is not found', async () => { - const { repository } = makeSut(); - const userId = 'non_existent_user_id'; prisma.account.findMany.mockResolvedValue([]); diff --git a/src/features/auth/controllers/auth-controller.test.ts b/src/features/auth/controllers/auth-controller.test.ts index ad74149..f876a7a 100644 --- a/src/features/auth/controllers/auth-controller.test.ts +++ b/src/features/auth/controllers/auth-controller.test.ts @@ -1,52 +1,49 @@ -import type { Request, Response } from 'express'; +import type { NextFunction, Request, Response } from 'express'; import { mock, mockDeep } from 'vitest-mock-extended'; import { AuthController } from '@/features/auth/controllers/auth-controller'; import { AuthLoginService } from '@/features/auth/services/auth-login-service'; import { HttpError } from '@/shared/errors/http-error'; import { JWTHelper } from '@/shared/infra/jwt/jwt'; +import { HttpStatusCode } from '@/shared/protocols/http-client'; +import { bcryptAdapteMock } from '@/shared/test-helpers/mocks/bcryptAdapter.mock'; import { authRepositoryMock } from '@/shared/test-helpers/mocks/repositories/auth-repository.mock'; -const makeSut = () => { - const jwtMock = mock(new JWTHelper('secret')); - - const loginServiceMock = mock( - new AuthLoginService( - authRepositoryMock, - { - compare: vi.fn(), - encrypt: vi.fn(), - }, - jwtMock - ) - ); - - const authController = new AuthController(loginServiceMock); - - const req = mockDeep(); - const res = { - json: vi.fn(), - send: vi.fn(), - status: vi.fn().mockReturnThis(), - } as unknown as Response; - const next = vi.fn(); - - return { - authController, - jwtMock, - loginService: loginServiceMock, - next, - req, - res, - }; -}; - describe('[Controllers] AuthController', () => { + let jwtMock: JWTHelper; + let loginServiceMock: AuthLoginService; + let authController: AuthController; + + let req: Request; + let res: Response; + let next: NextFunction; + + let error: HttpError; + + beforeEach(() => { + jwtMock = mock(new JWTHelper('secret')); + + loginServiceMock = mock( + new AuthLoginService(authRepositoryMock, bcryptAdapteMock, jwtMock) + ); + + authController = new AuthController(loginServiceMock); + + req = mockDeep(); + + res = { + json: vi.fn(), + send: vi.fn(), + status: vi.fn().mockReturnThis(), + } as unknown as Response; + + next = vi.fn() as unknown as NextFunction; + + error = new HttpError(HttpStatusCode.serverError, 'error'); + }); + describe('Login', () => { it('return token with success', async () => { - const { authController, jwtMock, loginService, next, req, res } = - makeSut(); - const body = { password: 'password', username: 'username', @@ -54,10 +51,14 @@ describe('[Controllers] AuthController', () => { req.body = body; - const token = jwtMock.createToken({ userId: '1' }); + const token = jwtMock.createToken({ + name: 'John Doe', + userId: '1', + username: 'johndoe', + }); const loginServiceSpy = vi - .spyOn(loginService, 'execute') + .spyOn(loginServiceMock, 'execute') .mockResolvedValue({ token, }); @@ -70,17 +71,14 @@ describe('[Controllers] AuthController', () => { }); it('should call next when an service error occurs', async () => { - const { authController, loginService, next, req, res } = makeSut(); - const body = { password: 'password', username: 'username', }; - req.body = body; - const error = new HttpError(500, 'error'); + req.body = body; - vi.spyOn(loginService, 'execute').mockRejectedValueOnce(error); + vi.spyOn(loginServiceMock, 'execute').mockRejectedValueOnce(error); await authController.login(req, res, next); @@ -88,7 +86,6 @@ describe('[Controllers] AuthController', () => { expect(error.toJSON()).toStrictEqual({ code: 500, message: 'error' }); }); it('calls next when validation fails', async () => { - const { authController, next, req, res } = makeSut(); req.body = { password: 'password', username: 1, diff --git a/src/features/auth/services/auth-token-validation-services.test.ts b/src/features/auth/services/auth-token-validation-services.test.ts index 7ed219a..b5c1367 100644 --- a/src/features/auth/services/auth-token-validation-services.test.ts +++ b/src/features/auth/services/auth-token-validation-services.test.ts @@ -10,34 +10,53 @@ import { UserMock } from '@/shared/test-helpers/mocks/user.mock'; describe('Auth email token validation service sut', () => { let sut: AuthTokenValidationService; - const jwt = new JWTHelper('secret-test-key'); - const userRepository = mock(userRepositoryMock); + let jwt: JWTHelper; + let userRepository: UserRepository; beforeEach(() => { + jwt = new JWTHelper('secret-test-key'); + userRepository = mock(userRepositoryMock); sut = new AuthTokenValidationService(userRepositoryMock, jwt); }); describe('Success', () => { it('should validate the email confirmation and update the status', async () => { - const userMock = UserMock.create({ isActive: false }); - const token = jwt.createToken({ userId: userMock.id }); + const user = UserMock.create({ + isActive: false, + }); - vi.spyOn(userRepository, 'findById').mockResolvedValue(userMock); - vi.spyOn(userRepository, 'updateIsActiveStatus').mockResolvedValue(); + vi.spyOn(jwt, 'parseToken').mockResolvedValue({ + name: user.name!, + userId: user.id, + username: user.username, + }); - await sut.execute({ token }); + vi.spyOn(userRepository, 'findById').mockResolvedValue(user); - expect(userRepository.findById).toHaveBeenCalledWith(userMock.id); - expect(userRepository.updateIsActiveStatus).toHaveBeenCalledWith( - userMock.id + vi.spyOn(userRepository, 'updateIsActiveStatus').mockImplementationOnce( + (userId) => { + if (user.id === userId) { + user.isActive = true; + } + return Promise.resolve(); + } ); + + await sut.execute({ token: 'token' }); + + expect(userRepository.updateIsActiveStatus).toHaveBeenCalledWith(user.id); + + expect(user.isActive).toBe(true); }); }); describe('Error', () => { it('should throw UserNotFound error if user is not found', async () => { - const userMock = UserMock.create({ isActive: false }); - const token = jwt.createToken({ userId: userMock.id }); + const token = jwt.createToken({ + name: 'John Doe', + userId: '1', + username: 'johndoe', + }); vi.spyOn(userRepository, 'findById').mockResolvedValue(null); @@ -45,17 +64,22 @@ describe('Auth email token validation service sut', () => { UserNotFound ); }); - }); - it('should throw EmailAlreadyActiveError if user is already active', async () => { - const userMock = UserMock.create({ isActive: true }); - const token = jwt.createToken({ userId: userMock.id }); + it('should throw EmailAlreadyActiveError if user is already active', async () => { + const user = UserMock.create({ isActive: true }); - vi.spyOn(userRepository, 'findById').mockResolvedValue(userMock); - vi.spyOn(userRepository, 'updateIsActiveStatus').mockResolvedValue(); + vi.spyOn(userRepository, 'findById').mockResolvedValue(user); + vi.spyOn(userRepository, 'updateIsActiveStatus').mockResolvedValue(); + vi.spyOn(jwt, 'parseToken').mockResolvedValue({ + name: user.name!, + userId: user.id, + username: user.username, + }); - await expect(() => sut.execute({ token })).rejects.toBeInstanceOf( - EmailAlreadyActiveError - ); + expect(userRepository.updateIsActiveStatus).not.toHaveBeenCalled(); + await expect(sut.execute({ token: 'token' })).rejects.toThrow( + EmailAlreadyActiveError + ); + }); }); }); diff --git a/src/features/social-media/controllers/social-medias-controller.test.ts b/src/features/social-media/controllers/social-medias-controller.test.ts index c5118e9..99e9bd7 100644 --- a/src/features/social-media/controllers/social-medias-controller.test.ts +++ b/src/features/social-media/controllers/social-medias-controller.test.ts @@ -1,39 +1,41 @@ -import type { Request, Response } from 'express'; +import type { NextFunction, Request, Response } from 'express'; import { mock, mockDeep } from 'vitest-mock-extended'; import { SocialMediasController } from '@/features/social-media/controllers/social-medias-controller'; import { ListSocialMediasService } from '@/features/social-media/services/list-social-medias'; import { HttpError } from '@/shared/errors/http-error'; +import { HttpStatusCode } from '@/shared/protocols/http-client'; import { socialMediasRepositoryMock } from '@/shared/test-helpers/mocks/repositories/social-medias-repository.mock'; -const makeSut = () => { - const findAllServiceMock = mock( - new ListSocialMediasService(socialMediasRepositoryMock) - ); +describe('[Controllers] SocialMediasController', () => { + let findAllServiceMock: ListSocialMediasService; + let socialMediasController: SocialMediasController; + let req: Request; + let res: Response; + let next: NextFunction; + let error: HttpError; + + beforeEach(() => { + findAllServiceMock = mock( + new ListSocialMediasService(socialMediasRepositoryMock) + ); - const socialMediasController = new SocialMediasController(findAllServiceMock); + socialMediasController = new SocialMediasController(findAllServiceMock); - const req = mockDeep(); - const res = { - json: vi.fn(), - status: vi.fn().mockReturnThis(), - } as unknown as Response; + req = mockDeep(); - const next = vi.fn(); + res = { + json: vi.fn(), + status: vi.fn().mockReturnThis(), + } as unknown as Response; - return { - next, - req, - res, - serviceFindAll: findAllServiceMock, - socialMediasController, - }; -}; + next = vi.fn() as unknown as NextFunction; + + error = new HttpError(HttpStatusCode.serverError, 'error'); + }); -describe('[Controllers] SocialMediasController', () => { - const { next, req, res, serviceFindAll, socialMediasController } = makeSut(); it('should call service with correctly params', async () => { - const serviceFindAllSpy = vi.spyOn(serviceFindAll, 'execute'); + const serviceFindAllSpy = vi.spyOn(findAllServiceMock, 'execute'); await socialMediasController.findAll(req, res, next); @@ -46,7 +48,7 @@ describe('[Controllers] SocialMediasController', () => { { description: 'Insta', id: 2, name: 'Instagram' }, ]; - vi.spyOn(serviceFindAll, 'execute').mockResolvedValue({ + vi.spyOn(findAllServiceMock, 'execute').mockResolvedValue({ socialMedias: response, }); @@ -59,11 +61,7 @@ describe('[Controllers] SocialMediasController', () => { }); it('should call next when an service error occurs', async () => { - const error = new HttpError(500, 'error'); - - vi.spyOn(serviceFindAll, 'execute').mockRejectedValueOnce( - new HttpError(500, 'error') - ); + vi.spyOn(findAllServiceMock, 'execute').mockRejectedValueOnce(error); await socialMediasController.findAll(req, res, next); diff --git a/src/features/social-media/repositories/social-media.test.ts b/src/features/social-media/repositories/social-media.test.ts index cc663f4..d296371 100644 --- a/src/features/social-media/repositories/social-media.test.ts +++ b/src/features/social-media/repositories/social-media.test.ts @@ -3,17 +3,17 @@ import { prisma } from 'mocks/prisma'; import { SocialMediaRepository } from '@/features/social-media/repositories/social-media'; import { SocialMediaMock } from '@/shared/test-helpers/mocks/social-media.mock'; -const makeSut = () => { - const repository = new SocialMediaRepository(); - const socialMedias = SocialMediaMock.List(); - return { repository, socialMedias }; -}; - describe('[Repositories] SocialMediaRepository', () => { + let repository: SocialMediaRepository; + let socialMedias: any; + + beforeEach(() => { + repository = new SocialMediaRepository(); + socialMedias = SocialMediaMock.List(); + }); + describe('List', () => { it('should return a list of social medias', async () => { - const { repository, socialMedias } = makeSut(); - prisma.socialMedia.findMany.mockResolvedValue(socialMedias); const result = await repository.list(); expect(result).toBe(socialMedias); diff --git a/src/features/social-media/services/list-social-medias.test.ts b/src/features/social-media/services/list-social-medias.test.ts index 6605880..79aa6af 100644 --- a/src/features/social-media/services/list-social-medias.test.ts +++ b/src/features/social-media/services/list-social-medias.test.ts @@ -2,18 +2,18 @@ import { SocialMediaRepository } from '@/features/social-media/repositories/soci import { ListSocialMediasService } from '@/features/social-media/services/list-social-medias'; import { SocialMediaMock } from '@/shared/test-helpers/mocks/social-media.mock'; -const makeSut = () => { - const socialMediaRepository = new SocialMediaRepository(); - const socialMediaService = new ListSocialMediasService(socialMediaRepository); - const socialMediasMock = SocialMediaMock.List(); - return { socialMediaRepository, socialMediaService, socialMediasMock }; -}; - describe('List Social Media', () => { - it('should return list of avaiable social medias', async () => { - const { socialMediaRepository, socialMediaService, socialMediasMock } = - makeSut(); + let socialMediaRepository: SocialMediaRepository; + let socialMediaService: ListSocialMediasService; + let socialMediasMock: any; + beforeEach(() => { + socialMediaRepository = new SocialMediaRepository(); + socialMediaService = new ListSocialMediasService(socialMediaRepository); + socialMediasMock = SocialMediaMock.List(); + }); + + it('should return list of avaiable social medias', async () => { vi.spyOn(socialMediaRepository, 'list').mockResolvedValueOnce( socialMediasMock ); diff --git a/src/features/twitter/controllers/twitter-controller.test.ts b/src/features/twitter/controllers/twitter-controller.test.ts index d93b3ca..06dae39 100644 --- a/src/features/twitter/controllers/twitter-controller.test.ts +++ b/src/features/twitter/controllers/twitter-controller.test.ts @@ -1,4 +1,4 @@ -import type { Request, Response } from 'express'; +import type { NextFunction, Request, Response } from 'express'; import { mock, mockDeep } from 'vitest-mock-extended'; import type { Logger } from '@/shared/infra/logger/logger'; @@ -10,49 +10,46 @@ import { AuthorizeTwitterService } from '../services/authorize-twitter-service'; import type { TwitterService } from '../services/twitter-service'; import { TwitterController } from './twitter-controller'; -const makeSut = () => { - const mockLogger: Logger = mock(loggerMock); - const twitterServiceMock = mock({ - getTwitterOAuthToken: vi.fn(), - getTwitterUser: vi.fn(), - }); +describe('[Controller] Twitter', () => { + let mockLogger: Logger; + let twitterServiceMock: TwitterService; + let authorizeTwitterService: AuthorizeTwitterService; + let authController: TwitterController; + let req: Request; + let res: Response; + let next: NextFunction; + beforeEach(() => { + mockLogger = mock(loggerMock); - const authorizeTwitterService = mock( - new AuthorizeTwitterService( - mockLogger, - twitterServiceMock, - accountRepositoryMock, - tokenRepositoryMock - ) - ); + twitterServiceMock = mock({ + getTwitterOAuthToken: vi.fn(), + getTwitterUser: vi.fn(), + }); - const authController = new TwitterController(authorizeTwitterService); + authorizeTwitterService = mock( + new AuthorizeTwitterService( + mockLogger, + twitterServiceMock, + accountRepositoryMock, + tokenRepositoryMock + ) + ); - const req = mockDeep(); - const res = { - json: vi.fn(), - send: vi.fn(), - status: vi.fn().mockReturnThis(), - } as unknown as Response; - const next = vi.fn(); + authController = new TwitterController(authorizeTwitterService); - return { - authController, - authorizeTwitterService, - mockLogger, - next, - req, - res, - twitterServiceMock, - }; -}; + req = mockDeep(); + + res = { + json: vi.fn(), + send: vi.fn(), + status: vi.fn().mockReturnThis(), + } as unknown as Response; + + next = vi.fn() as unknown as NextFunction; + }); -describe('[Controller] Twitter', () => { describe('callback', () => { it('should be return code', async () => { - const { authController, authorizeTwitterService, next, req, res } = - makeSut(); - const spyAuthorizeTwitter = vi .spyOn(authorizeTwitterService, 'execute') .mockReturnThis(); @@ -70,8 +67,6 @@ describe('[Controller] Twitter', () => { describe('login', () => { it('should be return 401', () => { - const { authController, next, req, res } = makeSut(); - req.headers.authorization = undefined; authController.login(req, res, next); diff --git a/src/features/user/controllers/user-controller.test.ts b/src/features/user/controllers/user-controller.test.ts index 7d45ace..c80ea37 100644 --- a/src/features/user/controllers/user-controller.test.ts +++ b/src/features/user/controllers/user-controller.test.ts @@ -1,5 +1,5 @@ import { randomUUID } from 'crypto'; -import type { Request, Response } from 'express'; +import type { NextFunction, Request, Response } from 'express'; import { mock, mockDeep } from 'vitest-mock-extended'; import { GetUserAccountsService } from '@/features/account/services/get-user-accounts-service'; @@ -9,54 +9,55 @@ import { UserFindByIdService } from '@/features/user/services/user-find-by-id-se import { HttpError } from '@/shared/errors/http-error'; import { UserNotFound } from '@/shared/errors/user-not-found-error'; import { HttpStatusCode } from '@/shared/protocols/http-client'; +import { bcryptAdapteMock } from '@/shared/test-helpers/mocks/bcryptAdapter.mock'; import { accountRepositoryMock } from '@/shared/test-helpers/mocks/repositories/account-repository.mock'; import { userRepositoryMock } from '@/shared/test-helpers/mocks/repositories/user-repository.mock'; -const makeSut = () => { - const mockUserCreateService = mock( - new UserCreateService(userRepositoryMock, { - compare: vi.fn(), - encrypt: vi.fn(), - }) - ); - const mockUserFindByIdService = mock( - new UserFindByIdService(userRepositoryMock) - ); - - const mockGetUserAccountsService = mock( - new GetUserAccountsService(userRepositoryMock, accountRepositoryMock) - ); - - const userController = new UserController( - mockUserCreateService, - mockUserFindByIdService, - mockGetUserAccountsService - ); - - const req = mockDeep(); - const res = { - json: vi.fn(), - status: vi.fn().mockReturnThis(), - } as unknown as Response; - const next = vi.fn(); - - return { - getUserAccountsService: mockGetUserAccountsService, - next, - req, - res, - userController, - userCreateService: mockUserCreateService, - userFindByIdService: mockUserFindByIdService, - }; -}; - describe('[Controllers] UserController', () => { + let mockUserCreateService: UserCreateService; + let mockUserFindByIdService: UserFindByIdService; + let mockGetUserAccountsService: GetUserAccountsService; + let userController: UserController; + + let req: Request; + let res: Response; + let next: NextFunction; + let error: HttpError; + + beforeEach(() => { + mockUserCreateService = mock( + new UserCreateService(userRepositoryMock, bcryptAdapteMock) + ); + + mockUserFindByIdService = mock( + new UserFindByIdService(userRepositoryMock) + ); + + mockGetUserAccountsService = mock( + new GetUserAccountsService(userRepositoryMock, accountRepositoryMock) + ); + + userController = new UserController( + mockUserCreateService, + mockUserFindByIdService, + mockGetUserAccountsService + ); + + req = mockDeep(); + + res = { + json: vi.fn(), + status: vi.fn().mockReturnThis(), + } as unknown as Response; + + next = vi.fn() as unknown as NextFunction; + + error = new HttpError(HttpStatusCode.serverError, 'error'); + }); + describe('create', () => { it('should call service with correctly params', async () => { - const { next, req, res, userController, userCreateService } = makeSut(); - - const serviceSpy = vi.spyOn(userCreateService, 'execute'); + const serviceSpy = vi.spyOn(mockUserCreateService, 'execute'); const body = { email: 'valid_email@domain.com', @@ -80,8 +81,6 @@ describe('[Controllers] UserController', () => { }); it('should throw error', async () => { - const { next, req, res, userController, userCreateService } = makeSut(); - const body = { email: 'valid_email@domain.com', name: 'valid_name', @@ -92,22 +91,14 @@ describe('[Controllers] UserController', () => { req.body = body; - const userCreateSpy = vi - .spyOn(userCreateService, 'execute') - .mockRejectedValueOnce(new HttpError(500, 'error')); - - await userController.create(req, res, next); - - expect(userCreateSpy).toHaveBeenCalled(); - expect(next).toHaveBeenCalledWith(expect.any(Error)); + vi.spyOn(mockUserCreateService, 'execute').mockRejectedValueOnce(error); await userController.create(req, res, next); - expect(userCreateSpy).toHaveBeenCalled(); - expect(next).toHaveBeenCalledWith(expect.any(Error)); + expect(next).toHaveBeenCalledWith(error); }); + it('calls next when validation fails', async () => { - const { next, req, res, userController } = makeSut(); const invalidBody = { email: 'invalid_email@domain.com', name: 1, @@ -125,9 +116,7 @@ describe('[Controllers] UserController', () => { describe('userFindById', () => { it('should call service with correctly params', async () => { - const { next, req, res, userController, userFindByIdService } = makeSut(); - - const serviceSpy = vi.spyOn(userFindByIdService, 'execute'); + const serviceSpy = vi.spyOn(mockUserFindByIdService, 'execute'); const uuid = randomUUID(); @@ -143,24 +132,20 @@ describe('[Controllers] UserController', () => { }); it('should call next when an error', async () => { - const { next, req, res, userController, userFindByIdService } = makeSut(); - req.params = { id: randomUUID(), }; const userFindSpy = vi - .spyOn(userFindByIdService, 'execute') - .mockRejectedValueOnce(new HttpError(500, 'error')); + .spyOn(mockUserFindByIdService, 'execute') + .mockRejectedValueOnce(error); await userController.userFindById(req, res, next); expect(userFindSpy).toHaveBeenCalled(); - expect(next).toHaveBeenCalledWith(expect.any(Error)); + expect(next).toHaveBeenCalledWith(error); }); it('calls next when validation fails', async () => { - const { next, req, res, userController } = makeSut(); - req.params = { id: 'invalid-uuid' }; await userController.userFindById(req, res, next); @@ -171,10 +156,7 @@ describe('[Controllers] UserController', () => { describe('getAccounts', () => { it('should call next with an error if user not found', async () => { - const { getUserAccountsService, next, req, res, userController } = - makeSut(); - - vi.spyOn(getUserAccountsService, 'execute').mockRejectedValueOnce( + vi.spyOn(mockGetUserAccountsService, 'execute').mockRejectedValueOnce( new UserNotFound() ); @@ -188,8 +170,6 @@ describe('[Controllers] UserController', () => { expect(next).toHaveBeenCalledWith(new Error('User not found')); }); it('calls next when validation fails', async () => { - const { next, req, res, userController } = makeSut(); - req.params = { id: 'invalid-uuid' }; await userController.getAccounts(req, res, next); @@ -198,9 +178,6 @@ describe('[Controllers] UserController', () => { }); it('should return accounts from user', async () => { - const { getUserAccountsService, next, req, res, userController } = - makeSut(); - const accounts = [ { avatarUrl: 'avatar-url', @@ -213,7 +190,7 @@ describe('[Controllers] UserController', () => { socialMedia: { id: 2, name: 'social-name' }, }, ]; - vi.spyOn(getUserAccountsService, 'execute').mockResolvedValueOnce({ + vi.spyOn(mockGetUserAccountsService, 'execute').mockResolvedValueOnce({ accounts, }); diff --git a/src/features/user/repositories/user-repository.test.ts b/src/features/user/repositories/user-repository.test.ts index fc73b34..cbd9876 100644 --- a/src/features/user/repositories/user-repository.test.ts +++ b/src/features/user/repositories/user-repository.test.ts @@ -3,17 +3,14 @@ import { prisma } from 'mocks/prisma'; import { UserRepository } from '@/features/user/repositories/user-repository'; import { UserMock } from '@/shared/test-helpers/mocks/user.mock'; -const makeSut = () => { - const repository = new UserRepository(); - - return { repository }; -}; - describe('[Repositories] UserRepository', () => { + let repository: UserRepository; + beforeEach(() => { + repository = new UserRepository(); + }); + describe('create', () => { it('should call service with correctly params', async () => { - const { repository } = makeSut(); - const input = { email: 'test@test.com', name: 'test', @@ -31,8 +28,6 @@ describe('[Repositories] UserRepository', () => { describe('findById', () => { it('return user if found', async () => { - const { repository } = makeSut(); - const user = UserMock.create(); prisma.user.findUnique.mockResolvedValue(user); @@ -49,8 +44,6 @@ describe('[Repositories] UserRepository', () => { }); it('return null if user is not found', async () => { - const { repository } = makeSut(); - prisma.user.findUnique.mockResolvedValue(null); const result = await repository.findById('non_existent_id'); @@ -68,8 +61,6 @@ describe('[Repositories] UserRepository', () => { describe('findByEmail', () => { it('return user if found', async () => { - const { repository } = makeSut(); - const user = UserMock.create(); prisma.user.findUnique.mockResolvedValue(user); @@ -87,8 +78,6 @@ describe('[Repositories] UserRepository', () => { }); it('return null if user is not found', async () => { - const { repository } = makeSut(); - prisma.user.findUnique.mockResolvedValue(null); const result = await repository.findByEmail('non_existent_email'); @@ -106,8 +95,6 @@ describe('[Repositories] UserRepository', () => { describe('updateIsActiveStatus', () => { it('should call service with correctly params', async () => { - const { repository } = makeSut(); - const user = UserMock.create(); await repository.updateIsActiveStatus(user.id); @@ -123,8 +110,6 @@ describe('[Repositories] UserRepository', () => { }); it('should throw an error if an error occurs', async () => { - const { repository } = makeSut(); - const user = UserMock.create(); prisma.user.update.mockImplementationOnce(() => { diff --git a/src/features/user/services/user-create-service.test.ts b/src/features/user/services/user-create-service.test.ts index fdbcced..c71f9f9 100644 --- a/src/features/user/services/user-create-service.test.ts +++ b/src/features/user/services/user-create-service.test.ts @@ -7,61 +7,63 @@ import { userRepositoryMock } from '@/shared/test-helpers/mocks/repositories/use import { UserCreateService } from './user-create-service'; -let userCreateService: UserCreateService; +describe('[Services] UserCreateService', () => { + let userCreateService: UserCreateService; -let userRepository = mock(userRepositoryMock); + let userRepository = mock(userRepositoryMock); -let bcryptAdapter = mock(bcryptAdapteMock); + let bcryptAdapter = mock(bcryptAdapteMock); -beforeEach(() => { - userCreateService = new UserCreateService(userRepository, bcryptAdapter); -}); + beforeEach(() => { + userCreateService = new UserCreateService(userRepository, bcryptAdapter); + }); -describe('UserCreateService', () => { - it('should call userRepository with correct params', async () => { - const repositorySpy = vi.spyOn(userRepository, 'create'); + describe('UserCreateService', () => { + it('should call userRepository with correct params', async () => { + const repositorySpy = vi.spyOn(userRepository, 'create'); - vi.spyOn(bcryptAdapter, 'encrypt').mockResolvedValue('valid_password'); + vi.spyOn(bcryptAdapter, 'encrypt').mockResolvedValue('valid_password'); - await userCreateService.execute({ - email: 'valid_email@email.com', - name: 'valid_name', - password: 'valid_password', - repeatPassword: 'valid_password', - username: 'valid_username', - }); + await userCreateService.execute({ + email: 'valid_email@email.com', + name: 'valid_name', + password: 'valid_password', + repeatPassword: 'valid_password', + username: 'valid_username', + }); - expect(repositorySpy).toHaveBeenCalledWith({ - email: 'valid_email@email.com', - name: 'valid_name', - password: 'valid_password', - username: 'valid_username', + expect(repositorySpy).toHaveBeenCalledWith({ + email: 'valid_email@email.com', + name: 'valid_name', + password: 'valid_password', + username: 'valid_username', + }); }); - }); - it('should throw when userRepository throws', async () => { - vi.spyOn(userRepository, 'create').mockRejectedValue(new Error('error')); + it('should throw when userRepository throws', async () => { + vi.spyOn(userRepository, 'create').mockRejectedValue(new Error('error')); - const response = userCreateService.execute({ - email: 'valid_email@email.com', - name: 'valid_name', - password: 'valid_password', - repeatPassword: 'valid_password', - username: 'valid_username', + const response = userCreateService.execute({ + email: 'valid_email@email.com', + name: 'valid_name', + password: 'valid_password', + repeatPassword: 'valid_password', + username: 'valid_username', + }); + + await expect(response).rejects.toThrowError(); }); - await expect(response).rejects.toThrowError(); - }); + it('should conflict when password and repeatPassword dont match', async () => { + const response = userCreateService.execute({ + email: 'valid_email@email.com', + name: 'valid_name', + password: 'valid_password', + repeatPassword: 'invalid_password', + username: 'valid_username', + }); - it('should conflict when password and repeatPassword dont match', async () => { - const response = userCreateService.execute({ - email: 'valid_email@email.com', - name: 'valid_name', - password: 'valid_password', - repeatPassword: 'invalid_password', - username: 'valid_username', + await expect(response).rejects.toThrowError(); }); - - await expect(response).rejects.toThrowError(); }); }); diff --git a/src/shared/test-helpers/mocks/account.mock.ts b/src/shared/test-helpers/mocks/account.mock.ts index c7f3fde..68acad5 100644 --- a/src/shared/test-helpers/mocks/account.mock.ts +++ b/src/shared/test-helpers/mocks/account.mock.ts @@ -1,19 +1,31 @@ import { faker } from '@faker-js/faker'; type Account = { - avatarUrl: string; + avatarUrl: null | string; + createdAt: Date; + favorite: boolean; id: string; + name: null | string; socialMediaId: number; - userId: string; + socialMediaUserId: null | string; + updatedAt: Date; + userId: null | string; + username: null | string; }; export class AccountMock { public static create(override?: Partial) { return { avatarUrl: faker.image.avatar(), + createdAt: faker.date.past(), + favorite: faker.datatype.boolean(), id: faker.string.numeric(), + name: faker.person.firstName(), socialMediaId: faker.number.int(), + socialMediaUserId: faker.string.uuid(), + updatedAt: faker.date.recent(), userId: faker.string.numeric(), + username: faker.internet.userName(), ...override, }; }