From 4dfab49b9d8cf46e3282617e736d62f365422ed4 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:01:15 -0300 Subject: [PATCH 01/15] feat: ensure AddSurveyController to throw any throw --- .../controllers/add-survey-controller.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/presentation/controllers/add-survey-controller.ts b/src/presentation/controllers/add-survey-controller.ts index 68889038..ef12624d 100644 --- a/src/presentation/controllers/add-survey-controller.ts +++ b/src/presentation/controllers/add-survey-controller.ts @@ -1,6 +1,6 @@ -import { Controller, HttpResponse, Validation } from '@/presentation/protocols' -import { badRequest, serverError, noContent } from '@/presentation/helpers' import { AddSurvey } from '@/domain/usecases' +import { badRequest, noContent } from '@/presentation/helpers' +import { Controller, HttpResponse, Validation } from '@/presentation/protocols' export class AddSurveyController implements Controller { constructor ( @@ -9,19 +9,15 @@ export class AddSurveyController implements Controller { ) {} async handle (request: AddSurveyController.Request): Promise { - try { - const error = this.validation.validate(request) - if (error) { - return badRequest(error) - } - await this.addSurvey.add({ - ...request, - date: new Date() - }) - return noContent() - } catch (error) { - return serverError(error) + const error = this.validation.validate(request) + if (error) { + return badRequest(error) } + await this.addSurvey.add({ + ...request, + date: new Date() + }) + return noContent() } } From 7dd81bf2de4de82512e6e7072a6a0a1de4cf7447 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:01:44 -0300 Subject: [PATCH 02/15] test: ensure AddSurveyController to throw any throw --- .../controllers/add-survey-controller.spec.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/presentation/controllers/add-survey-controller.spec.ts b/tests/presentation/controllers/add-survey-controller.spec.ts index 54e796e2..021b6d89 100644 --- a/tests/presentation/controllers/add-survey-controller.spec.ts +++ b/tests/presentation/controllers/add-survey-controller.spec.ts @@ -1,10 +1,9 @@ import { AddSurveyController } from '@/presentation/controllers' -import { badRequest, serverError, noContent } from '@/presentation/helpers' -import { ValidationSpy, AddSurveySpy } from '@/tests/presentation/mocks' +import { badRequest, noContent } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - -import MockDate from 'mockdate' +import { AddSurveySpy, ValidationSpy } from '@/tests/presentation/mocks' import faker from 'faker' +import MockDate from 'mockdate' const mockRequest = (): AddSurveyController.Request => ({ question: faker.random.words(), @@ -54,6 +53,13 @@ describe('AddSurvey Controller', () => { expect(httpResponse).toEqual(badRequest(validationSpy.error)) }) + test('Should throw if Validate throws', async () => { + const { sut, validationSpy } = makeSut() + jest.spyOn(validationSpy,'validate').mockImplementation(throwError) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() + }) + test('Should call AddSurvey with correct values', async () => { const { sut, addSurveySpy } = makeSut() const request = mockRequest() @@ -61,11 +67,11 @@ describe('AddSurvey Controller', () => { expect(addSurveySpy.params).toEqual({ ...request, date: new Date() }) }) - test('Should return 500 if AddSurvey throws', async () => { + test('Should throw if AddSurvey throws', async () => { const { sut, addSurveySpy } = makeSut() jest.spyOn(addSurveySpy, 'add').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should return 204 on success', async () => { From c5faf8285e5fdc587863415bffb7b2844ce0ab9f Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:02:07 -0300 Subject: [PATCH 03/15] feat: ensure LoadSurveyResultController to throw any throw --- .../load-survey-result-controller.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/presentation/controllers/load-survey-result-controller.ts b/src/presentation/controllers/load-survey-result-controller.ts index caaa00bd..7b96f461 100644 --- a/src/presentation/controllers/load-survey-result-controller.ts +++ b/src/presentation/controllers/load-survey-result-controller.ts @@ -1,7 +1,7 @@ -import { Controller, HttpResponse } from '@/presentation/protocols' -import { forbidden, serverError, ok } from '@/presentation/helpers' -import { InvalidParamError } from '@/presentation/errors' import { CheckSurveyById, LoadSurveyResult } from '@/domain/usecases' +import { InvalidParamError } from '@/presentation/errors' +import { forbidden, ok } from '@/presentation/helpers' +import { Controller, HttpResponse } from '@/presentation/protocols' export class LoadSurveyResultController implements Controller { constructor ( @@ -10,17 +10,13 @@ export class LoadSurveyResultController implements Controller { ) {} async handle (request: LoadSurveyResultController.Request): Promise { - try { - const { surveyId, accountId } = request - const exists = await this.checkSurveyById.checkById(surveyId) - if (!exists) { - return forbidden(new InvalidParamError('surveyId')) - } - const surveyResult = await this.loadSurveyResult.load(surveyId, accountId) - return ok(surveyResult) - } catch (error) { - return serverError(error) + const { surveyId, accountId } = request + const exists = await this.checkSurveyById.checkById(surveyId) + if (!exists) { + return forbidden(new InvalidParamError('surveyId')) } + const surveyResult = await this.loadSurveyResult.load(surveyId, accountId) + return ok(surveyResult) } } From 7d50ad79f7fe6e3bc5fbef63e4e35eab7a17c980 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:02:26 -0300 Subject: [PATCH 04/15] test: ensure LoadSurveyResultController to throw any throw --- .../load-survey-result-controller.spec.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/presentation/controllers/load-survey-result-controller.spec.ts b/tests/presentation/controllers/load-survey-result-controller.spec.ts index 8335b370..33fb73ff 100644 --- a/tests/presentation/controllers/load-survey-result-controller.spec.ts +++ b/tests/presentation/controllers/load-survey-result-controller.spec.ts @@ -1,11 +1,10 @@ import { LoadSurveyResultController } from '@/presentation/controllers' -import { forbidden, serverError, ok } from '@/presentation/helpers' import { InvalidParamError } from '@/presentation/errors' -import { CheckSurveyByIdSpy, LoadSurveyResultSpy } from '@/tests/presentation/mocks' +import { forbidden, ok } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - -import MockDate from 'mockdate' +import { CheckSurveyByIdSpy, LoadSurveyResultSpy } from '@/tests/presentation/mocks' import faker from 'faker' +import MockDate from 'mockdate' const mockRequest = (): LoadSurveyResultController.Request => ({ accountId: faker.datatype.uuid(), @@ -52,11 +51,11 @@ describe('LoadSurveyResult Controller', () => { expect(httpResponse).toEqual(forbidden(new InvalidParamError('surveyId'))) }) - test('Should return 500 if CheckSurveyById throws', async () => { + test('Should throw if CheckSurveyById throws', async () => { const { sut, checkSurveyByIdSpy } = makeSut() jest.spyOn(checkSurveyByIdSpy, 'checkById').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should call LoadSurveyResult with correct values', async () => { @@ -67,11 +66,11 @@ describe('LoadSurveyResult Controller', () => { expect(loadSurveyResultSpy.accountId).toBe(request.accountId) }) - test('Should return 500 if LoadSurveyResult throws', async () => { + test('Should throw if LoadSurveyResult throws', async () => { const { sut, loadSurveyResultSpy } = makeSut() jest.spyOn(loadSurveyResultSpy, 'load').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should return 200 on success', async () => { From f17b721732c4558b5e5a3b747a76a54edfb2e258 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:02:53 -0300 Subject: [PATCH 05/15] feat: ensure LoadSurveysController to throw any throw --- .../controllers/load-surveys-controller.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/presentation/controllers/load-surveys-controller.ts b/src/presentation/controllers/load-surveys-controller.ts index 29e2c9dc..4602711c 100644 --- a/src/presentation/controllers/load-surveys-controller.ts +++ b/src/presentation/controllers/load-surveys-controller.ts @@ -1,17 +1,13 @@ -import { Controller, HttpResponse } from '@/presentation/protocols' -import { noContent, serverError, ok } from '@/presentation/helpers' import { LoadSurveys } from '@/domain/usecases' +import { noContent, ok } from '@/presentation/helpers' +import { Controller, HttpResponse } from '@/presentation/protocols' export class LoadSurveysController implements Controller { constructor (private readonly loadSurveys: LoadSurveys) {} async handle (request: LoadSurveysController.Request): Promise { - try { - const surveys = await this.loadSurveys.load(request.accountId) - return surveys.length ? ok(surveys) : noContent() - } catch (error) { - return serverError(error) - } + const surveys = await this.loadSurveys.load(request.accountId) + return surveys.length ? ok(surveys) : noContent() } } From 5d6a09d6c70cf4c675f555d720d12de4f381c413 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:03:07 -0300 Subject: [PATCH 06/15] test: ensure LoadSurveysController to throw any throw --- .../controllers/load-surveys-controller.spec.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/presentation/controllers/load-surveys-controller.spec.ts b/tests/presentation/controllers/load-surveys-controller.spec.ts index 1546ade4..d74853a9 100644 --- a/tests/presentation/controllers/load-surveys-controller.spec.ts +++ b/tests/presentation/controllers/load-surveys-controller.spec.ts @@ -1,10 +1,9 @@ import { LoadSurveysController } from '@/presentation/controllers' -import { ok, serverError, noContent } from '@/presentation/helpers' -import { LoadSurveysSpy } from '@/tests/presentation/mocks' +import { noContent, ok } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - -import MockDate from 'mockdate' +import { LoadSurveysSpy } from '@/tests/presentation/mocks' import faker from 'faker' +import MockDate from 'mockdate' const mockRequest = (): LoadSurveysController.Request => ({ accountId: faker.datatype.uuid() }) @@ -51,10 +50,10 @@ describe('LoadSurveys Controller', () => { expect(httpResponse).toEqual(noContent()) }) - test('Should return 500 if LoadSurveys throws', async () => { + test('Should throw if LoadSurveys throws', async () => { const { sut, loadSurveysSpy } = makeSut() jest.spyOn(loadSurveysSpy, 'load').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) }) From 5963dc6a91dcfbeb052194ced61ed9d6fe40c7c3 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:03:35 -0300 Subject: [PATCH 07/15] feat: ensure LoginController to throw any throw --- .../controllers/login-controller.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/presentation/controllers/login-controller.ts b/src/presentation/controllers/login-controller.ts index 7e63e931..0e6e67e8 100644 --- a/src/presentation/controllers/login-controller.ts +++ b/src/presentation/controllers/login-controller.ts @@ -1,6 +1,6 @@ -import { Controller, HttpResponse, Validation } from '@/presentation/protocols' -import { badRequest, serverError, unauthorized, ok } from '@/presentation/helpers' import { Authentication } from '@/domain/usecases' +import { badRequest, ok, unauthorized } from '@/presentation/helpers' +import { Controller, HttpResponse, Validation } from '@/presentation/protocols' export class LoginController implements Controller { constructor ( @@ -9,19 +9,15 @@ export class LoginController implements Controller { ) {} async handle (request: LoginController.Request): Promise { - try { - const error = this.validation.validate(request) - if (error) { - return badRequest(error) - } - const authenticationModel = await this.authentication.auth(request) - if (!authenticationModel) { - return unauthorized() - } - return ok(authenticationModel) - } catch (error) { - return serverError(error) + const error = this.validation.validate(request) + if (error) { + return badRequest(error) + } + const authenticationModel = await this.authentication.auth(request) + if (!authenticationModel) { + return unauthorized() } + return ok(authenticationModel) } } From ebf3f9031670e4ee9c518cb4fcd85f3a2a51c544 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:03:57 -0300 Subject: [PATCH 08/15] test: ensure LoginController to throw any throw --- .../presentation/controllers/login-controller.spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/presentation/controllers/login-controller.spec.ts b/tests/presentation/controllers/login-controller.spec.ts index 411a3a5e..c69ebfe6 100644 --- a/tests/presentation/controllers/login-controller.spec.ts +++ b/tests/presentation/controllers/login-controller.spec.ts @@ -1,9 +1,8 @@ import { LoginController } from '@/presentation/controllers' -import { badRequest, serverError, unauthorized, ok } from '@/presentation/helpers' import { MissingParamError } from '@/presentation/errors' -import { AuthenticationSpy, ValidationSpy } from '@/tests/presentation/mocks' +import { badRequest, ok, unauthorized } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - +import { AuthenticationSpy, ValidationSpy } from '@/tests/presentation/mocks' import faker from 'faker' const mockRequest = (): LoginController.Request => ({ @@ -46,11 +45,11 @@ describe('Login Controller', () => { expect(httpResponse).toEqual(unauthorized()) }) - test('Should return 500 if Authentication throws', async () => { + test('Should throw if Authentication throws', async () => { const { sut, authenticationSpy } = makeSut() jest.spyOn(authenticationSpy, 'auth').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should return 200 if valid credentials are provided', async () => { From 0ecf6fc7d4ba97e1a581397b4055e12078eda331 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:04:30 -0300 Subject: [PATCH 09/15] feat: ensure SaveSurveyResultController to throw any throw --- .../save-survey-result-controller.ts | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/presentation/controllers/save-survey-result-controller.ts b/src/presentation/controllers/save-survey-result-controller.ts index c01ea0ff..0db21b94 100644 --- a/src/presentation/controllers/save-survey-result-controller.ts +++ b/src/presentation/controllers/save-survey-result-controller.ts @@ -1,7 +1,7 @@ -import { Controller, HttpResponse } from '@/presentation/protocols' -import { forbidden, serverError, ok } from '@/presentation/helpers' -import { InvalidParamError } from '@/presentation/errors' import { LoadAnswersBySurvey, SaveSurveyResult } from '@/domain/usecases' +import { InvalidParamError } from '@/presentation/errors' +import { forbidden, ok } from '@/presentation/helpers' +import { Controller, HttpResponse } from '@/presentation/protocols' export class SaveSurveyResultController implements Controller { constructor ( @@ -10,22 +10,18 @@ export class SaveSurveyResultController implements Controller { ) {} async handle (request: SaveSurveyResultController.Request): Promise { - try { - const { surveyId, answer } = request - const answers = await this.loadAnswersBySurvey.loadAnswers(surveyId) - if (!answers.length) { - return forbidden(new InvalidParamError('surveyId')) - } else if (!answers.includes(answer)) { - return forbidden(new InvalidParamError('answer')) - } - const surveyResult = await this.saveSurveyResult.save({ - ...request, - date: new Date() - }) - return ok(surveyResult) - } catch (error) { - return serverError(error) + const { surveyId, answer } = request + const answers = await this.loadAnswersBySurvey.loadAnswers(surveyId) + if (!answers.length) { + return forbidden(new InvalidParamError('surveyId')) + } else if (!answers.includes(answer)) { + return forbidden(new InvalidParamError('answer')) } + const surveyResult = await this.saveSurveyResult.save({ + ...request, + date: new Date() + }) + return ok(surveyResult) } } From f77de94023bb983713932226be698c5acf65d553 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:04:45 -0300 Subject: [PATCH 10/15] test: ensure SaveSurveyResultController to throw any throw --- .../save-survey-result-controller.spec.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/presentation/controllers/save-survey-result-controller.spec.ts b/tests/presentation/controllers/save-survey-result-controller.spec.ts index fa10639d..970305a0 100644 --- a/tests/presentation/controllers/save-survey-result-controller.spec.ts +++ b/tests/presentation/controllers/save-survey-result-controller.spec.ts @@ -1,11 +1,10 @@ import { SaveSurveyResultController } from '@/presentation/controllers' import { InvalidParamError } from '@/presentation/errors' -import { forbidden, serverError, ok } from '@/presentation/helpers' -import { SaveSurveyResultSpy, LoadAnswersBySurveySpy } from '@/tests/presentation/mocks' +import { forbidden, ok } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - -import MockDate from 'mockdate' +import { LoadAnswersBySurveySpy, SaveSurveyResultSpy } from '@/tests/presentation/mocks' import faker from 'faker' +import MockDate from 'mockdate' const mockRequest = (answer: string = null): SaveSurveyResultController.Request => ({ surveyId: faker.datatype.uuid(), @@ -53,11 +52,11 @@ describe('SaveSurveyResult Controller', () => { expect(httpResponse).toEqual(forbidden(new InvalidParamError('surveyId'))) }) - test('Should return 500 if LoadAnswersBySurvey throws', async () => { + test('Should throw if LoadAnswersBySurvey throws', async () => { const { sut, loadAnswersBySurveySpy } = makeSut() jest.spyOn(loadAnswersBySurveySpy, 'loadAnswers').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should return 403 if an invalid answer is provided', async () => { @@ -78,12 +77,12 @@ describe('SaveSurveyResult Controller', () => { }) }) - test('Should return 500 if SaveSurveyResult throws', async () => { + test('Should throw if SaveSurveyResult throws', async () => { const { sut, saveSurveyResultSpy, loadAnswersBySurveySpy } = makeSut() jest.spyOn(saveSurveyResultSpy, 'save').mockImplementationOnce(throwError) const request = mockRequest(loadAnswersBySurveySpy.result[0]) - const httpResponse = await sut.handle(request) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(request) + await expect(promise).rejects.toThrow() }) test('Should return 200 on success', async () => { From b4b5cb9240cc6aa3f4cd3f6264551b0914d66b36 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:05:10 -0300 Subject: [PATCH 11/15] feat: ensure SignUpController to throw any throw --- .../controllers/signup-controller.ts | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/presentation/controllers/signup-controller.ts b/src/presentation/controllers/signup-controller.ts index e47da2c6..f54a4c31 100644 --- a/src/presentation/controllers/signup-controller.ts +++ b/src/presentation/controllers/signup-controller.ts @@ -1,7 +1,7 @@ -import { Controller, HttpResponse, Validation } from '@/presentation/protocols' -import { badRequest, serverError, ok, forbidden } from '@/presentation/helpers' -import { EmailInUseError } from '@/presentation/errors' import { AddAccount, Authentication } from '@/domain/usecases' +import { EmailInUseError } from '@/presentation/errors' +import { badRequest, forbidden, ok } from '@/presentation/helpers' +import { Controller, HttpResponse, Validation } from '@/presentation/protocols' export class SignUpController implements Controller { constructor ( @@ -11,28 +11,24 @@ export class SignUpController implements Controller { ) {} async handle (request: SignUpController.Request): Promise { - try { - const error = this.validation.validate(request) - if (error) { - return badRequest(error) - } - const { name, email, password } = request - const isValid = await this.addAccount.add({ - name, - email, - password - }) - if (!isValid) { - return forbidden(new EmailInUseError()) - } - const authenticationModel = await this.authentication.auth({ - email, - password - }) - return ok(authenticationModel) - } catch (error) { - return serverError(error) + const error = this.validation.validate(request) + if (error) { + return badRequest(error) + } + const { name, email, password } = request + const isValid = await this.addAccount.add({ + name, + email, + password + }) + if (!isValid) { + return forbidden(new EmailInUseError()) } + const authenticationModel = await this.authentication.auth({ + email, + password + }) + return ok(authenticationModel) } } From 7bb74daa0721a901a4203cb3520ded470b131ca0 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:05:26 -0300 Subject: [PATCH 12/15] test: ensure SignUpController to throw any throw --- .../controllers/signup-controller.spec.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/presentation/controllers/signup-controller.spec.ts b/tests/presentation/controllers/signup-controller.spec.ts index 1de15232..e8ff6469 100644 --- a/tests/presentation/controllers/signup-controller.spec.ts +++ b/tests/presentation/controllers/signup-controller.spec.ts @@ -1,9 +1,8 @@ import { SignUpController } from '@/presentation/controllers' -import { MissingParamError, ServerError, EmailInUseError } from '@/presentation/errors' -import { ok, serverError, badRequest, forbidden } from '@/presentation/helpers' -import { AuthenticationSpy, ValidationSpy, AddAccountSpy } from '@/tests/presentation/mocks' +import { EmailInUseError, MissingParamError } from '@/presentation/errors' +import { badRequest, forbidden, ok } from '@/presentation/helpers' import { throwError } from '@/tests/domain/mocks' - +import { AddAccountSpy, AuthenticationSpy, ValidationSpy } from '@/tests/presentation/mocks' import faker from 'faker' const mockRequest = (): SignUpController.Request => { @@ -37,11 +36,11 @@ const makeSut = (): SutTypes => { } describe('SignUp Controller', () => { - test('Should return 500 if AddAccount throws', async () => { + test('Should throw if AddAccount throws', async () => { const { sut, addAccountSpy } = makeSut() jest.spyOn(addAccountSpy, 'add').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new ServerError(null))) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) test('Should call AddAccount with correct values', async () => { @@ -92,10 +91,10 @@ describe('SignUp Controller', () => { }) }) - test('Should return 500 if Authentication throws', async () => { + test('Should throw if Authentication throws', async () => { const { sut, authenticationSpy } = makeSut() jest.spyOn(authenticationSpy, 'auth').mockImplementationOnce(throwError) - const httpResponse = await sut.handle(mockRequest()) - expect(httpResponse).toEqual(serverError(new Error())) + const promise = sut.handle(mockRequest()) + await expect(promise).rejects.toThrow() }) }) From 8551c85effce5a358f0b831e7c3c96607f0871cf Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:12:02 -0300 Subject: [PATCH 13/15] feat: ensure ServerErrorController Decorator returns 500 on any server error --- src/main/decorators/index.ts | 1 + .../server-error-controller-decorator.ts | 27 +++++++++++++++++++ src/presentation/errors/server-error.ts | 8 +++--- src/presentation/helpers/http-helper.ts | 4 +-- tsconfig.json | 2 +- 5 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/main/decorators/server-error-controller-decorator.ts diff --git a/src/main/decorators/index.ts b/src/main/decorators/index.ts index 0b2d5a18..ea14d659 100644 --- a/src/main/decorators/index.ts +++ b/src/main/decorators/index.ts @@ -1 +1,2 @@ export * from './log-controller-decorator' +export * from './server-error-controller-decorator' diff --git a/src/main/decorators/server-error-controller-decorator.ts b/src/main/decorators/server-error-controller-decorator.ts new file mode 100644 index 00000000..4ed8edba --- /dev/null +++ b/src/main/decorators/server-error-controller-decorator.ts @@ -0,0 +1,27 @@ +import { Controller, HttpResponse } from '@/presentation/protocols' +import { LogErrorRepository } from '@/data/protocols' +import { serverError } from '../../presentation/helpers' +import { ServerError } from '../../presentation/errors' + +export class ServerErrorControllerDecorator implements Controller { + constructor ( + private readonly controller: Controller, + private readonly logErrorRepository: LogErrorRepository + ) {} + + async handle (request: any): Promise { + let httpResponse: HttpResponse + try { + httpResponse = await this.controller.handle(request) + } catch (controllerError) { + const finalError = [controllerError] + try { + await this.logErrorRepository.logError(controllerError.stack) + } catch (logError) { + finalError.push(logError) + } + return serverError(new ServerError(...finalError)) + } + return httpResponse + } +} diff --git a/src/presentation/errors/server-error.ts b/src/presentation/errors/server-error.ts index 711557ed..524aa378 100644 --- a/src/presentation/errors/server-error.ts +++ b/src/presentation/errors/server-error.ts @@ -1,7 +1,7 @@ -export class ServerError extends Error { - constructor (stack: string) { - super('Internal server error') +export class ServerError extends AggregateError { + constructor (...errors: Error[]) { + super(errors, 'Internal server error') this.name = 'ServerError' - this.stack = stack + this.stack = errors.map(error => error.stack).join('\n') } } diff --git a/src/presentation/helpers/http-helper.ts b/src/presentation/helpers/http-helper.ts index c40432fc..e5baf2fa 100644 --- a/src/presentation/helpers/http-helper.ts +++ b/src/presentation/helpers/http-helper.ts @@ -16,9 +16,9 @@ export const unauthorized = (): HttpResponse => ({ body: new UnauthorizedError() }) -export const serverError = (error: Error): HttpResponse => ({ +export const serverError = (...errors: Error[]): HttpResponse => ({ statusCode: 500, - body: new ServerError(error.stack) + body: new ServerError(...errors) }) export const ok = (data: any): HttpResponse => ({ diff --git a/tsconfig.json b/tsconfig.json index 074ab698..087ac1b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "outDir": "dist", "module": "commonjs", - "target": "es2019", + "target": "es2021", "esModuleInterop": true, "sourceMap": true, "rootDirs": ["src", "tests"], From 069b97b5730d89f19d8b0e0683408845ef8e1d64 Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:12:19 -0300 Subject: [PATCH 14/15] test: ensure ServerErrorController Decorator returns 500 on any server error --- .../server-error-controller-decorator.spec.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/main/decorators/server-error-controller-decorator.spec.ts diff --git a/tests/main/decorators/server-error-controller-decorator.spec.ts b/tests/main/decorators/server-error-controller-decorator.spec.ts new file mode 100644 index 00000000..843b4c4f --- /dev/null +++ b/tests/main/decorators/server-error-controller-decorator.spec.ts @@ -0,0 +1,78 @@ +import { LogErrorRepository } from '@/data/protocols' +import { ServerErrorControllerDecorator } from '@/main/decorators' +import { ok, serverError } from '@/presentation/helpers' +import { Controller, HttpResponse } from '@/presentation/protocols' +import { throwError } from '@/tests/domain/mocks' +import faker from 'faker' + +class ControllerSpy implements Controller { + params: any + result = ok(faker.random.word()) + async handle (request: any): Promise { + this.params = request + return this.result + } +} +class LogErrorRepositorySpy implements LogErrorRepository { + params: string + async logError (error: string): Promise { + this.params = error + } +} + +type SutTypes = { + sut: ServerErrorControllerDecorator + controllerSpy: ControllerSpy + logErrorRepositorySpy: LogErrorRepositorySpy +} + +const makeSut = (): SutTypes => { + const controllerSpy = new ControllerSpy() + const logErrorRepositorySpy = new LogErrorRepositorySpy() + const sut = new ServerErrorControllerDecorator(controllerSpy, logErrorRepositorySpy) + return { + sut, + controllerSpy, + logErrorRepositorySpy + } +} + +describe('ServerErrorController Decorator', () => { + test('Should call Controller with correct values', async () => { + const { controllerSpy, sut } = makeSut() + const params = faker.lorem.sentences() + await sut.handle(params) + expect(controllerSpy.params).toEqual(params) + }) + + test('Should return the Controller\'s return', async () => { + const { controllerSpy, sut } = makeSut() + const httpResponse = await sut.handle(faker.lorem.sentences()) + expect(httpResponse).toEqual(controllerSpy.result) + }) + + test('Should return 500 if Controller throws', async () => { + const { controllerSpy, sut } = makeSut() + jest.spyOn(controllerSpy,'handle').mockImplementation(throwError) + const httpResponse = await sut.handle(faker.lorem.sentences()) + expect(httpResponse).toEqual(serverError(new Error())) + }) + + test('Should call LogErrorRepository with correct values', async () => { + const { sut, logErrorRepositorySpy, controllerSpy } = makeSut() + const error = new Error() + error.stack = faker.lorem.sentences() + jest.spyOn(controllerSpy,'handle').mockImplementation(async (): Promise => { throw error }) + const params = faker.lorem.sentences() + await sut.handle(params) + expect(logErrorRepositorySpy.params).toEqual(error.stack) + }) + + test('Should return 500 if LogErrorRepository throws', async () => { + const { logErrorRepositorySpy, controllerSpy, sut } = makeSut() + jest.spyOn(controllerSpy,'handle').mockImplementation(() => { throw new Error('Controller error') }) + jest.spyOn(logErrorRepositorySpy,'logError').mockImplementation(() => { throw new Error('LogErrorRepository error') }) + const httpResponse = await sut.handle(faker.lorem.sentences()) + expect(httpResponse).toEqual(serverError(new Error('Controller error'), new Error('LogErrorRepository error'))) + }) +}) From c41684386482d979dead6a0115052aab7878ec8b Mon Sep 17 00:00:00 2001 From: Guilherme Ledes Date: Mon, 20 Dec 2021 16:14:40 -0300 Subject: [PATCH 15/15] refactor: replace LogControllerDecorator by ServerErrorControllerDecorator --- src/main/decorators/index.ts | 1 - .../decorators/log-controller-decorator.ts | 17 ----- .../add-survey-controller-factory.ts | 4 +- .../load-survey-result-controller-factory.ts | 4 +- .../load-surveys-controller-factory.ts | 4 +- .../controllers/login-controller-factory.ts | 4 +- .../save-survey-result-controller-factory.ts | 4 +- .../controllers/signup-controller-factory.ts | 4 +- src/main/factories/decorators/index.ts | 2 +- .../log-controller-decorator-factory.ts | 8 --- ...rver-error-controller-decorator-factory.ts | 8 +++ .../log-controller-decorator.spec.ts | 62 ------------------- 12 files changed, 21 insertions(+), 101 deletions(-) delete mode 100644 src/main/decorators/log-controller-decorator.ts delete mode 100644 src/main/factories/decorators/log-controller-decorator-factory.ts create mode 100644 src/main/factories/decorators/server-error-controller-decorator-factory.ts delete mode 100644 tests/main/decorators/log-controller-decorator.spec.ts diff --git a/src/main/decorators/index.ts b/src/main/decorators/index.ts index ea14d659..c6830eda 100644 --- a/src/main/decorators/index.ts +++ b/src/main/decorators/index.ts @@ -1,2 +1 @@ -export * from './log-controller-decorator' export * from './server-error-controller-decorator' diff --git a/src/main/decorators/log-controller-decorator.ts b/src/main/decorators/log-controller-decorator.ts deleted file mode 100644 index 18f3243c..00000000 --- a/src/main/decorators/log-controller-decorator.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Controller, HttpResponse } from '@/presentation/protocols' -import { LogErrorRepository } from '@/data/protocols/db' - -export class LogControllerDecorator implements Controller { - constructor ( - private readonly controller: Controller, - private readonly logErrorRepository: LogErrorRepository - ) {} - - async handle (request: any): Promise { - const httpResponse = await this.controller.handle(request) - if (httpResponse.statusCode === 500) { - await this.logErrorRepository.logError(httpResponse.body.stack) - } - return httpResponse - } -} diff --git a/src/main/factories/controllers/add-survey-controller-factory.ts b/src/main/factories/controllers/add-survey-controller-factory.ts index a8ddd93b..ed54da69 100644 --- a/src/main/factories/controllers/add-survey-controller-factory.ts +++ b/src/main/factories/controllers/add-survey-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeAddSurveyValidation, makeLogControllerDecorator, makeDbAddSurvey } from '@/main/factories' +import { makeAddSurveyValidation, makeServerErrorControllerDecorator, makeDbAddSurvey } from '@/main/factories' import { Controller } from '@/presentation/protocols' import { AddSurveyController } from '@/presentation/controllers' export const makeAddSurveyController = (): Controller => { const controller = new AddSurveyController(makeAddSurveyValidation(), makeDbAddSurvey()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/controllers/load-survey-result-controller-factory.ts b/src/main/factories/controllers/load-survey-result-controller-factory.ts index 8c845029..d5e23486 100644 --- a/src/main/factories/controllers/load-survey-result-controller-factory.ts +++ b/src/main/factories/controllers/load-survey-result-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeLogControllerDecorator, makeDbCheckSurveyById, makeDbLoadSurveyResult } from '@/main/factories' +import { makeServerErrorControllerDecorator, makeDbCheckSurveyById, makeDbLoadSurveyResult } from '@/main/factories' import { Controller } from '@/presentation/protocols' import { LoadSurveyResultController } from '@/presentation/controllers' export const makeLoadSurveyResultController = (): Controller => { const controller = new LoadSurveyResultController(makeDbCheckSurveyById(), makeDbLoadSurveyResult()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/controllers/load-surveys-controller-factory.ts b/src/main/factories/controllers/load-surveys-controller-factory.ts index 1e93260e..983bfec2 100644 --- a/src/main/factories/controllers/load-surveys-controller-factory.ts +++ b/src/main/factories/controllers/load-surveys-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeLogControllerDecorator, makeDbLoadSurveys } from '@/main/factories' +import { makeServerErrorControllerDecorator, makeDbLoadSurveys } from '@/main/factories' import { Controller } from '@/presentation/protocols' import { LoadSurveysController } from '@/presentation/controllers' export const makeLoadSurveysController = (): Controller => { const controller = new LoadSurveysController(makeDbLoadSurveys()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/controllers/login-controller-factory.ts b/src/main/factories/controllers/login-controller-factory.ts index d7b6aa72..dced8d68 100644 --- a/src/main/factories/controllers/login-controller-factory.ts +++ b/src/main/factories/controllers/login-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeDbAuthentication, makeLoginValidation, makeLogControllerDecorator } from '@/main/factories' +import { makeDbAuthentication, makeLoginValidation, makeServerErrorControllerDecorator } from '@/main/factories' import { Controller } from '@/presentation/protocols' import { LoginController } from '@/presentation/controllers' export const makeLoginController = (): Controller => { const controller = new LoginController(makeDbAuthentication(), makeLoginValidation()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/controllers/save-survey-result-controller-factory.ts b/src/main/factories/controllers/save-survey-result-controller-factory.ts index 28370a34..8e85ada4 100644 --- a/src/main/factories/controllers/save-survey-result-controller-factory.ts +++ b/src/main/factories/controllers/save-survey-result-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeLogControllerDecorator, makeDbLoadAnswersBySurvey, makeDbSaveSurveyResult } from '@/main/factories' +import { makeServerErrorControllerDecorator, makeDbLoadAnswersBySurvey, makeDbSaveSurveyResult } from '@/main/factories' import { Controller } from '@/presentation/protocols' import { SaveSurveyResultController } from '@/presentation/controllers' export const makeSaveSurveyResultController = (): Controller => { const controller = new SaveSurveyResultController(makeDbLoadAnswersBySurvey(), makeDbSaveSurveyResult()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/controllers/signup-controller-factory.ts b/src/main/factories/controllers/signup-controller-factory.ts index 3c4270c7..735ad5c3 100644 --- a/src/main/factories/controllers/signup-controller-factory.ts +++ b/src/main/factories/controllers/signup-controller-factory.ts @@ -1,8 +1,8 @@ -import { makeDbAuthentication, makeSignUpValidation, makeLogControllerDecorator, makeDbAddAccount } from '@/main/factories' +import { makeDbAuthentication, makeSignUpValidation, makeServerErrorControllerDecorator, makeDbAddAccount } from '@/main/factories' import { SignUpController } from '@/presentation/controllers' import { Controller } from '@/presentation/protocols' export const makeSignUpController = (): Controller => { const controller = new SignUpController(makeDbAddAccount(), makeSignUpValidation(), makeDbAuthentication()) - return makeLogControllerDecorator(controller) + return makeServerErrorControllerDecorator(controller) } diff --git a/src/main/factories/decorators/index.ts b/src/main/factories/decorators/index.ts index 7c04ba20..f2508a18 100644 --- a/src/main/factories/decorators/index.ts +++ b/src/main/factories/decorators/index.ts @@ -1 +1 @@ -export * from './log-controller-decorator-factory' +export * from './server-error-controller-decorator-factory' diff --git a/src/main/factories/decorators/log-controller-decorator-factory.ts b/src/main/factories/decorators/log-controller-decorator-factory.ts deleted file mode 100644 index 1c1210aa..00000000 --- a/src/main/factories/decorators/log-controller-decorator-factory.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { LogControllerDecorator } from '@/main/decorators' -import { LogMongoRepository } from '@/infra/db' -import { Controller } from '@/presentation/protocols' - -export const makeLogControllerDecorator = (controller: Controller): Controller => { - const logMongoRepository = new LogMongoRepository() - return new LogControllerDecorator(controller, logMongoRepository) -} diff --git a/src/main/factories/decorators/server-error-controller-decorator-factory.ts b/src/main/factories/decorators/server-error-controller-decorator-factory.ts new file mode 100644 index 00000000..6180e74d --- /dev/null +++ b/src/main/factories/decorators/server-error-controller-decorator-factory.ts @@ -0,0 +1,8 @@ +import { LogMongoRepository } from '@/infra/db' +import { ServerErrorControllerDecorator } from '@/main/decorators' +import { Controller } from '@/presentation/protocols' + +export const makeServerErrorControllerDecorator = (controller: Controller): Controller => { + const logMongoRepository = new LogMongoRepository() + return new ServerErrorControllerDecorator(controller, logMongoRepository) +} diff --git a/tests/main/decorators/log-controller-decorator.spec.ts b/tests/main/decorators/log-controller-decorator.spec.ts deleted file mode 100644 index 9739c817..00000000 --- a/tests/main/decorators/log-controller-decorator.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { LogControllerDecorator } from '@/main/decorators' -import { Controller, HttpResponse } from '@/presentation/protocols' -import { serverError, ok } from '@/presentation/helpers' -import { LogErrorRepositorySpy } from '@/tests/data/mocks' - -import faker from 'faker' - -class ControllerSpy implements Controller { - httpResponse = ok(faker.datatype.uuid()) - request: any - - async handle (request: any): Promise { - this.request = request - return this.httpResponse - } -} - -const mockServerError = (): HttpResponse => { - const fakeError = new Error() - fakeError.stack = 'any_stack' - return serverError(fakeError) -} - -type SutTypes = { - sut: LogControllerDecorator - controllerSpy: ControllerSpy - logErrorRepositorySpy: LogErrorRepositorySpy -} - -const makeSut = (): SutTypes => { - const controllerSpy = new ControllerSpy() - const logErrorRepositorySpy = new LogErrorRepositorySpy() - const sut = new LogControllerDecorator(controllerSpy, logErrorRepositorySpy) - return { - sut, - controllerSpy, - logErrorRepositorySpy - } -} - -describe('LogController Decorator', () => { - test('Should call controller handle', async () => { - const { sut, controllerSpy } = makeSut() - const request = faker.lorem.sentence() - await sut.handle(request) - expect(controllerSpy.request).toEqual(request) - }) - - test('Should return the same result of the controller', async () => { - const { sut, controllerSpy } = makeSut() - const httpResponse = await sut.handle(faker.lorem.sentence()) - expect(httpResponse).toEqual(controllerSpy.httpResponse) - }) - - test('Should call LogErrorRepository with correct error if controller returns a server error', async () => { - const { sut, controllerSpy, logErrorRepositorySpy } = makeSut() - const serverError = mockServerError() - controllerSpy.httpResponse = serverError - await sut.handle(faker.lorem.sentence()) - expect(logErrorRepositorySpy.stack).toBe(serverError.body.stack) - }) -})