Skip to content

Commit

Permalink
Merge pull request #8 from kistgab/feat/load-survey-result
Browse files Browse the repository at this point in the history
Feat/load survey result
  • Loading branch information
ogabrielkist authored Mar 25, 2024
2 parents 129069f + 0101267 commit 85d4bcb
Show file tree
Hide file tree
Showing 24 changed files with 745 additions and 71 deletions.
13 changes: 13 additions & 0 deletions src/data/models/survey-result-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type SurveyResultModel = {
surveyId: string;
question: string;
date: Date;
answers: SurveyResultAnswerModel[];
};

type SurveyResultAnswerModel = {
image?: string;
answer: string;
count: number;
percent: number;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SurveyResultModel } from "@src/data/models/survey-result-model";

export interface LoadSurveyResultRepository {
loadBySurveyId(id: string): Promise<SurveyResultModel | null>;
}
7 changes: 2 additions & 5 deletions src/data/protocols/db/survey/save-survey-answer-repository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
SaveSurveyAnswerModel,
SurveyAnswerModel,
} from "@src/data/models/save-survey-answer-model";
import { SaveSurveyAnswerModel } from "@src/data/models/save-survey-answer-model";

export interface SaveSurveyAnswerRepository {
save(data: SaveSurveyAnswerModel): Promise<SurveyAnswerModel>;
save(data: SaveSurveyAnswerModel): Promise<void>;
}
12 changes: 12 additions & 0 deletions src/data/test/mock-db-survey-result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SurveyResultModel } from "@src/data/models/survey-result-model";
import { LoadSurveyResultRepository } from "@src/data/protocols/db/survey-answer/load-survey-result-repository";
import { mockSurveyResultModel } from "@src/domain/test/mock-survey";

export function mockLoadSurveyResultRepository(): LoadSurveyResultRepository {
class LoadSurveyResultRepositoryStub implements LoadSurveyResultRepository {
async loadBySurveyId(): Promise<SurveyResultModel> {
return Promise.resolve(mockSurveyResultModel());
}
}
return new LoadSurveyResultRepositoryStub();
}
11 changes: 3 additions & 8 deletions src/data/test/mock-db-survey.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { SurveyAnswerModel } from "@src/data/models/save-survey-answer-model";
import { SurveyModel } from "@src/data/models/survey-model";
import AddSurveyRepository from "@src/data/protocols/db/survey/add-survey-repository";
import { FindAllSurveysRepository } from "@src/data/protocols/db/survey/find-all-surveys-repository";
import { FindSurveyByIdRepository } from "@src/data/protocols/db/survey/find-by-id-surveys-repository";
import { SaveSurveyAnswerRepository } from "@src/data/protocols/db/survey/save-survey-answer-repository";
import {
mockSurveyAnswerModel,
mockSurveyModel,
mockSurveyModelList,
} from "@src/domain/test/mock-survey";
import { mockSurveyModel, mockSurveyModelList } from "@src/domain/test/mock-survey";

export function mockSurveyRepository(): AddSurveyRepository {
class AddSurveyRepositoryStub implements AddSurveyRepository {
Expand Down Expand Up @@ -39,8 +34,8 @@ export function mockFindSurveyByIdRepository(): FindSurveyByIdRepository {

export function mockSaveSurveyAnswerRepository(): SaveSurveyAnswerRepository {
class SaveSurveyRepositoryStub implements SaveSurveyAnswerRepository {
async save(): Promise<SurveyAnswerModel> {
return Promise.resolve(mockSurveyAnswerModel());
async save(): Promise<void> {
return Promise.resolve();
}
}
return new SaveSurveyRepositoryStub();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { LoadSurveyResultRepository } from "@src/data/protocols/db/survey-answer/load-survey-result-repository";
import { SaveSurveyAnswerRepository } from "@src/data/protocols/db/survey/save-survey-answer-repository";
import { mockSaveSurveyAnswerRepository } from "@src/data/test/mock-db-survey";
import { mockLoadSurveyResultRepository } from "@src/data/test/mock-db-survey-result";
import { DbAnswerSurvey } from "@src/data/usecases/survey-answer/answer-survey/db-answer-survey";
import { mockInputAnswerSurveyDto, mockSurveyAnswerModel } from "@src/domain/test/mock-survey";
import { mockInputAnswerSurveyDto, mockSurveyResultModel } from "@src/domain/test/mock-survey";
import * as Mockdate from "mockdate";

type SutTypes = {
sut: DbAnswerSurvey;
saveSurveyAnswerRepositoryStub: SaveSurveyAnswerRepository;
loadSurveyResultRepositoryStub: LoadSurveyResultRepository;
};

function createSut(): SutTypes {
const saveSurveyAnswerRepositoryStub = mockSaveSurveyAnswerRepository();
const sut = new DbAnswerSurvey(saveSurveyAnswerRepositoryStub);
const loadSurveyResultRepositoryStub = mockLoadSurveyResultRepository();
const sut = new DbAnswerSurvey(saveSurveyAnswerRepositoryStub, loadSurveyResultRepositoryStub);
return {
sut,
saveSurveyAnswerRepositoryStub,
loadSurveyResultRepositoryStub,
};
}

Expand Down Expand Up @@ -48,11 +53,32 @@ describe("DbAnswerSurvey UseCase", () => {
);
});

it("should call LoadSurveyResultRepository with correct values", async () => {
const { sut, loadSurveyResultRepositoryStub } = createSut();
const loadBySurveyIdSpy = jest.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId");
const input = mockInputAnswerSurveyDto();

await sut.answer(input);

expect(loadBySurveyIdSpy).toHaveBeenCalledWith(input.surveyId);
});

it("should throw when LoadSurveyResultRepository throws", async () => {
const { sut, loadSurveyResultRepositoryStub } = createSut();
jest
.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId")
.mockReturnValueOnce(Promise.reject(new Error("Repository error")));

await expect(sut.answer(mockInputAnswerSurveyDto())).rejects.toThrow(
new Error("Repository error"),
);
});

it("should return a survey answer on success", async () => {
const { sut } = createSut();

const result = await sut.answer(mockInputAnswerSurveyDto());

expect(result).toEqual(mockSurveyAnswerModel());
expect(result).toEqual(mockSurveyResultModel());
});
});
16 changes: 11 additions & 5 deletions src/data/usecases/survey-answer/answer-survey/db-answer-survey.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { SurveyAnswerModel } from "@src/data/models/save-survey-answer-model";
import { SurveyResultModel } from "@src/data/models/survey-result-model";
import { LoadSurveyResultRepository } from "@src/data/protocols/db/survey-answer/load-survey-result-repository";
import { SaveSurveyAnswerRepository } from "@src/data/protocols/db/survey/save-survey-answer-repository";
import { InputAnswerSurveyDto } from "@src/domain/dtos/answer-survey-dto";
import AnswerSurvey from "@src/domain/usecases/survey-answer/answer-survey";

export class DbAnswerSurvey implements AnswerSurvey {
constructor(private readonly saveSurveyAnswerRepository: SaveSurveyAnswerRepository) {}
constructor(
private readonly saveSurveyAnswerRepository: SaveSurveyAnswerRepository,
private readonly loadSurveyResultRepository: LoadSurveyResultRepository,
) {}

async answer(data: InputAnswerSurveyDto): Promise<SurveyAnswerModel> {
const savedAnswer = await this.saveSurveyAnswerRepository.save(data);
return savedAnswer;
async answer(data: InputAnswerSurveyDto): Promise<SurveyResultModel> {
await this.saveSurveyAnswerRepository.save(data);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const surveyResult = (await this.loadSurveyResultRepository.loadBySurveyId(data.surveyId))!;
return surveyResult;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { SurveyResultModel } from "@src/data/models/survey-result-model";
import { LoadSurveyResultRepository } from "@src/data/protocols/db/survey-answer/load-survey-result-repository";
import { FindSurveyByIdRepository } from "@src/data/protocols/db/survey/find-by-id-surveys-repository";
import { mockFindSurveyByIdRepository } from "@src/data/test/mock-db-survey";
import { mockLoadSurveyResultRepository } from "@src/data/test/mock-db-survey-result";
import { DbLoadSurveyResult } from "@src/data/usecases/survey-answer/load-survey-result/db-load-survey-result";
import { mockSurveyResultModel } from "@src/domain/test/mock-survey";
import * as Mockdate from "mockdate";

type SutTypes = {
sut: DbLoadSurveyResult;
loadSurveyResultRepositoryStub: LoadSurveyResultRepository;
findSurveyByIdRepositoryStub: FindSurveyByIdRepository;
};

function createSut(): SutTypes {
const loadSurveyResultRepositoryStub = mockLoadSurveyResultRepository();
const findSurveyByIdRepositoryStub = mockFindSurveyByIdRepository();
const sut = new DbLoadSurveyResult(loadSurveyResultRepositoryStub, findSurveyByIdRepositoryStub);
return {
sut,
loadSurveyResultRepositoryStub,
findSurveyByIdRepositoryStub,
};
}

describe("DbLoadSurveyResult UseCase", () => {
beforeAll(() => {
Mockdate.set(new Date());
});

afterAll(() => {
Mockdate.reset();
});

it("Should call LoadSurveyResultRepository", async () => {
const { sut, loadSurveyResultRepositoryStub } = createSut();
const loadBySurveyIdSpy = jest.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId");

await sut.load("any_survey_id");

expect(loadBySurveyIdSpy).toHaveBeenCalledWith("any_survey_id");
});

it("should throw when LoadSurveyResultRepository throws", async () => {
const { sut, loadSurveyResultRepositoryStub } = createSut();
jest
.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId")
.mockReturnValueOnce(Promise.reject(new Error("Repository error")));

await expect(sut.load("any_id")).rejects.toThrow(new Error("Repository error"));
});

it("should return a SurveyResultModel on success", async () => {
const { sut } = createSut();

const result = await sut.load("any_id");

expect(result).toEqual(mockSurveyResultModel());
});

it("should call FindSurveyByIdRepository if LoadSurveyResultRepository returns null", async () => {
const { sut, loadSurveyResultRepositoryStub, findSurveyByIdRepositoryStub } = createSut();
jest
.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId")
.mockReturnValueOnce(Promise.resolve(null));
const findById = jest.spyOn(findSurveyByIdRepositoryStub, "findById");

await sut.load("any_id");

expect(findById).toHaveBeenCalledWith("any_id");
});

it("should return a SurveyResultModel that every answers have count and percent as 0", async () => {
const { sut, loadSurveyResultRepositoryStub } = createSut();
jest
.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId")
.mockReturnValueOnce(Promise.resolve(null));

const result = await sut.load("any_id");

const expectedResult: SurveyResultModel = {
...mockSurveyResultModel(),
answers: [{ ...mockSurveyResultModel().answers[0], count: 0, percent: 0 }],
};
expect(result).toEqual(expectedResult);
});

it("should return null if there is no survey with the specified id", async () => {
const { sut, loadSurveyResultRepositoryStub, findSurveyByIdRepositoryStub } = createSut();
jest
.spyOn(loadSurveyResultRepositoryStub, "loadBySurveyId")
.mockReturnValueOnce(Promise.resolve(null));
jest.spyOn(findSurveyByIdRepositoryStub, "findById").mockReturnValueOnce(Promise.resolve(null));

const result = await sut.load("any_non_existing_id");

expect(result).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { SurveyResultModel } from "@src/data/models/survey-result-model";
import { LoadSurveyResultRepository } from "@src/data/protocols/db/survey-answer/load-survey-result-repository";
import { FindSurveyByIdRepository } from "@src/data/protocols/db/survey/find-by-id-surveys-repository";
import { LoadSurveyResult } from "@src/domain/usecases/survey-answer/load-survey-result";

export class DbLoadSurveyResult implements LoadSurveyResult {
constructor(
private readonly loadSurveyResultRepository: LoadSurveyResultRepository,
private readonly findSurveyByIdRepository: FindSurveyByIdRepository,
) {}

async load(surveyId: string): Promise<SurveyResultModel | null> {
let surveyResult = await this.loadSurveyResultRepository.loadBySurveyId(surveyId);
if (surveyResult) {
return surveyResult;
}
const survey = await this.findSurveyByIdRepository.findById(surveyId);
if (!survey) {
return null;
}
surveyResult = {
surveyId: survey.id,
question: survey.question,
date: survey.date,
answers: survey.answers.map((answer) => ({
...answer,
count: 0,
percent: 0,
})),
};
return surveyResult;
}
}
10 changes: 8 additions & 2 deletions src/domain/dtos/answer-survey-dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ export type InputAnswerSurveyDto = {
};

export type OutputAnswerSurveyDto = {
id: string;
surveyId: string;
accountId: string;
question: string;
date: Date;
answers: SurveyResultAnswerModel[];
};

type SurveyResultAnswerModel = {
image?: string;
answer: string;
count: number;
percent: number;
};
44 changes: 34 additions & 10 deletions src/domain/test/mock-survey.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SurveyAnswerModel } from "@src/data/models/save-survey-answer-model";
import { AddSurveyModel, SurveyModel } from "@src/data/models/survey-model";
import { SurveyResultModel } from "@src/data/models/survey-result-model";
import { InputAddSurveyDto } from "@src/domain/dtos/add-survey-dto";
import { InputAnswerSurveyDto, OutputAnswerSurveyDto } from "@src/domain/dtos/answer-survey-dto";

Expand Down Expand Up @@ -56,12 +56,24 @@ export function mockSurveyModelList(): SurveyModel[] {
];
}

export function mockSurveyAnswerModel(): SurveyAnswerModel {
export function mockSurveyResultModel(): SurveyResultModel {
return {
id: "valid_id",
accountId: "valid_account_id",
surveyId: "valid_survey_id",
answer: "valid_answer",
answers: [
{
answer: "any_answer",
count: 1,
percent: 100,
image: "any_image",
},
{
answer: "another_answer",
count: 0,
percent: 0,
image: "another_image",
},
],
question: "any_question",
surveyId: "any_id",
date: new Date(),
};
}
Expand All @@ -77,11 +89,23 @@ export function mockInputAnswerSurveyDto(): InputAnswerSurveyDto {

export function mockOutputSurveyAnswerDto(): OutputAnswerSurveyDto {
return {
id: "any_id",
surveyId: "any_survey_id",
accountId: "any_account_id",
answers: [
{
answer: "any_answer",
count: 1,
percent: 100,
image: "any_image",
},
{
answer: "another_answer",
count: 0,
percent: 0,
image: "another_image",
},
],
question: "valid_question",
surveyId: "valid_survey_id",
date: new Date(),
answer: "any_answer",
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/domain/usecases/survey-answer/load-survey-result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SurveyResultModel } from "@src/data/models/survey-result-model";

export interface LoadSurveyResult {
load(surveyId: string): Promise<SurveyResultModel | null>;
}
Loading

0 comments on commit 85d4bcb

Please sign in to comment.