From 36646e477e833b6455696864fe8e209069c4a77d Mon Sep 17 00:00:00 2001 From: Leonardo Giraldi Moreno Giuranno Date: Wed, 22 May 2024 21:20:35 -0300 Subject: [PATCH] feat: add new fastify controller This commit includes: - Implementation of main script - Configuration of Task table seeding - Implementation of fastify Task controller - implementation of fastify Task controller unit tests --- src/app.ts | 151 +++- src/infra/datasource/typeorm/ds.typeorm.ts | 13 +- src/infra/datasource/typeorm/index.ts | 1 + src/infra/datasource/typeorm/seeds/index.ts | 1 + .../datasource/typeorm/seeds/main.seeder.ts | 15 + src/infra/datasource/typeorm/task/index.ts | 7 +- .../typeorm/task/task.seeder.factory.ts | 23 + .../datasource/typeorm/task/task.seeder.ts | 17 + .../web/fastify/task/controllers/index.ts | 2 + .../task/controllers/task.controller.ts | 173 ++++ .../fastify/task/controllers/types/index.ts | 1 + .../controllers/types/request.params.id.ts | 5 + src/infra/web/fastify/task/index.ts | 3 + src/infra/web/fastify/task/schemas/index.ts | 3 + .../fastify/task/schemas/insertTask.schema.ts | 18 + .../web/fastify/task/schemas/taskId.schema.ts | 7 + .../fastify/task/schemas/updateTask.schema.ts | 18 + src/infra/web/fastify/task/standards/index.ts | 4 + .../web/fastify/task/standards/pagination.ts | 24 + .../task/standards/standard.response.error.ts | 13 + .../standards/standard.response.success.ts | 14 + .../task/standards/standard.response.ts | 27 + .../web/fastify/task/task.controller.test.ts | 840 ++++++++++++++++++ src/tests/unit/mocks/fastify/fastify.mock.ts | 8 + src/tests/unit/mocks/fastify/index.ts | 1 + ...eateTask.controller.input.boundary.mock.ts | 5 + ...leteTask.controller.input.boundary.mock.ts | 5 + src/tests/unit/mocks/task/index.ts | 4 + ...TaskById.controller.input.boundary.mock.ts | 5 + ...dateTask.controller.input.boundary.mock.ts | 5 + 30 files changed, 1406 insertions(+), 7 deletions(-) create mode 100644 src/infra/datasource/typeorm/seeds/index.ts create mode 100644 src/infra/datasource/typeorm/seeds/main.seeder.ts create mode 100644 src/infra/datasource/typeorm/task/task.seeder.factory.ts create mode 100644 src/infra/datasource/typeorm/task/task.seeder.ts create mode 100644 src/infra/web/fastify/task/controllers/index.ts create mode 100644 src/infra/web/fastify/task/controllers/task.controller.ts create mode 100644 src/infra/web/fastify/task/controllers/types/index.ts create mode 100644 src/infra/web/fastify/task/controllers/types/request.params.id.ts create mode 100644 src/infra/web/fastify/task/index.ts create mode 100644 src/infra/web/fastify/task/schemas/index.ts create mode 100644 src/infra/web/fastify/task/schemas/insertTask.schema.ts create mode 100644 src/infra/web/fastify/task/schemas/taskId.schema.ts create mode 100644 src/infra/web/fastify/task/schemas/updateTask.schema.ts create mode 100644 src/infra/web/fastify/task/standards/index.ts create mode 100644 src/infra/web/fastify/task/standards/pagination.ts create mode 100644 src/infra/web/fastify/task/standards/standard.response.error.ts create mode 100644 src/infra/web/fastify/task/standards/standard.response.success.ts create mode 100644 src/infra/web/fastify/task/standards/standard.response.ts create mode 100644 src/tests/unit/infra/web/fastify/task/task.controller.test.ts create mode 100644 src/tests/unit/mocks/fastify/fastify.mock.ts create mode 100644 src/tests/unit/mocks/fastify/index.ts create mode 100644 src/tests/unit/mocks/task/createTask.controller.input.boundary.mock.ts create mode 100644 src/tests/unit/mocks/task/deleteTask.controller.input.boundary.mock.ts create mode 100644 src/tests/unit/mocks/task/listTaskById.controller.input.boundary.mock.ts create mode 100644 src/tests/unit/mocks/task/updateTask.controller.input.boundary.mock.ts diff --git a/src/app.ts b/src/app.ts index 940a3ff..96f1693 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1 +1,150 @@ -console.log("Hello world!"); +import "reflect-metadata"; +import Fastify from "fastify"; +import { config } from "dotenv"; +import cors from "@fastify/cors"; +import { runSeeders } from "typeorm-extension"; + +if (process.env.NODE_ENV !== "production") { + config(); +} + +import { TaskController, RequestParamsId } from "@/infra/web/fastify/task"; +import { + CreateTaskDsTypeorm, + DeleteTaskDsTypeorm, + ListTaskByIdDsTypeorm, + TaskDataMapperTypeorm, + TaskRepositoryTypeorm, + UpdateTaskDsTypeorm, + dsTypeorm, +} from "@/infra/datasource/typeorm"; +import { + CreateTaskController, + CreateTaskPresenter, + DeleteTaskController, + DeleteTaskPresenter, + ListTaskByIdController, + ListTaskByIdPresenter, + UpdateTaskController, + UpdateTaskPresenter, +} from "@/adapters/task"; +import { + CreateTaskInteractor, + DeleteTaskInteractor, + ListTaskByIdInteractor, + UpdateTaskInteractor, +} from "@/useCases/task"; + +const fastify = Fastify({ + logger: process.env.NODE_ENV !== "test" ? true : false, +}); + +fastify.register(cors); + +if (process.env.NODE_ENV !== "test") { + dsTypeorm + .initialize() + .then(async (ds) => { + console.log( + "Conexão com o banco de dados estabelecida com sucesso!" + ); + + if (process.env.NODE_ENV === "development") { + console.log("Executando seeders..."); + await runSeeders(ds); + console.log("Seeders executados com sucesso!"); + } + }) + .catch((error) => { + console.log( + `Erro ao estabelecer a conexão com o banco de dados: ${error}` + ); + process.exit(1); + }); +} + +const taskController = new TaskController( + new CreateTaskController( + new CreateTaskInteractor( + new CreateTaskDsTypeorm( + new TaskRepositoryTypeorm( + TaskDataMapperTypeorm, + dsTypeorm.createEntityManager() + ) + ), + new CreateTaskPresenter() + ) + ), + new UpdateTaskController( + new UpdateTaskInteractor( + new UpdateTaskDsTypeorm( + new TaskRepositoryTypeorm( + TaskDataMapperTypeorm, + dsTypeorm.createEntityManager() + ) + ), + new UpdateTaskPresenter() + ) + ), + new DeleteTaskController( + new DeleteTaskInteractor( + new DeleteTaskDsTypeorm( + new TaskRepositoryTypeorm( + TaskDataMapperTypeorm, + dsTypeorm.createEntityManager() + ) + ), + new DeleteTaskPresenter() + ) + ), + new ListTaskByIdController( + new ListTaskByIdInteractor( + new ListTaskByIdDsTypeorm( + new TaskRepositoryTypeorm( + TaskDataMapperTypeorm, + dsTypeorm.createEntityManager() + ) + ), + new ListTaskByIdPresenter() + ) + ) +); + +fastify.post("/tasks", async (request, reply) => { + await taskController.insert(request, reply); +}); +fastify.get<{ Params: RequestParamsId }>( + "/tasks/:id", + async (request, reply) => { + await taskController.findById(request, reply); + } +); +fastify.put<{ Params: RequestParamsId }>( + "/tasks/:id", + async (request, reply) => { + await taskController.update(request, reply); + } +); +fastify.delete<{ Params: RequestParamsId }>( + "/tasks/:id", + async (request, reply) => { + await taskController.delete(request, reply); + } +); + +if (process.env.NODE_ENV !== "test") { + const port = parseInt(process.env.PORT ?? "3000"); + fastify + .listen({ + port, + }) + .then(() => { + console.log(`Servidor iniciado na porta ${port}`); + }) + .catch((error) => { + console.log(`Erro ao iniciar o servidor: ${error}`); + process.exit(1); + }); +} + +export { fastify }; diff --git a/src/infra/datasource/typeorm/ds.typeorm.ts b/src/infra/datasource/typeorm/ds.typeorm.ts index 638fa3d..fe89b68 100644 --- a/src/infra/datasource/typeorm/ds.typeorm.ts +++ b/src/infra/datasource/typeorm/ds.typeorm.ts @@ -1,12 +1,15 @@ import { z } from "zod"; import { config } from "dotenv"; -import { DataSource } from "typeorm"; +import { DataSource, DataSourceOptions } from "typeorm"; +import { SeederOptions } from "typeorm-extension"; + +import { MainSeeder } from "./seeds/main.seeder"; if (process.env.NODE_ENV !== "production") { config(); } -const dsTypeorm = new DataSource({ +const options: DataSourceOptions & SeederOptions = { type: z .enum(["mysql", "postgres", "mongodb"]) .parse(process.env.DATABASE_TYPE), @@ -24,6 +27,10 @@ const dsTypeorm = new DataSource({ process.env.NODE_ENV === "test" ? [] : [`${__dirname}/migrations/*{.js,.ts}`], -}); + seeds: [MainSeeder], + seedTracking: false, +}; + +const dsTypeorm = new DataSource(options); export { dsTypeorm }; diff --git a/src/infra/datasource/typeorm/index.ts b/src/infra/datasource/typeorm/index.ts index df3bed7..89a9b5d 100644 --- a/src/infra/datasource/typeorm/index.ts +++ b/src/infra/datasource/typeorm/index.ts @@ -1,2 +1,3 @@ export * from "./ds.typeorm"; export * from "./task"; +export * from "./seeds"; diff --git a/src/infra/datasource/typeorm/seeds/index.ts b/src/infra/datasource/typeorm/seeds/index.ts new file mode 100644 index 0000000..1fa67f8 --- /dev/null +++ b/src/infra/datasource/typeorm/seeds/index.ts @@ -0,0 +1 @@ +export * from "./main.seeder"; diff --git a/src/infra/datasource/typeorm/seeds/main.seeder.ts b/src/infra/datasource/typeorm/seeds/main.seeder.ts new file mode 100644 index 0000000..4e7b2a4 --- /dev/null +++ b/src/infra/datasource/typeorm/seeds/main.seeder.ts @@ -0,0 +1,15 @@ +import { DataSource } from "typeorm"; +import { Seeder, SeederFactoryManager, runSeeder } from "typeorm-extension"; + +import { TaskSeeder } from "../task/task.seeder"; +import { taskSeederFactory } from "../task/task.seeder.factory"; + +class MainSeeder implements Seeder { + async run(dataSource: DataSource, _factoryManager: SeederFactoryManager) { + await runSeeder(dataSource, TaskSeeder, { + factories: [taskSeederFactory], + }); + } +} + +export { MainSeeder }; diff --git a/src/infra/datasource/typeorm/task/index.ts b/src/infra/datasource/typeorm/task/index.ts index 92011ad..2641c69 100644 --- a/src/infra/datasource/typeorm/task/index.ts +++ b/src/infra/datasource/typeorm/task/index.ts @@ -1,7 +1,8 @@ -export * from "./task.dataMapper.typeorm"; -export * from "./task.factory.typeorm"; -export * from "./task.repository.typeorm"; export * from "./createTask"; export * from "./deleteTask"; export * from "./listTaskById"; export * from "./updateTask"; +export * from "./task.dataMapper.typeorm"; +export * from "./task.factory.typeorm"; +export * from "./task.repository.typeorm"; +export * from "./task.seeder"; diff --git a/src/infra/datasource/typeorm/task/task.seeder.factory.ts b/src/infra/datasource/typeorm/task/task.seeder.factory.ts new file mode 100644 index 0000000..7018690 --- /dev/null +++ b/src/infra/datasource/typeorm/task/task.seeder.factory.ts @@ -0,0 +1,23 @@ +import { setSeederFactory } from "typeorm-extension"; + +import { TaskDataMapperTypeorm } from "./task.dataMapper.typeorm"; + +const taskSeederFactory = setSeederFactory(TaskDataMapperTypeorm, (faker) => { + const task = new TaskDataMapperTypeorm(); + task.createdAt = faker.date.past(); + task.description = faker.lorem.words(4); + task.dueDate = faker.date.future(); + task.id = faker.string.uuid(); + task.priority = faker.helpers.arrayElement([ + "low", + "medium", + "high", + "critical", + ]); + task.status = faker.helpers.arrayElement(["todo", "inProgress", "done"]); + task.title = faker.lorem.words(3); + + return task; +}); + +export { taskSeederFactory }; diff --git a/src/infra/datasource/typeorm/task/task.seeder.ts b/src/infra/datasource/typeorm/task/task.seeder.ts new file mode 100644 index 0000000..8fafbc4 --- /dev/null +++ b/src/infra/datasource/typeorm/task/task.seeder.ts @@ -0,0 +1,17 @@ +import { DataSource } from "typeorm"; +import { Seeder, SeederFactoryManager } from "typeorm-extension"; + +import { TaskDataMapperTypeorm } from "./task.dataMapper.typeorm"; + +class TaskSeeder implements Seeder { + async run(dataSource: DataSource, factoryManager: SeederFactoryManager) { + const repository = dataSource.getRepository(TaskDataMapperTypeorm); + const entities = await repository.find(); + await repository.remove(entities); + + const taskFactory = factoryManager.get(TaskDataMapperTypeorm); + await taskFactory.saveMany(10); + } +} + +export { TaskSeeder }; diff --git a/src/infra/web/fastify/task/controllers/index.ts b/src/infra/web/fastify/task/controllers/index.ts new file mode 100644 index 0000000..1c814a3 --- /dev/null +++ b/src/infra/web/fastify/task/controllers/index.ts @@ -0,0 +1,2 @@ +export * from "./task.controller"; +export * from "./types"; diff --git a/src/infra/web/fastify/task/controllers/task.controller.ts b/src/infra/web/fastify/task/controllers/task.controller.ts new file mode 100644 index 0000000..e9b1e3b --- /dev/null +++ b/src/infra/web/fastify/task/controllers/task.controller.ts @@ -0,0 +1,173 @@ +import { ZodError } from "zod"; +import { FastifyReply, FastifyRequest } from "fastify"; + +import { + StandardResponseError, + StandardResponseSuccess, + insertTaskSchema, + updateTaskSchema, + taskIdSchema, + RequestParamsId, +} from ".."; +import { + CreateTaskControllerInputBoundary, + DeleteTaskControllerInputBoundary, + ListTaskByIdControllerInputBoundary, + UpdateTaskControllerInputBoundary, +} from "@/adapters/task"; +import { + InvalidAttributeException, + ResourceNotFoundException, +} from "@/adapters/exceptions"; + +class TaskController { + private createTaskInputBoundary: CreateTaskControllerInputBoundary; + private updateTaskInputBoundary: UpdateTaskControllerInputBoundary; + private deleteTaskInputBoundary: DeleteTaskControllerInputBoundary; + private listTaskByIdInputBoundary: ListTaskByIdControllerInputBoundary; + + constructor( + createTaskInputBoundary: CreateTaskControllerInputBoundary, + updateTaskInputBoundary: UpdateTaskControllerInputBoundary, + deleteTaskInputBoundary: DeleteTaskControllerInputBoundary, + listTaskByIdInputBoundary: ListTaskByIdControllerInputBoundary + ) { + this.createTaskInputBoundary = createTaskInputBoundary; + this.updateTaskInputBoundary = updateTaskInputBoundary; + this.deleteTaskInputBoundary = deleteTaskInputBoundary; + this.listTaskByIdInputBoundary = listTaskByIdInputBoundary; + } + + async insert(request: FastifyRequest, reply: FastifyReply) { + try { + const task = await this.createTaskInputBoundary.create( + insertTaskSchema.parse(request.body) + ); + + reply.status(201).send( + new StandardResponseSuccess({ + content: task, + status: 201, + message: "Tarefa criada com sucesso", + }) + ); + } catch (error) { + const { message, status } = this.formatError(error); + reply.status(status).send( + new StandardResponseError({ + message, + status, + }) + ); + } + } + + async update( + request: FastifyRequest<{ + Params: RequestParamsId; + }>, + reply: FastifyReply + ) { + try { + const { id } = request.params; + const task = await this.updateTaskInputBoundary.update({ + ...updateTaskSchema.parse(request.body), + id: taskIdSchema.parse(id), + }); + + reply.status(200).send( + new StandardResponseSuccess({ + content: task, + status: 200, + message: "Tarefa alterada com sucesso", + }) + ); + } catch (error) { + const { message, status } = this.formatError(error); + reply.status(status).send( + new StandardResponseError({ + message, + status, + }) + ); + } + } + + async findById( + request: FastifyRequest<{ Params: RequestParamsId }>, + reply: FastifyReply + ) { + try { + const { id } = request.params; + const task = await this.listTaskByIdInputBoundary.listById( + taskIdSchema.parse(id) + ); + + reply.status(200).send( + new StandardResponseSuccess({ + content: task, + status: 200, + message: "Tarefa encontrada com sucesso", + }) + ); + } catch (error) { + const { message, status } = this.formatError(error); + reply.status(status).send( + new StandardResponseError({ + message, + status, + }) + ); + } + } + + async delete( + request: FastifyRequest<{ Params: RequestParamsId }>, + reply: FastifyReply + ) { + try { + const { id } = request.params; + await this.deleteTaskInputBoundary.delete(taskIdSchema.parse(id)); + + reply.status(204).send(); + } catch (error) { + const { message, status } = this.formatError(error); + reply.status(status).send( + new StandardResponseError({ + message, + status, + }) + ); + } + } + + private formatError(error: unknown): { message: string; status: number } { + if (error instanceof InvalidAttributeException) { + return { + message: error.message, + status: 400, + }; + } + + if (error instanceof ResourceNotFoundException) { + return { + message: error.message, + status: 404, + }; + } + + if (error instanceof ZodError) { + return { + message: error.issues[0].message, + status: 400, + }; + } + + return { + message: `Erro interno: ${JSON.stringify(error)}`, + status: 500, + }; + } +} + +export { TaskController }; diff --git a/src/infra/web/fastify/task/controllers/types/index.ts b/src/infra/web/fastify/task/controllers/types/index.ts new file mode 100644 index 0000000..791f41b --- /dev/null +++ b/src/infra/web/fastify/task/controllers/types/index.ts @@ -0,0 +1 @@ +export * from "./request.params.id"; diff --git a/src/infra/web/fastify/task/controllers/types/request.params.id.ts b/src/infra/web/fastify/task/controllers/types/request.params.id.ts new file mode 100644 index 0000000..53b86cc --- /dev/null +++ b/src/infra/web/fastify/task/controllers/types/request.params.id.ts @@ -0,0 +1,5 @@ +interface RequestParamsId { + id: string; +} + +export { RequestParamsId }; diff --git a/src/infra/web/fastify/task/index.ts b/src/infra/web/fastify/task/index.ts new file mode 100644 index 0000000..5aa66d5 --- /dev/null +++ b/src/infra/web/fastify/task/index.ts @@ -0,0 +1,3 @@ +export * from "./controllers"; +export * from "./schemas"; +export * from "./standards"; diff --git a/src/infra/web/fastify/task/schemas/index.ts b/src/infra/web/fastify/task/schemas/index.ts new file mode 100644 index 0000000..6632313 --- /dev/null +++ b/src/infra/web/fastify/task/schemas/index.ts @@ -0,0 +1,3 @@ +export * from "./insertTask.schema"; +export * from "./updateTask.schema"; +export * from "./taskId.schema"; diff --git a/src/infra/web/fastify/task/schemas/insertTask.schema.ts b/src/infra/web/fastify/task/schemas/insertTask.schema.ts new file mode 100644 index 0000000..9cc023c --- /dev/null +++ b/src/infra/web/fastify/task/schemas/insertTask.schema.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; + +const insertTaskSchema = z.object({ + title: z.string({ required_error: "Título é obrigatório" }), + description: z.string({ required_error: "Descrição é obrigatória" }), + status: z.enum(["todo", "inProgress", "done"], { + message: "Status deve ser 'todo', 'inProgress' ou 'done'", + }), + priority: z.enum(["low", "medium", "high"], { + message: "Prioridade deve ser 'low', 'medium' ou 'high'", + }), + dueDate: z + .string({ message: "Data de vencimento é obrigatória" }) + .datetime("Data deve ser no formato YYYY-MM-DDTHH:MM:SSZ") + .transform((date) => new Date(date)), +}); + +export { insertTaskSchema }; diff --git a/src/infra/web/fastify/task/schemas/taskId.schema.ts b/src/infra/web/fastify/task/schemas/taskId.schema.ts new file mode 100644 index 0000000..f831229 --- /dev/null +++ b/src/infra/web/fastify/task/schemas/taskId.schema.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +const taskIdSchema = z + .string({ required_error: "Id da tarefa é obrigatório" }) + .uuid({ message: "Id da tarefa deve ser um UUID válido" }); + +export { taskIdSchema }; diff --git a/src/infra/web/fastify/task/schemas/updateTask.schema.ts b/src/infra/web/fastify/task/schemas/updateTask.schema.ts new file mode 100644 index 0000000..2cd6a7f --- /dev/null +++ b/src/infra/web/fastify/task/schemas/updateTask.schema.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; + +const updateTaskSchema = z.object({ + title: z.string({ required_error: "Título é obrigatório" }), + description: z.string({ required_error: "Descrição é obrigatória" }), + status: z.enum(["todo", "inProgress", "done"], { + message: "Status deve ser 'todo', 'inProgress' ou 'done'", + }), + priority: z.enum(["low", "medium", "high"], { + message: "Prioridade deve ser 'low', 'medium' ou 'high'", + }), + dueDate: z + .string({ message: "Data de vencimento é obrigatória" }) + .datetime("Data deve ser no formato YYYY-MM-DDTHH:MM:SSZ") + .transform((date) => new Date(date)), +}); + +export { updateTaskSchema }; diff --git a/src/infra/web/fastify/task/standards/index.ts b/src/infra/web/fastify/task/standards/index.ts new file mode 100644 index 0000000..8c2b7f1 --- /dev/null +++ b/src/infra/web/fastify/task/standards/index.ts @@ -0,0 +1,4 @@ +export * from "./pagination"; +export * from "./standard.response"; +export * from "./standard.response.success"; +export * from "./standard.response.error"; diff --git a/src/infra/web/fastify/task/standards/pagination.ts b/src/infra/web/fastify/task/standards/pagination.ts new file mode 100644 index 0000000..e0721dd --- /dev/null +++ b/src/infra/web/fastify/task/standards/pagination.ts @@ -0,0 +1,24 @@ +class Pagination { + currentPage: number; + nextPage: number | null; + previousPage: number | null; + totalCount: number; + totalPages: number; + + constructor(params: { + totalCount: number; + currentPage: number; + pageSize: number; + }) { + const { totalCount, currentPage, pageSize } = params; + + this.currentPage = currentPage; + this.totalPages = Math.ceil(totalCount / pageSize); + this.nextPage = + currentPage === this.totalPages ? null : currentPage + 1; + this.previousPage = currentPage === 1 ? null : currentPage - 1; + this.totalCount = totalCount; + } +} + +export { Pagination }; diff --git a/src/infra/web/fastify/task/standards/standard.response.error.ts b/src/infra/web/fastify/task/standards/standard.response.error.ts new file mode 100644 index 0000000..d09b67d --- /dev/null +++ b/src/infra/web/fastify/task/standards/standard.response.error.ts @@ -0,0 +1,13 @@ +import { StandardResponse } from "."; + +class StandardResponseError extends StandardResponse { + constructor(params: { + status: number; + message: string; + content?: unknown; + }) { + super({ ...params, error: true, content: params.content || null }); + } +} + +export { StandardResponseError }; diff --git a/src/infra/web/fastify/task/standards/standard.response.success.ts b/src/infra/web/fastify/task/standards/standard.response.success.ts new file mode 100644 index 0000000..a42f3b2 --- /dev/null +++ b/src/infra/web/fastify/task/standards/standard.response.success.ts @@ -0,0 +1,14 @@ +import { Pagination, StandardResponse } from "."; + +class StandardResponseSuccess extends StandardResponse { + constructor(params: { + status: number; + message: string; + content: unknown; + pagination?: Pagination; + }) { + super({ ...params, error: false }); + } +} + +export { StandardResponseSuccess }; diff --git a/src/infra/web/fastify/task/standards/standard.response.ts b/src/infra/web/fastify/task/standards/standard.response.ts new file mode 100644 index 0000000..f4a85fd --- /dev/null +++ b/src/infra/web/fastify/task/standards/standard.response.ts @@ -0,0 +1,27 @@ +import { Pagination } from "."; + +class StandardResponse { + timestamp: Date; + status: number; + error: boolean; + message: string; + content: unknown; + pagination?: Pagination; + + constructor(params: { + status: number; + error: boolean; + message: string; + content: unknown; + pagination?: Pagination; + }) { + this.timestamp = new Date(); + this.status = params.status; + this.error = params.error; + this.message = params.message; + this.content = params.content; + this.pagination = params.pagination; + } +} + +export { StandardResponse }; diff --git a/src/tests/unit/infra/web/fastify/task/task.controller.test.ts b/src/tests/unit/infra/web/fastify/task/task.controller.test.ts new file mode 100644 index 0000000..56d3dd0 --- /dev/null +++ b/src/tests/unit/infra/web/fastify/task/task.controller.test.ts @@ -0,0 +1,840 @@ +import { v4 as uuidv4 } from "uuid"; +import { FastifyRequest } from "fastify"; + +import { + createTaskControllerInputBoundaryMock, + deleteTaskControllerInputBoundaryMock, + listTaskByIdControllerInputBoundaryMock, + updateTaskControllerInputBoundaryMock, +} from "@/tests/unit/mocks/task"; +import { + fastifyReplyMock, + fastifyRequestMock, +} from "@/tests/unit/mocks/fastify"; + +import { + RequestParamsId, + StandardResponseError, + StandardResponseSuccess, + TaskController, +} from "@/infra/web/fastify/task"; +import { + CreateTaskResponseModel, + UpdateTaskResponseModel, + ListTaskByIdResponseModel, +} from "@/useCases/task"; +import { + InvalidAttributeException, + ResourceNotFoundException, +} from "@/adapters/exceptions"; + +describe("TaskController", () => { + let controller: TaskController; + + const dateMock = new Date("2022-01-01T00:00:00.000Z"); + + beforeEach(() => { + jest.clearAllMocks(); + + jest.useFakeTimers().setSystemTime(dateMock); + + controller = new TaskController( + createTaskControllerInputBoundaryMock, + updateTaskControllerInputBoundaryMock, + deleteTaskControllerInputBoundaryMock, + listTaskByIdControllerInputBoundaryMock + ); + }); + + describe("insert", () => { + let responseModel: CreateTaskResponseModel; + + beforeEach(() => { + responseModel = new CreateTaskResponseModel({ + description: "Task 1 description", + dueDate: new Date("2022-01-01T00:00:00.000Z"), + id: "1", + title: "Task 1", + priority: "low", + status: "todo", + }); + + createTaskControllerInputBoundaryMock.create.mockResolvedValue( + responseModel + ); + }); + + it("should return 201 and created task", async () => { + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(201); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseSuccess({ + content: responseModel, + status: 201, + message: "Tarefa criada com sucesso", + }) + ); + }); + + it("should return 400 when invalid attribute exception creating task", async () => { + createTaskControllerInputBoundaryMock.create.mockRejectedValue( + new InvalidAttributeException("Invalid attribute") + ); + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Invalid attribute", + status: 400, + }) + ); + }); + + it("should return 500 when unexpected exception creating task", async () => { + const expectedError = new Error("Unexpected error"); + createTaskControllerInputBoundaryMock.create.mockRejectedValue( + expectedError + ); + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(500); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: `Erro interno: ${JSON.stringify(expectedError)}`, + status: 500, + }) + ); + }); + + it("should return 400 when title is not given", async () => { + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Título é obrigatório", + status: 400, + }) + ); + }); + + it("should return 400 when description is not given", async () => { + fastifyRequestMock.body = { + title: responseModel.title, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Descrição é obrigatória", + status: 400, + }) + ); + }); + + it("should return 400 when dueDate is not given", async () => { + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Data de vencimento é obrigatória", + status: 400, + }) + ); + }); + + it("should return 400 when date is in correct format", async () => { + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: "invalid-date", + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Data deve ser no formato YYYY-MM-DDTHH:MM:SSZ", + status: 400, + }) + ); + }); + + it("should return 400 when priority is not given", async () => { + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + status: responseModel.status, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Prioridade deve ser 'low', 'medium' ou 'high'", + status: 400, + }) + ); + }); + + it("should return 400 when status is not given", async () => { + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + }; + + await controller.insert(fastifyRequestMock, fastifyReplyMock); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Status deve ser 'todo', 'inProgress' ou 'done'", + status: 400, + }) + ); + }); + }); + + describe("update", () => { + let responseModel: UpdateTaskResponseModel; + + beforeEach(() => { + responseModel = new UpdateTaskResponseModel({ + description: "Task 1 description", + dueDate: new Date("2022-01-01T00:00:00.000Z"), + id: "1", + title: "Task 1", + priority: "low", + status: "todo", + }); + + updateTaskControllerInputBoundaryMock.update.mockResolvedValue( + responseModel + ); + }); + + it("should return 200 and updated task", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(200); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseSuccess({ + content: responseModel, + status: 200, + message: "Tarefa alterada com sucesso", + }) + ); + }); + + it("should return 400 when invalid attribute exception updating task", async () => { + updateTaskControllerInputBoundaryMock.update.mockRejectedValue( + new InvalidAttributeException("Invalid attribute") + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Invalid attribute", + status: 400, + }) + ); + }); + + it("should return 404 when resource not found exception updating task", async () => { + updateTaskControllerInputBoundaryMock.update.mockRejectedValue( + new ResourceNotFoundException("Resource not found") + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(404); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Resource not found", + status: 404, + }) + ); + }); + + it("should return 500 when unexpected exception updating task", async () => { + const expectedError = new Error("Unexpected error"); + updateTaskControllerInputBoundaryMock.update.mockRejectedValue( + expectedError + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(500); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: `Erro interno: ${JSON.stringify(expectedError)}`, + status: 500, + }) + ); + }); + + it("should return 400 when id is not given", async () => { + fastifyRequestMock.params = {}; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa é obrigatório", + status: 400, + }) + ); + }); + + it("should return 400 when id is not UUID", async () => { + fastifyRequestMock.params = { + id: "invalid-id", + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + title: responseModel.title, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa deve ser um UUID válido", + status: 400, + }) + ); + }); + + it("should return 400 when title is not given", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Título é obrigatório", + status: 400, + }) + ); + }); + + it("should return 400 when description is not given", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + title: responseModel.title, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Descrição é obrigatória", + status: 400, + }) + ); + }); + + it("should return 400 when dueDate is not given", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Data de vencimento é obrigatória", + status: 400, + }) + ); + }); + + it("should return 400 when dueDate is invalid", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: "invalid-date", + priority: responseModel.priority, + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Data deve ser no formato YYYY-MM-DDTHH:MM:SSZ", + status: 400, + }) + ); + }); + + it("should return 400 when priority is not given", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + status: responseModel.status, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Prioridade deve ser 'low', 'medium' ou 'high'", + status: 400, + }) + ); + }); + + it("should return 400 when status is not given", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + fastifyRequestMock.body = { + title: responseModel.title, + description: responseModel.description, + dueDate: responseModel.dueDate.toISOString(), + priority: responseModel.priority, + }; + + await controller.update( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Status deve ser 'todo', 'inProgress' ou 'done'", + status: 400, + }) + ); + }); + }); + + describe("findById", () => { + let responseModel: ListTaskByIdResponseModel; + + beforeEach(() => { + responseModel = new ListTaskByIdResponseModel({ + description: "Task 1 description", + dueDate: new Date("2022-01-01T00:00:00.000Z"), + id: "1", + title: "Task 1", + priority: "low", + status: "todo", + }); + + listTaskByIdControllerInputBoundaryMock.listById.mockResolvedValue( + responseModel + ); + }); + + it("should return 200 and found task", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.findById( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(200); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseSuccess({ + content: responseModel, + status: 200, + message: "Tarefa encontrada com sucesso", + }) + ); + }); + + it("should return 404 when resource not found exception listing task", async () => { + listTaskByIdControllerInputBoundaryMock.listById.mockRejectedValue( + new ResourceNotFoundException("Resource not found") + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.findById( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(404); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Resource not found", + status: 404, + }) + ); + }); + + // 500 + + it("should return 500 when unexpected exception listing task", async () => { + const expectedError = new Error("Unexpected error"); + listTaskByIdControllerInputBoundaryMock.listById.mockRejectedValue( + expectedError + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.findById( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(500); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: `Erro interno: ${JSON.stringify(expectedError)}`, + status: 500, + }) + ); + }); + + it("should return 400 when id is not UUID", async () => { + fastifyRequestMock.params = { + id: "invalid-id", + }; + + await controller.findById( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa deve ser um UUID válido", + status: 400, + }) + ); + }); + + it("should return 400 when id is not given", async () => { + fastifyRequestMock.params = {}; + + await controller.findById( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa é obrigatório", + status: 400, + }) + ); + }); + }); + + describe("delete", () => { + beforeEach(() => { + deleteTaskControllerInputBoundaryMock.delete.mockResolvedValue( + undefined + ); + }); + + it("should return 204", async () => { + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.delete( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(204); + expect(fastifyReplyMock.send).toHaveBeenCalled(); + }); + + it("should return 404 when resource not found exception deleting task", async () => { + deleteTaskControllerInputBoundaryMock.delete.mockRejectedValue( + new ResourceNotFoundException("Resource not found") + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.delete( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(404); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Resource not found", + status: 404, + }) + ); + }); + + it("should return 500 when unexpected exception deleting task", async () => { + const expectedError = new Error("Unexpected error"); + deleteTaskControllerInputBoundaryMock.delete.mockRejectedValue( + expectedError + ); + fastifyRequestMock.params = { + id: uuidv4(), + }; + + await controller.delete( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(500); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: `Erro interno: ${JSON.stringify(expectedError)}`, + status: 500, + }) + ); + }); + + it("should return 400 when id is not UUID", async () => { + fastifyRequestMock.params = { + id: "invalid-id", + }; + + await controller.delete( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa deve ser um UUID válido", + status: 400, + }) + ); + }); + + it("should return 400 when id is not given", async () => { + fastifyRequestMock.params = {}; + + await controller.delete( + fastifyRequestMock as FastifyRequest<{ + Params: RequestParamsId; + }>, + fastifyReplyMock + ); + + expect(fastifyReplyMock.status).toHaveBeenCalledWith(400); + expect(fastifyReplyMock.send).toHaveBeenCalledWith( + new StandardResponseError({ + message: "Id da tarefa é obrigatório", + status: 400, + }) + ); + }); + }); +}); diff --git a/src/tests/unit/mocks/fastify/fastify.mock.ts b/src/tests/unit/mocks/fastify/fastify.mock.ts new file mode 100644 index 0000000..84ceea3 --- /dev/null +++ b/src/tests/unit/mocks/fastify/fastify.mock.ts @@ -0,0 +1,8 @@ +import { mock, mockDeep } from "jest-mock-extended"; +import { FastifyReply, FastifyRequest } from "fastify"; + +const fastifyRequestMock = mock(); +const fastifyReplyMock = mockDeep(); +fastifyReplyMock.status.mockReturnValue(fastifyReplyMock); + +export { fastifyRequestMock, fastifyReplyMock }; diff --git a/src/tests/unit/mocks/fastify/index.ts b/src/tests/unit/mocks/fastify/index.ts new file mode 100644 index 0000000..78ab9da --- /dev/null +++ b/src/tests/unit/mocks/fastify/index.ts @@ -0,0 +1 @@ +export * from "./fastify.mock"; diff --git a/src/tests/unit/mocks/task/createTask.controller.input.boundary.mock.ts b/src/tests/unit/mocks/task/createTask.controller.input.boundary.mock.ts new file mode 100644 index 0000000..e038fd9 --- /dev/null +++ b/src/tests/unit/mocks/task/createTask.controller.input.boundary.mock.ts @@ -0,0 +1,5 @@ +const createTaskControllerInputBoundaryMock = { + create: jest.fn(), +}; + +export { createTaskControllerInputBoundaryMock }; diff --git a/src/tests/unit/mocks/task/deleteTask.controller.input.boundary.mock.ts b/src/tests/unit/mocks/task/deleteTask.controller.input.boundary.mock.ts new file mode 100644 index 0000000..6c0286b --- /dev/null +++ b/src/tests/unit/mocks/task/deleteTask.controller.input.boundary.mock.ts @@ -0,0 +1,5 @@ +const deleteTaskControllerInputBoundaryMock = { + delete: jest.fn(), +}; + +export { deleteTaskControllerInputBoundaryMock }; diff --git a/src/tests/unit/mocks/task/index.ts b/src/tests/unit/mocks/task/index.ts index 9f3f474..e58638d 100644 --- a/src/tests/unit/mocks/task/index.ts +++ b/src/tests/unit/mocks/task/index.ts @@ -11,3 +11,7 @@ export * from "./deleteTask.ds.gateway.mock"; export * from "./deleteTask.output.boundary.mock"; export * from "./deleteTask.input.boundary.mock"; export * from "./task.repository.typeorm.mock"; +export * from "./createTask.controller.input.boundary.mock"; +export * from "./deleteTask.controller.input.boundary.mock"; +export * from "./listTaskById.controller.input.boundary.mock"; +export * from "./updateTask.controller.input.boundary.mock"; diff --git a/src/tests/unit/mocks/task/listTaskById.controller.input.boundary.mock.ts b/src/tests/unit/mocks/task/listTaskById.controller.input.boundary.mock.ts new file mode 100644 index 0000000..737909f --- /dev/null +++ b/src/tests/unit/mocks/task/listTaskById.controller.input.boundary.mock.ts @@ -0,0 +1,5 @@ +const listTaskByIdControllerInputBoundaryMock = { + listById: jest.fn(), +}; + +export { listTaskByIdControllerInputBoundaryMock }; diff --git a/src/tests/unit/mocks/task/updateTask.controller.input.boundary.mock.ts b/src/tests/unit/mocks/task/updateTask.controller.input.boundary.mock.ts new file mode 100644 index 0000000..a525f9c --- /dev/null +++ b/src/tests/unit/mocks/task/updateTask.controller.input.boundary.mock.ts @@ -0,0 +1,5 @@ +const updateTaskControllerInputBoundaryMock = { + update: jest.fn(), +}; + +export { updateTaskControllerInputBoundaryMock };