Skip to content

Commit

Permalink
[GH-38] feat(User): add controllers
Browse files Browse the repository at this point in the history
  • Loading branch information
pablojvritx committed Dec 15, 2023
1 parent 7c65168 commit 5442cad
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/apps/apiApp/controllers/Auth/LoginController.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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;
}
}
28 changes: 28 additions & 0 deletions src/apps/apiApp/controllers/Auth/RegisterController.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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;
}
}
24 changes: 24 additions & 0 deletions src/apps/apiApp/controllers/Auth/ValidateMailController.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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;
}
}
3 changes: 3 additions & 0 deletions src/apps/apiApp/controllers/Auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './LoginController';
export * from './RegisterController';
export * from './ValidateMailController';
56 changes: 56 additions & 0 deletions tests/apps/apiApp/controllers/Auth/LoginController.test.ts
Original file line number Diff line number Diff line change
@@ -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<Request>;
let res: Partial<Response>;
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));
});
});
});
62 changes: 62 additions & 0 deletions tests/apps/apiApp/controllers/Auth/RegisterController.test.ts
Original file line number Diff line number Diff line change
@@ -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<Request>;
let res: Partial<Response>;
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));
});
});
});
52 changes: 52 additions & 0 deletions tests/apps/apiApp/controllers/Auth/ValidateMailControllet.test.ts
Original file line number Diff line number Diff line change
@@ -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<Request>;
let res: Partial<Response>;
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));
});
});
});

0 comments on commit 5442cad

Please sign in to comment.