diff --git a/src/apps/apiApp/controllers/Auth/LoginController.ts b/src/apps/apiApp/controllers/Auth/LoginController.ts new file mode 100644 index 0000000..6bbf7b1 --- /dev/null +++ b/src/apps/apiApp/controllers/Auth/LoginController.ts @@ -0,0 +1,24 @@ +import httpStatus from 'http-status'; +import { NextFunction, Request, Response } from 'express'; +import { Controller } from '../../shared/interfaces/Controller'; +import { LoginUser } from '../../../../Contexts/apiApp/Auth/application'; + +export class LoginController implements Controller { + constructor(protected login: LoginUser) {} + + async run(req: Request, res: Response, next: NextFunction): Promise { + try { + const { email, password } = req.body; + + const token = await this.login.run({ email, password }); + + res.status(this.status()).json({ token }); + } catch (error) { + next(error); + } + } + + protected status() { + return httpStatus.OK; + } +} diff --git a/src/apps/apiApp/controllers/Auth/RegisterController.ts b/src/apps/apiApp/controllers/Auth/RegisterController.ts new file mode 100644 index 0000000..4f89477 --- /dev/null +++ b/src/apps/apiApp/controllers/Auth/RegisterController.ts @@ -0,0 +1,28 @@ +import httpStatus from 'http-status'; +import { NextFunction, Request, Response } from 'express'; +import { Controller } from '../../shared/interfaces/Controller'; +import { InvalidArgumentError } from '../../../../Contexts/shared/domain/errors/InvalidArgumentError'; +import { RegisterUser } from '../../../../Contexts/apiApp/Auth/application'; + +export class RegisterController implements Controller { + constructor(protected register: RegisterUser) {} + + async run(req: Request, res: Response, next: NextFunction): Promise { + try { + const { email, username, password, repeatPassword } = req.body; + if (password !== repeatPassword) { + throw new InvalidArgumentError('Passwords do not match'); + } + + await this.register.run({ email, password, username }); + + res.status(this.status()).send(); + } catch (error) { + next(error); + } + } + + protected status() { + return httpStatus.CREATED; + } +} diff --git a/src/apps/apiApp/controllers/Auth/ValidateMailController.ts b/src/apps/apiApp/controllers/Auth/ValidateMailController.ts new file mode 100644 index 0000000..7f63433 --- /dev/null +++ b/src/apps/apiApp/controllers/Auth/ValidateMailController.ts @@ -0,0 +1,24 @@ +import httpStatus from 'http-status'; +import { NextFunction, Request, Response } from 'express'; +import { Controller } from '../../shared/interfaces/Controller'; +import { ValidateMail } from '../../../../Contexts/apiApp/Auth/application'; + +export class ValidateMailController implements Controller { + constructor(protected validateMail: ValidateMail) {} + + async run(req: Request, res: Response, next: NextFunction): Promise { + try { + const { token } = req.params; + + const newToken = await this.validateMail.run({ token }); + + res.status(this.status()).json({ token: newToken }); + } catch (error) { + next(error); + } + } + + protected status() { + return httpStatus.OK; + } +} diff --git a/src/apps/apiApp/controllers/Auth/index.ts b/src/apps/apiApp/controllers/Auth/index.ts new file mode 100644 index 0000000..c5486fd --- /dev/null +++ b/src/apps/apiApp/controllers/Auth/index.ts @@ -0,0 +1,3 @@ +export * from './LoginController'; +export * from './RegisterController'; +export * from './ValidateMailController'; diff --git a/tests/apps/apiApp/controllers/Auth/LoginController.test.ts b/tests/apps/apiApp/controllers/Auth/LoginController.test.ts new file mode 100644 index 0000000..8463265 --- /dev/null +++ b/tests/apps/apiApp/controllers/Auth/LoginController.test.ts @@ -0,0 +1,56 @@ +import { Request, Response } from 'express'; +import httpStatus from 'http-status'; +import { + LoginUser, + LoginUserRequest +} from '../../../../../src/Contexts/apiApp/Auth/application'; +import { LoginController } from '../../../../../src/apps/apiApp/controllers/Auth'; +import { UserRepositoryMock } from '../../../../Contexts/apiApp/Auth/__mocks__/UserRepositoryMock'; +import { CryptAdapterMock } from '../../../../Contexts/apiApp/Auth/__mocks__/CryptAdapterMock'; +import { LoginUserRequestMother } from '../../../../Contexts/apiApp/Auth/application/mothers/LoginUserRequestMother'; +import { AuthError } from '../../../../../src/Contexts/shared/domain/errors/AuthError'; + +describe('LoginController', () => { + let repository: UserRepositoryMock; + let encrypter: CryptAdapterMock; + let controller: LoginController; + let service: LoginUser; + let request: LoginUserRequest; + + let req: Partial; + let res: Partial; + let next: jest.Mock; + + const spyService = jest.spyOn(LoginUser.prototype, 'run'); + + beforeEach(() => { + repository = new UserRepositoryMock({ exists: true }); + encrypter = new CryptAdapterMock({ login: true, token: true }); + service = new LoginUser(repository, encrypter); + controller = new LoginController(service); + request = LoginUserRequestMother.random(); + req = { body: request }; + res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; + next = jest.fn(); + }); + + describe('run', () => { + it('should login the user and send 200 status', async () => { + await controller.run(req as Request, res as Response, next); + + expect(spyService).toHaveBeenCalledWith(request); + expect(res.status).toHaveBeenCalledWith(httpStatus.OK); + expect(res.json).toHaveBeenCalledWith({ token: expect.any(String) }); + }); + + it('should call next with the AuthError if login fails', async () => { + encrypter = new CryptAdapterMock({ login: false }); + service = new LoginUser(repository, encrypter); + controller = new LoginController(service); + + await controller.run(req as Request, res as Response, next); + + expect(next).toHaveBeenCalledWith(expect.any(AuthError)); + }); + }); +}); diff --git a/tests/apps/apiApp/controllers/Auth/RegisterController.test.ts b/tests/apps/apiApp/controllers/Auth/RegisterController.test.ts new file mode 100644 index 0000000..f69cabc --- /dev/null +++ b/tests/apps/apiApp/controllers/Auth/RegisterController.test.ts @@ -0,0 +1,62 @@ +import httpStatus from 'http-status'; +import { Request, Response } from 'express'; +import { RegisterUser } from '../../../../../src/Contexts/apiApp/Auth/application'; +import { RegisterUserRequest } from '../../../../../src/Contexts/apiApp/Auth/application/RegisterUserRequest'; +import { RegisterController } from '../../../../../src/apps/apiApp/controllers/Auth'; +import { CryptAdapterMock } from '../../../../Contexts/apiApp/Auth/__mocks__/CryptAdapterMock'; +import { UserRepositoryMock } from '../../../../Contexts/apiApp/Auth/__mocks__/UserRepositoryMock'; +import { RegisterUserRequestMother } from '../../../../Contexts/apiApp/Auth/application/mothers/RegisterUserRequestMother'; +import { InvalidArgumentError } from '../../../../../src/Contexts/shared/domain/errors/InvalidArgumentError'; + +describe('RegisterController', () => { + let repository: UserRepositoryMock; + let encrypter: CryptAdapterMock; + let controller: RegisterController; + let service: RegisterUser; + let request: RegisterUserRequest; + + let req: Partial; + let res: Partial; + let next: jest.Mock; + + const spyService = jest.spyOn(RegisterUser.prototype, 'run'); + + beforeEach(() => { + repository = new UserRepositoryMock({ exists: false }); + encrypter = new CryptAdapterMock({ token: true }); + service = new RegisterUser(repository, encrypter); + controller = new RegisterController(service); + request = RegisterUserRequestMother.random(); + req = { body: { ...request, repeatPassword: request.password } }; + res = { status: jest.fn().mockReturnThis(), send: jest.fn() }; + next = jest.fn(); + }); + + describe('run', () => { + it('should register the user and send 201 status', async () => { + await controller.run(req as Request, res as Response, next); + + expect(spyService).toHaveBeenCalledWith(request); + expect(res.status).toHaveBeenCalledWith(httpStatus.CREATED); + expect(res.send).toHaveBeenCalledWith(); + }); + + it("should fail if passwords don't match", async () => { + req = { body: { ...request, repeatPassword: 'differentPassword' } }; + + await controller.run(req as Request, res as Response, next); + + expect(next).toHaveBeenCalledWith(expect.any(InvalidArgumentError)); + }); + + it('should fail if user exists', async () => { + repository = new UserRepositoryMock({ exists: true }); + service = new RegisterUser(repository, encrypter); + controller = new RegisterController(service); + + await controller.run(req as Request, res as Response, next); + + expect(next).toHaveBeenCalledWith(expect.any(InvalidArgumentError)); + }); + }); +}); diff --git a/tests/apps/apiApp/controllers/Auth/ValidateMailControllet.test.ts b/tests/apps/apiApp/controllers/Auth/ValidateMailControllet.test.ts new file mode 100644 index 0000000..c81b547 --- /dev/null +++ b/tests/apps/apiApp/controllers/Auth/ValidateMailControllet.test.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import httpStatus from 'http-status'; +import { UserRepositoryMock } from '../../../../Contexts/apiApp/Auth/__mocks__/UserRepositoryMock'; +import { ValidateMailController } from '../../../../../src/apps/apiApp/controllers/Auth'; +import { ValidateMail } from '../../../../../src/Contexts/apiApp/Auth/application'; +import { CryptAdapterMock } from '../../../../Contexts/apiApp/Auth/__mocks__/CryptAdapterMock'; +import { AuthError } from '../../../../../src/Contexts/shared/domain/errors/AuthError'; + +describe('ValidateMailController', () => { + let repository: UserRepositoryMock; + let encrypter: CryptAdapterMock; + let controller: ValidateMailController; + let service: ValidateMail; + let request: { token: string }; + + let req: Partial; + let res: Partial; + let next: jest.Mock; + + const spyService = jest.spyOn(ValidateMail.prototype, 'run'); + + beforeEach(() => { + repository = new UserRepositoryMock({ exists: true }); + encrypter = new CryptAdapterMock({ login: true, token: true }); + service = new ValidateMail(repository, encrypter); + controller = new ValidateMailController(service); + request = { token: 'token' }; + req = { params: request }; + res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; + next = jest.fn(); + }); + + describe('run', () => { + it('should validate the mail and send 200 status', async () => { + await controller.run(req as Request, res as Response, next); + + expect(spyService).toHaveBeenCalledWith(request); + expect(res.status).toHaveBeenCalledWith(httpStatus.OK); + expect(res.json).toHaveBeenCalledWith({ token: expect.any(String) }); + }); + + it('should call next with the AuthError if login fails', async () => { + encrypter = new CryptAdapterMock({ login: false }); + service = new ValidateMail(repository, encrypter); + controller = new ValidateMailController(service); + + await controller.run(req as Request, res as Response, next); + + expect(next).toHaveBeenCalledWith(expect.any(AuthError)); + }); + }); +});