Skip to content

Commit

Permalink
feat: created email confirmation endpoint (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukasveiga authored Jun 17, 2024
1 parent 5a30845 commit 0cb807c
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 40 deletions.
131 changes: 94 additions & 37 deletions src/features/auth/controllers/auth-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,34 @@ const makeSut = () => {
}
}

class ConfirmationServiceStub implements Service {
public execute(params: any): any {
return params;
}
}

const validator = new ValidatorStub();
const authService = new AuthServiceStub();
const confirmationService = new ConfirmationServiceStub();

const authController = new AuthController(validator, authService);
const authController = new AuthController(
validator,
authService,
confirmationService
);

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,
authService,
confirmationService,
next,
req,
res,
Expand All @@ -41,59 +54,103 @@ const makeSut = () => {
};

describe('[Controllers] AuthController', () => {
const body = {
password: 'valid_password',
username: 'valid_username',
};
const {
authController,
authService,
confirmationService,
next,
req,
res,
validator,
} = makeSut();

const { authController, authService, next, req, res, validator } = makeSut();
describe('Login', () => {
const body = {
password: 'valid_password',
username: 'valid_username',
};

it('should call validator with correctly params', async () => {
const validateSpy = vi.spyOn(validator, 'validate');
it('should call validator with correctly params', async () => {
const validateSpy = vi.spyOn(validator, 'validate');

req.body = body;
req.body = body;

await authController.login(req, res, next);
await authController.login(req, res, next);

expect(validateSpy).toHaveBeenCalledWith(expect.anything(), { body });
});
expect(validateSpy).toHaveBeenCalledWith(expect.anything(), { body });
});

it('should call service with correctly params', async () => {
const serviceSpy = vi.spyOn(authService, 'execute');
it('should call service with correctly params', async () => {
const serviceSpy = vi.spyOn(authService, 'execute');

req.body = body;
req.body = body;

await authController.login(req, res, next);
await authController.login(req, res, next);

expect(serviceSpy).toHaveBeenCalledWith(body);
});
expect(serviceSpy).toHaveBeenCalledWith(body);
});

it('should response 200 with access token informations', async () => {
const response = {
expireAt: '1714237277',
issuedAt: '1714233677',
token:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
};
it('should response 200 with access token informations', async () => {
const response = {
expireAt: '1714237277',
issuedAt: '1714233677',
token:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
};

vi.spyOn(authService, 'execute').mockReturnValue(response);

await authController.login(req, res, next);

expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith(response);
});

it('should call next when an service error occurs', async () => {
const error = new HttpError(500, 'error');

vi.spyOn(authService, 'execute').mockReturnValue(response);
vi.spyOn(authService, 'execute').mockRejectedValueOnce(
new HttpError(500, 'error')
);

await authController.login(req, res, next);
await authController.login(req, res, next);

expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith(response);
expect(next).toHaveBeenCalledWith(error);
expect(error.toJSON()).toStrictEqual({ code: 500, message: 'error' });
});
});

it('should call next when an service error occurs', async () => {
const error = new HttpError(500, 'error');
describe('Confirmation', () => {
const token = 'eyJhbGciOi.JIUzI1NiIsInR5c.CI6IkpXVCJ9';

it('should call service with correclty params', async () => {
const serviceSpy = vi.spyOn(confirmationService, 'execute');

req.query = { token };

await authController.confirmation(req, res, next);

expect(serviceSpy).toHaveBeenCalledWith({ token });
});

it('should response 204 with no content', async () => {
await authController.confirmation(req, res, next);

expect(res.status).toHaveBeenCalledWith(204);
expect(res.send).toHaveBeenCalledWith();
});

it('should call next when an service error occurs', async () => {
const error = new HttpError(500, 'error');

vi.spyOn(authService, 'execute').mockRejectedValueOnce(
new HttpError(500, 'error')
);
vi.spyOn(confirmationService, 'execute').mockRejectedValueOnce(
new HttpError(500, 'error')
);

await authController.login(req, res, next);
await authController.confirmation(req, res, next);

expect(next).toHaveBeenCalledWith(error);
expect(error.toJSON()).toStrictEqual({ code: 500, message: 'error' });
expect(next).toHaveBeenCalledWith(error);
expect(error.toJSON()).toStrictEqual({ code: 500, message: 'error' });
});
});
});
17 changes: 16 additions & 1 deletion src/features/auth/controllers/auth-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import { HttpStatusCode } from '@/shared/protocols/http-client.js';
import type { Service } from '@/shared/protocols/service.js';

export class AuthController implements Controller {
confirmation: AsyncRequestHandler = async (req, res, next) => {
const { token } = req.query;

try {
await this.confirmationService.execute({
token,
});

return res.status(HttpStatusCode.noContent).send();
} catch (error) {
next(error);
}
};

login: AsyncRequestHandler = async (req, res, next) => {
try {
this.validator.validate(authSchema, {
Expand All @@ -23,6 +37,7 @@ export class AuthController implements Controller {
};
constructor(
private validator: Validator,
private authService: Service<unknown>
private authService: Service<unknown>,
private confirmationService: Service<unknown>
) {}
}
9 changes: 7 additions & 2 deletions src/features/auth/routes/auth-controller-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { Validator } from '@/shared/infra/validator/validator.js';

export function authControllerFactory() {
const validator = new Validator();
const service = {} as Service;
const authController = new AuthController(validator, service);
const authService = {} as Service;
const confirmationService = {} as Service;
const authController = new AuthController(
validator,
authService,
confirmationService
);
return { authController };
}
1 change: 1 addition & 0 deletions src/features/auth/routes/auth-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const router = Router();

const { authController } = authControllerFactory();
router.post('/login', authController.login);
router.get('/confirmation', authController.confirmation);

export default {
prefix: 'auth',
Expand Down

0 comments on commit 0cb807c

Please sign in to comment.