Skip to content

Commit

Permalink
refactor: standardized and updated tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Frompaje committed Aug 27, 2024
1 parent 7caeaab commit 7327a07
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 347 deletions.
37 changes: 29 additions & 8 deletions src/features/account/controllers/account-controller.test.ts
Original file line number Diff line number Diff line change
@@ -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<AccountRepository>;
let deleteAccountByIdService: DeleteUserAccountsService;
let accountController: AccountController;

let req: Request;
let res: Response;
let next: NextFunction;
let error: HttpError;

beforeEach(() => {
accountRepository = mock<AccountRepository>();
deleteAccountByIdService = new DeleteUserAccountsService(accountRepository);
accountController = new AccountController(deleteAccountByIdService);

req = mockDeep<Request>();

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<Request>();
req.params.id = 'f19c169c-5fa2-406d-8de9-4d2c36dc6529';

const res = mock<Response>();
res.status.mockReturnThis();
res.send.mockReturnThis();
await accountController.deleteAccountById(req, res, next);

const next = mock<NextFunction>();
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);
});
});
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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([]);
Expand All @@ -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 = {
Expand All @@ -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([]);
Expand Down
89 changes: 43 additions & 46 deletions src/features/auth/controllers/auth-controller.test.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,64 @@
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<JWTHelper>(new JWTHelper('secret'));

const loginServiceMock = mock<AuthLoginService>(
new AuthLoginService(
authRepositoryMock,
{
compare: vi.fn(),
encrypt: vi.fn(),
},
jwtMock
)
);

const authController = new AuthController(loginServiceMock);

const req = mockDeep<Request>();
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<JWTHelper>(new JWTHelper('secret'));

loginServiceMock = mock<AuthLoginService>(
new AuthLoginService(authRepositoryMock, bcryptAdapteMock, jwtMock)
);

authController = new AuthController(loginServiceMock);

req = mockDeep<Request>();

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',
};

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,
});
Expand All @@ -70,25 +71,21 @@ 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);

expect(next).toHaveBeenCalledWith(expect.any(Error));
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,
Expand Down
Loading

0 comments on commit 7327a07

Please sign in to comment.