From 6e19de20bd841cfcfb0ba1fae9854b124fda84cd Mon Sep 17 00:00:00 2001 From: devhebert Date: Mon, 29 Apr 2024 21:11:01 -0300 Subject: [PATCH] =?UTF-8?q?Cria=20solu=C3=A7=C3=A3o=20para=20o=20desafio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../challenge/modules/task/dtos/TaskDTO.java | 6 +- .../modules/task/dtos/TaskProgressDTO.java | 6 +- .../http/controllers/TaskController.java | 111 +++++++++++++----- .../controllers/TaskProgressController.java | 35 ++++-- .../modules/task/messages/ErrorMessages.java | 13 ++ .../challenge/modules/task/models/Task.java | 22 ++-- .../task/repositories/ITaskRepository.java | 6 +- .../task/repositories/TaskRepository.java | 42 ++++--- .../task/services/CreateTaskService.java | 22 +++- .../task/services/DeleteTaskService.java | 22 +++- .../task/services/ICreateTaskService.java | 35 +++++- .../task/services/IDeleteTaskService.java | 45 ++++++- .../services/IRetrieveAllTasksService.java | 23 +++- .../services/IRetrieveTaskByIdService.java | 43 ++++++- .../services/IUpdateTaskProgressService.java | 41 ++++++- .../task/services/IUpdateTaskService.java | 46 +++++++- .../services/RetrieveAllTasksService.java | 10 +- .../services/RetrieveTaskByIdService.java | 42 +++++++ .../services/UpdateTaskProgressService.java | 67 +++++++++++ .../task/services/UpdateTaskService.java | 49 ++++++++ tests/Runner.java | 3 - .../task/services/CreateTaskServiceTest.java | 51 +++++++- .../task/services/DeleteTaskServiceTest.java | 43 ++++++- .../services/RetrieveAllTasksServiceTest.java | 40 +++++-- .../services/RetrieveTaskByIdServiceTest.java | 82 +++++++++++-- .../UpdateTaskProgressServiceTest.java | 104 ++++++++++++++-- .../task/services/UpdateTaskServiceTest.java | 91 +++++++++++--- 27 files changed, 962 insertions(+), 138 deletions(-) create mode 100644 source/backend/challenge/modules/task/messages/ErrorMessages.java create mode 100644 source/backend/challenge/modules/task/services/RetrieveTaskByIdService.java create mode 100644 source/backend/challenge/modules/task/services/UpdateTaskProgressService.java create mode 100644 source/backend/challenge/modules/task/services/UpdateTaskService.java diff --git a/source/backend/challenge/modules/task/dtos/TaskDTO.java b/source/backend/challenge/modules/task/dtos/TaskDTO.java index d73cf02..03767f9 100644 --- a/source/backend/challenge/modules/task/dtos/TaskDTO.java +++ b/source/backend/challenge/modules/task/dtos/TaskDTO.java @@ -1,19 +1,17 @@ package backend.challenge.modules.task.dtos; -import backend.challenge.modules.task.enums.TaskStatus; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; -import java.util.Date; - @Getter @Setter @Accessors(chain = true) @NoArgsConstructor(staticName = "create") +@AllArgsConstructor public class TaskDTO { - private String title; private String description; diff --git a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java index ee6277f..d51e181 100644 --- a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java +++ b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java @@ -1,17 +1,21 @@ package backend.challenge.modules.task.dtos; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import java.util.UUID; + @Getter @Setter @Accessors(chain = true) @NoArgsConstructor(staticName = "create") +@AllArgsConstructor public class TaskProgressDTO { - private Long id; + private UUID id; private int progress; } diff --git a/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java b/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java index 6a06a52..936229f 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java @@ -1,5 +1,6 @@ package backend.challenge.modules.task.infra.http.controllers; +import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.infra.http.views.TaskView; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.services.*; @@ -7,11 +8,11 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.util.UUID; @Singleton @Path("tasks") public class TaskController { - private final ICreateTaskService createTaskService; private final IDeleteTaskService deleteTaskService; private final IRetrieveAllTasksService retrieveAllTasksService; @@ -20,56 +21,112 @@ public class TaskController { @Inject public TaskController( - final ICreateTaskService createTaskService, - final IDeleteTaskService deleteTaskService, - final IRetrieveAllTasksService retrieveAllTasksService + final ICreateTaskService createTaskService, + final IDeleteTaskService deleteTaskService, + final IRetrieveAllTasksService retrieveAllTasksService, + final IRetrieveTaskByIdService retrieveTaskByIdService, + final IUpdateTaskService updateTaskService ) { this.createTaskService = createTaskService; this.deleteTaskService = deleteTaskService; this.retrieveAllTasksService = retrieveAllTasksService; - this.retrieveTaskByIdService = null; - this.updateTaskService = null; + this.retrieveTaskByIdService = retrieveTaskByIdService; + this.updateTaskService = updateTaskService; } @GET public Response show() { - // TODO: Rota que lista todas as tarefas - - return DefaultResponse.ok().entity("Hello world"); + IRetrieveAllTasksService.OutputPort outputPort = this.retrieveAllTasksService.execute(); + return DefaultResponse.ok().entity(outputPort); } @GET @Path("single/{taskId}") - public Response index(@PathParam("taskId") Long taskId) { - // TODO: A rota deve retornar somente a tarefa a qual o id corresponder - - return DefaultResponse.ok().entity("Hello world"); + public Response index(@PathParam("taskId") String taskId) { + UUID id = UUID.fromString(taskId); + IRetrieveTaskByIdService.OutputPort outputPort = this.retrieveTaskByIdService.execute(new IRetrieveTaskByIdService.InputPort(id)); + + if (outputPort instanceof IRetrieveTaskByIdService.OutputPort.Ok) { + IRetrieveTaskByIdService.OutputPort.Ok ok = (IRetrieveTaskByIdService.OutputPort.Ok) outputPort; + return DefaultResponse.ok().entity(ok); + } + + if (outputPort instanceof IRetrieveTaskByIdService.OutputPort.NotFound) { + IRetrieveTaskByIdService.OutputPort.NotFound notFound = (IRetrieveTaskByIdService.OutputPort.NotFound) outputPort; + return DefaultResponse.notFound().entity(notFound); + } + + if (outputPort instanceof IRetrieveTaskByIdService.OutputPort.Error) { + IRetrieveTaskByIdService.OutputPort.Error error = (IRetrieveTaskByIdService.OutputPort.Error) outputPort; + return DefaultResponse.badRequest().entity(error); + } + + return DefaultResponse.serverError().entity("An unexpected error occurred"); } @POST public Response create(TaskView task) { - // TODO: A rota deve receber title e description, sendo o `title` o título da tarefa e `description` uma descrição da tarefa. + TaskDTO inputPort = new TaskDTO(task.getTitle(), task.getDescription()); + ICreateTaskService.OutputPort outputPort = this.createTaskService.execute(new ICreateTaskService.InputPort(inputPort)); + + if (outputPort instanceof ICreateTaskService.OutputPort.Ok) { + ICreateTaskService.OutputPort.Ok ok = (ICreateTaskService.OutputPort.Ok) outputPort; + return DefaultResponse.created().entity(ok); + } - return DefaultResponse.ok().entity("Hello world"); + if (outputPort instanceof ICreateTaskService.OutputPort.Error) { + ICreateTaskService.OutputPort.Error error = (ICreateTaskService.OutputPort.Error) outputPort; + return DefaultResponse.badRequest().entity(error); + } + + return DefaultResponse.serverError().entity("An unexpected error occurred"); } @PUT @Path("single/{taskId}") - public Response update(@PathParam("taskId") Long taskId, Task task) { - /* - TODO: A rota deve alterar apenas o title e description da tarefa - que possua o id igual ao id correspondente nos parâmetros da rota. - */ - - return DefaultResponse.ok().entity("Hello world"); + public Response update(@PathParam("taskId") String taskId, Task task) { + UUID id = UUID.fromString(taskId); + TaskDTO inputPort = new TaskDTO(task.getTitle(), task.getDescription()); + IUpdateTaskService.OutputPort outputPort = this.updateTaskService.execute(new IUpdateTaskService.InputPort(id, inputPort)); + + if (outputPort instanceof IUpdateTaskService.OutputPort.Ok) { + return DefaultResponse.noContent(); + } + + if (outputPort instanceof IUpdateTaskService.OutputPort.NotFound) { + IUpdateTaskService.OutputPort.NotFound notFound = (IUpdateTaskService.OutputPort.NotFound) outputPort; + return DefaultResponse.notFound().entity(notFound); + } + + if (outputPort instanceof IUpdateTaskService.OutputPort.Error) { + IUpdateTaskService.OutputPort.Error error = (IUpdateTaskService.OutputPort.Error) outputPort; + return DefaultResponse.badRequest().entity(error); + } + + return DefaultResponse.serverError().entity("An unexpected error occurred"); } @DELETE @Path("single/{taskId}") - public Response delete(@PathParam("taskId") Long taskId) { - // TODO: A rota deve deletar a tarefa com o id correspondente nos parâmetros da rota - - return DefaultResponse.ok().entity("Hello world"); + public Response delete(@PathParam("taskId") String taskId) { + UUID id = UUID.fromString(taskId); + IDeleteTaskService.OutputPort outputPort = this.deleteTaskService.execute(new IDeleteTaskService.InputPort(id)); + + if (outputPort instanceof IDeleteTaskService.OutputPort.Ok) { + IDeleteTaskService.OutputPort.Ok ok = (IDeleteTaskService.OutputPort.Ok) outputPort; + return DefaultResponse.noContent(); + } + + if (outputPort instanceof IDeleteTaskService.OutputPort.NotFound) { + IDeleteTaskService.OutputPort.NotFound notFound = (IDeleteTaskService.OutputPort.NotFound) outputPort; + return DefaultResponse.notFound().entity(notFound); + } + + if (outputPort instanceof IDeleteTaskService.OutputPort.Error) { + IDeleteTaskService.OutputPort.Error error = (IDeleteTaskService.OutputPort.Error) outputPort; + return DefaultResponse.badRequest().entity(error); + } + + return DefaultResponse.serverError().entity("An unexpected error occurred"); } - } diff --git a/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java b/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java index 4a25520..4b3288a 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java @@ -1,17 +1,17 @@ package backend.challenge.modules.task.infra.http.controllers; +import backend.challenge.modules.task.dtos.TaskProgressDTO; import backend.challenge.modules.task.infra.http.views.TaskProgressView; -import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.services.*; import kikaha.urouting.api.*; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.UUID; @Singleton @Path("tasks/progress") public class TaskProgressController { - private final IUpdateTaskProgressService updateTaskProgressService; @Inject @@ -21,15 +21,28 @@ public TaskProgressController(final IUpdateTaskProgressService updateTaskProgres @PUT @Path("single/{taskId}") - public Response updateProgress(@PathParam("taskId") Long taskId, TaskProgressView taskProgressView) { - /* - TODO: A rota deve alterar apenas o progresso da tarefa que possua o id igual ao id correspondente - nos parâmetros da rota. - O `progress` pode ter o valor máximo de 100, e quando ele atingi o máximo, - o `status` deve ser alterado para `COMPLETE` - */ - - return DefaultResponse.ok().entity("Hello world"); + public Response updateProgress(@PathParam("taskId") String taskId, TaskProgressView taskProgressView) { + UUID id = UUID.fromString(taskId); + TaskProgressDTO taskProgressDTO = new TaskProgressDTO(id, taskProgressView.getProgress()); + IUpdateTaskProgressService.InputPort inputPort = new IUpdateTaskProgressService.InputPort(taskProgressDTO); + + IUpdateTaskProgressService.OutputPort outputPort = this.updateTaskProgressService.execute(inputPort); + + if (outputPort instanceof IUpdateTaskProgressService.OutputPort.Ok) { + return DefaultResponse.noContent(); + } + + if (outputPort instanceof IUpdateTaskProgressService.OutputPort.NotFound) { + IUpdateTaskProgressService.OutputPort.NotFound notFound = (IUpdateTaskProgressService.OutputPort.NotFound) outputPort; + return DefaultResponse.notFound().entity(notFound); + } + + if (outputPort instanceof IUpdateTaskProgressService.OutputPort.Error) { + IUpdateTaskProgressService.OutputPort.Error error = (IUpdateTaskProgressService.OutputPort.Error) outputPort; + return DefaultResponse.badRequest().entity(error); + } + + return DefaultResponse.serverError().entity("An unexpected error occurred"); } } diff --git a/source/backend/challenge/modules/task/messages/ErrorMessages.java b/source/backend/challenge/modules/task/messages/ErrorMessages.java new file mode 100644 index 0000000..a24285a --- /dev/null +++ b/source/backend/challenge/modules/task/messages/ErrorMessages.java @@ -0,0 +1,13 @@ +package backend.challenge.modules.task.messages; + +public class ErrorMessages { + public static final String TASK_IS_REQUIRED = "Task is required"; + public static final String TITLE_IS_REQUIRED = "Title is required"; + public static final String TASK_ID_IS_REQUIRED = "Task ID is required"; + public static final String TASK_NOT_FOUND = "Task not found"; + public static final String INVALID_INPUT = "Invalid input"; + public static final String TASK_PROGRESS_IS_REQUIRED = "Task progress is required"; + public static final String TASK_PROGRESS_MUST_BE_GREATER_THAN_OR_EQUAL_TO_0 = "Task progress must be greater than or equal to 0"; + public static final String TASK_PROGRESS_MUST_BE_LESS_THAN_OR_EQUAL_TO_100 = "Task progress must be less than or equal to 100"; + public static final String TASK_IS_ALREADY_COMPLETE = "Task is already complete"; +} \ No newline at end of file diff --git a/source/backend/challenge/modules/task/models/Task.java b/source/backend/challenge/modules/task/models/Task.java index 123f10c..f5e3d53 100644 --- a/source/backend/challenge/modules/task/models/Task.java +++ b/source/backend/challenge/modules/task/models/Task.java @@ -1,23 +1,25 @@ package backend.challenge.modules.task.models; import backend.challenge.modules.task.enums.TaskStatus; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.experimental.Accessors; +import lombok.*; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; +import java.util.UUID; + +import static backend.challenge.modules.task.enums.TaskStatus.PROGRESS; @Getter @Setter +@NoArgsConstructor +@AllArgsConstructor public class Task { - - private Long id; + private UUID id = UUID.randomUUID(); private String title; private String description; - private int progress; - private TaskStatus status; - private Date createdAt; + private int progress = 0; + private TaskStatus status = PROGRESS; + private Date createdAt = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()); } diff --git a/source/backend/challenge/modules/task/repositories/ITaskRepository.java b/source/backend/challenge/modules/task/repositories/ITaskRepository.java index 527450a..abfe710 100644 --- a/source/backend/challenge/modules/task/repositories/ITaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/ITaskRepository.java @@ -4,13 +4,13 @@ import backend.challenge.modules.task.models.Task; import java.util.List; +import java.util.UUID; public interface ITaskRepository { - Task index(Long taskId); + Task index(UUID taskId); List show(); Task create(TaskDTO taskDTO); Task update(Task task); - void delete(Long taskId); - + void delete(UUID taskId); } diff --git a/source/backend/challenge/modules/task/repositories/TaskRepository.java b/source/backend/challenge/modules/task/repositories/TaskRepository.java index c47c8fd..39ff0f3 100644 --- a/source/backend/challenge/modules/task/repositories/TaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/TaskRepository.java @@ -4,43 +4,53 @@ import backend.challenge.modules.task.models.Task; import javax.inject.Singleton; +import java.util.ArrayList; import java.util.List; +import java.util.UUID; @Singleton public class TaskRepository implements ITaskRepository { + private final List tasks = new ArrayList<>(); @Override - public Task index(final Long taskId) { - // TODO: Criar método responsável por retornar tarefa por id - - return null; + public Task index(final UUID taskId) { + return this.tasks.stream() + .filter(t -> t.getId().equals(taskId)) + .findFirst() + .orElse(null); } @Override public List show() { - // TODO: Criar método responsável por retornar todas as tarefas - - return null; + return new ArrayList<>(this.tasks); } @Override public Task create(final TaskDTO taskDTO) { - // TODO: Criar método responsável por criar uma tarefa + Task newTask = new Task(); + newTask.setTitle(taskDTO.getTitle()); + newTask.setDescription(taskDTO.getDescription()); + + this.tasks.add(newTask); - return null; + return newTask; } @Override public Task update(final Task task) { - // TODO: Criar método responsável por atualizar uma tarefa - - return null; + tasks.stream() + .filter(existingTask -> existingTask.getId().equals(task.getId())) + .findFirst() + .ifPresent(existingTask -> { + existingTask.setTitle(task.getTitle()); + existingTask.setDescription(task.getDescription()); + }); + + return task; } @Override - public void delete(final Long taskId) { - // TODO: Criar método responsável por deletar tarefa por id - + public void delete(UUID taskId) { + this.tasks.removeIf(task -> task.getId().equals(taskId)); } - } diff --git a/source/backend/challenge/modules/task/services/CreateTaskService.java b/source/backend/challenge/modules/task/services/CreateTaskService.java index 46b144b..f90dd5a 100644 --- a/source/backend/challenge/modules/task/services/CreateTaskService.java +++ b/source/backend/challenge/modules/task/services/CreateTaskService.java @@ -1,6 +1,7 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.messages.ErrorMessages; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; @@ -18,8 +19,25 @@ public CreateTaskService(final ITaskRepository taskRepository) { } @Override - public Task execute(TaskDTO taskDTO) { - // TODO: Criar serviço responsável por criar uma tarefa + public OutputPort execute(InputPort inputPort) { + try { + if (inputPort.getTaskDTO() == null) return new OutputPort.Error(ErrorMessages.TASK_IS_REQUIRED); + + OutputPort validateInput = this.isValidInput(inputPort); + if (validateInput instanceof OutputPort.Error) return validateInput; + + Task createdTask = this.taskRepository.create(inputPort.getTaskDTO()); + + return new OutputPort.Ok(createdTask); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } + } + + private OutputPort isValidInput(InputPort inputPort) { + if (inputPort.getTaskDTO().getTitle() == null || inputPort.getTaskDTO().getTitle().isEmpty() || inputPort.getTaskDTO().getTitle().isBlank() || inputPort.getTaskDTO().getTitle().length() < 4) { + return new OutputPort.Error(ErrorMessages.TITLE_IS_REQUIRED); + } return null; } diff --git a/source/backend/challenge/modules/task/services/DeleteTaskService.java b/source/backend/challenge/modules/task/services/DeleteTaskService.java index c957d70..478d2b2 100644 --- a/source/backend/challenge/modules/task/services/DeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/DeleteTaskService.java @@ -1,13 +1,15 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.messages.ErrorMessages; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.UUID; @Singleton public class DeleteTaskService implements IDeleteTaskService { - private final ITaskRepository taskRepository; @Inject @@ -16,8 +18,22 @@ public DeleteTaskService(final ITaskRepository taskRepository) { } @Override - public void execute(Long taskId) { - // TODO: Criar serviço responsável por deletar a tarefa + public OutputPort execute(InputPort inputPort) { + try { + if (!isValidInput(inputPort.getTaskId())) return new OutputPort.Error(ErrorMessages.TASK_ID_IS_REQUIRED); + + Task taskExists = this.taskRepository.index(inputPort.getTaskId()); + if (taskExists == null) return new OutputPort.NotFound((ErrorMessages.TASK_NOT_FOUND)); + + this.taskRepository.delete(taskExists.getId()); + + return new OutputPort.Ok(); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } } + private boolean isValidInput(UUID taskId) { + return taskId != null; + } } diff --git a/source/backend/challenge/modules/task/services/ICreateTaskService.java b/source/backend/challenge/modules/task/services/ICreateTaskService.java index 33b7d7b..dcd5c8f 100644 --- a/source/backend/challenge/modules/task/services/ICreateTaskService.java +++ b/source/backend/challenge/modules/task/services/ICreateTaskService.java @@ -2,9 +2,42 @@ import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.models.Task; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; public interface ICreateTaskService { + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + class InputPort { + private TaskDTO taskDTO; + } + + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements OutputPort { + private Task task; + public Ok(Task task) { + this.task = task; + } + } + + @Getter + @Setter + class Error implements OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } - Task execute(TaskDTO taskDTO); + OutputPort execute(InputPort inputPort); } diff --git a/source/backend/challenge/modules/task/services/IDeleteTaskService.java b/source/backend/challenge/modules/task/services/IDeleteTaskService.java index 6b031ac..46f8250 100644 --- a/source/backend/challenge/modules/task/services/IDeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/IDeleteTaskService.java @@ -1,8 +1,51 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.models.Task; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.UUID; + public interface IDeleteTaskService { + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + class InputPort { + private UUID taskId; + } + + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements IDeleteTaskService.OutputPort { + private Task task; + public Ok() {} + } + + @Getter + @Setter + class NotFound implements IDeleteTaskService.OutputPort { + private String message; + public NotFound(String message) { + this.message = message; + } + } + + @Getter + @Setter + class Error implements IDeleteTaskService.OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } - void execute(Long taskId); + OutputPort execute(InputPort inputPort); } diff --git a/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java b/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java index 3e30258..e7b659d 100644 --- a/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java +++ b/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java @@ -1,11 +1,32 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.models.Task; +import lombok.Getter; +import lombok.Setter; +import java.io.Serializable; import java.util.List; public interface IRetrieveAllTasksService { + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements IRetrieveAllTasksService.OutputPort { + private List tasks; + public Ok(List tasks) { + this.tasks = tasks; + } + } - List execute(); + @Getter + @Setter + class Error implements IRetrieveAllTasksService.OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } + OutputPort execute(); } diff --git a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java index 192e28c..c436b3f 100644 --- a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java +++ b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java @@ -1,9 +1,50 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.models.Task; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.UUID; public interface IRetrieveTaskByIdService { + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + class InputPort { + private UUID taskId; + } + + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements OutputPort { + private Task task; + public Ok(Task task) {this.task = task;} + } + + @Getter + @Setter + class NotFound implements OutputPort { + private String message; + public NotFound(String message) { + this.message = message; + } + } + + @Getter + @Setter + class Error implements OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } - Task execute(Long taskId); + OutputPort execute(InputPort inputPort); } diff --git a/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java b/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java index 133b794..0e30913 100644 --- a/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java +++ b/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java @@ -2,9 +2,48 @@ import backend.challenge.modules.task.dtos.TaskProgressDTO; import backend.challenge.modules.task.models.Task; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.UUID; public interface IUpdateTaskProgressService { + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + class InputPort { + private TaskProgressDTO taskProgressDTO; + } + + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements IUpdateTaskProgressService.OutputPort { + public Ok() {} + } + + @Getter + @Setter + class NotFound implements IUpdateTaskProgressService.OutputPort { + private String message; + public NotFound(String message) { + this.message = message; + } + } - Task execute(TaskProgressDTO taskProgressDTO); + @Getter + @Setter + class Error implements IUpdateTaskProgressService.OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } + OutputPort execute(InputPort inputPort); } diff --git a/source/backend/challenge/modules/task/services/IUpdateTaskService.java b/source/backend/challenge/modules/task/services/IUpdateTaskService.java index 3da8340..c276068 100644 --- a/source/backend/challenge/modules/task/services/IUpdateTaskService.java +++ b/source/backend/challenge/modules/task/services/IUpdateTaskService.java @@ -1,9 +1,51 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.dtos.TaskDTO; import backend.challenge.modules.task.models.Task; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.UUID; public interface IUpdateTaskService { + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + class InputPort { + private UUID id; + private TaskDTO task; + } + + interface OutputPort extends Serializable { + @Getter + @Setter + class Ok implements OutputPort { + private Task task; + public Ok(Task task) {this.task = task;} + } + + @Getter + @Setter + class NotFound implements OutputPort { + private String message; + public NotFound(String message) { + this.message = message; + } + } - Task execute(Task task); + @Getter + @Setter + class Error implements OutputPort { + private String message; + public Error(String message) { + this.message = message; + } + } + } -} + OutputPort execute(InputPort inputPort); +} \ No newline at end of file diff --git a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java b/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java index 52a4149..3eba6ea 100644 --- a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java +++ b/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java @@ -18,10 +18,14 @@ public RetrieveAllTasksService(final ITaskRepository taskRepository) { } @Override - public List execute() { - // TODO: Criar serviço responsável por recuperar todas as tarefas + public OutputPort execute() { + try { + List tasks = this.taskRepository.show(); + return new OutputPort.Ok(tasks); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } - return null; } } diff --git a/source/backend/challenge/modules/task/services/RetrieveTaskByIdService.java b/source/backend/challenge/modules/task/services/RetrieveTaskByIdService.java new file mode 100644 index 0000000..631837e --- /dev/null +++ b/source/backend/challenge/modules/task/services/RetrieveTaskByIdService.java @@ -0,0 +1,42 @@ +package backend.challenge.modules.task.services; + +import backend.challenge.modules.task.messages.ErrorMessages; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class RetrieveTaskByIdService implements IRetrieveTaskByIdService { + private final ITaskRepository taskRepository; + + @Inject + public RetrieveTaskByIdService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public OutputPort execute(InputPort inputPort) { + try { + if (inputPort == null) return new OutputPort.Error(ErrorMessages.INVALID_INPUT); + + OutputPort validateInput = this.isValidInput(inputPort); + if (validateInput instanceof OutputPort.Error) return validateInput; + + Task task = this.taskRepository.index(inputPort.getTaskId()); + if (task == null) return new OutputPort.NotFound(ErrorMessages.TASK_NOT_FOUND); + + return new OutputPort.Ok(task); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } + } + + private OutputPort isValidInput(InputPort inputPort) { + if (inputPort.getTaskId() == null) return new OutputPort.Error(ErrorMessages.TASK_ID_IS_REQUIRED); + + return null; + } + +} diff --git a/source/backend/challenge/modules/task/services/UpdateTaskProgressService.java b/source/backend/challenge/modules/task/services/UpdateTaskProgressService.java new file mode 100644 index 0000000..46aa7e0 --- /dev/null +++ b/source/backend/challenge/modules/task/services/UpdateTaskProgressService.java @@ -0,0 +1,67 @@ +package backend.challenge.modules.task.services; + +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.messages.ErrorMessages; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class UpdateTaskProgressService implements IUpdateTaskProgressService { + private final ITaskRepository taskRepository; + + @Inject + public UpdateTaskProgressService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + + @Override + public OutputPort execute(InputPort inputPort) { + try { + if (inputPort == null) return new OutputPort.Error(ErrorMessages.INVALID_INPUT); + + OutputPort validateInput = this.isValidInput(inputPort); + if (validateInput instanceof OutputPort.Error) return validateInput; + + Task task = this.taskRepository.index(inputPort.getTaskProgressDTO().getId()); + if (task == null) return new OutputPort.NotFound(ErrorMessages.TASK_NOT_FOUND); + + if (task.getStatus() == TaskStatus.COMPLETE) return new OutputPort.Error(ErrorMessages.TASK_IS_ALREADY_COMPLETE); + + int progressToAdd = inputPort.getTaskProgressDTO().getProgress(); + if (task.getProgress() + progressToAdd < 0) return new OutputPort.Error(ErrorMessages.TASK_PROGRESS_MUST_BE_GREATER_THAN_OR_EQUAL_TO_0); + + int newProgress = this.calculateProgress(task, inputPort.getTaskProgressDTO()); + task.setProgress(newProgress); + this.taskRepository.update(task); + + return new OutputPort.Ok(); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } + + } + private OutputPort isValidInput(InputPort inputPort) { + if (inputPort.getTaskProgressDTO() == null) return new OutputPort.Error(ErrorMessages.TASK_PROGRESS_IS_REQUIRED); + if (inputPort.getTaskProgressDTO().getId() == null) return new OutputPort.Error(ErrorMessages.TASK_ID_IS_REQUIRED); + if (inputPort.getTaskProgressDTO().getProgress() < 0) return new OutputPort.Error(ErrorMessages.TASK_PROGRESS_MUST_BE_GREATER_THAN_OR_EQUAL_TO_0); + if (inputPort.getTaskProgressDTO().getProgress() > 100) return new OutputPort.Error(ErrorMessages.TASK_PROGRESS_MUST_BE_LESS_THAN_OR_EQUAL_TO_100); + + return null; + } + + private int calculateProgress(Task task, TaskProgressDTO taskProgressDTO) { + int progress = task.getProgress() + taskProgressDTO.getProgress(); + progress = Math.min(progress, 100); + + if (progress == 100) { + task.setStatus(TaskStatus.COMPLETE); + } + + return progress; + } +} diff --git a/source/backend/challenge/modules/task/services/UpdateTaskService.java b/source/backend/challenge/modules/task/services/UpdateTaskService.java new file mode 100644 index 0000000..1625945 --- /dev/null +++ b/source/backend/challenge/modules/task/services/UpdateTaskService.java @@ -0,0 +1,49 @@ +package backend.challenge.modules.task.services; + +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class UpdateTaskService implements IUpdateTaskService { + private final ITaskRepository taskRepository; + + @Inject + public UpdateTaskService(ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public OutputPort execute(InputPort inputPort) { + try { + if (!isValidInput(inputPort.getId(), inputPort.getTask())) return new OutputPort.Error("Invalid input"); + + Task taskToUpdate = this.taskRepository.index(inputPort.getId()); + if (taskToUpdate == null) return new OutputPort.NotFound("Task not found"); + + this.updateFields(inputPort.getTask(), taskToUpdate); + + return new OutputPort.Ok(taskRepository.update(taskToUpdate)); + } catch (Exception e) { + return new OutputPort.Error(e.getMessage()); + } + } + + private void updateFields(TaskDTO task, Task taskToUpdate) { + if (task.getTitle() != null && !task.getTitle().isEmpty()) { + taskToUpdate.setTitle(task.getTitle()); + } + if (task.getDescription() != null && !task.getDescription().isEmpty()) { + taskToUpdate.setDescription(task.getDescription()); + } + } + + private boolean isValidInput(UUID id, TaskDTO task) { + if (id == null || task == null) return false; + return task.getTitle() != null && !task.getTitle().isEmpty(); + } +} diff --git a/tests/Runner.java b/tests/Runner.java index 1ff4b19..2e186cf 100644 --- a/tests/Runner.java +++ b/tests/Runner.java @@ -1,7 +1,4 @@ -package backend.challenge; - import kikaha.core.test.KikahaServerRunner; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java index 684dc44..8b78770 100644 --- a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java @@ -1,5 +1,8 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; @@ -8,7 +11,9 @@ import org.junit.Test; import org.junit.runner.RunWith; -@RunWith( KikahaRunner.class ) +import static org.junit.Assert.*; + +@RunWith(KikahaRunner.class) public class CreateTaskServiceTest { private ICreateTaskService createTaskService; @@ -16,17 +21,55 @@ public class CreateTaskServiceTest { @Before public void init() { final ITaskRepository taskRepository = new TaskRepository(); - createTaskService = new CreateTaskService(taskRepository); } @Test public void shouldBeAbleToCreateANewTask() { - // TODO: Para que esse teste passe, sua aplicação deve permitir que - // uma tarefa seja criado, e retorne um json com a tarefa criada. + // Arrange + TaskDTO taskDTO = TaskDTO.create(); + taskDTO.setTitle("Test Task"); + ICreateTaskService.InputPort inputPort = new ICreateTaskService.InputPort(taskDTO); + + // Act + ICreateTaskService.OutputPort outputPort = createTaskService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof ICreateTaskService.OutputPort.Ok); + Task createdTask = ((ICreateTaskService.OutputPort.Ok) outputPort).getTask(); + assertNotNull(createdTask); + assertEquals("Test Task", createdTask.getTitle()); + assertEquals(0, createdTask.getProgress()); + assertEquals(TaskStatus.PROGRESS, createdTask.getStatus()); + assertNotNull(createdTask.getId()); } + @Test + public void shouldBeAbleToReturnErrorWhenTaskTitleIsEmpty() { + // Arrange + TaskDTO taskDTO = TaskDTO.create(); + ICreateTaskService.InputPort inputPort = new ICreateTaskService.InputPort(taskDTO); + + // Act + ICreateTaskService.OutputPort outputPort = createTaskService.execute(inputPort); + // Assert + assertTrue(outputPort instanceof ICreateTaskService.OutputPort.Error); + assertEquals("Title is required", ((ICreateTaskService.OutputPort.Error) outputPort).getMessage()); + } + @Test + public void shouldBeAbleToReturnErrorWhenTaskTitleIsNull() { + // Arrange + TaskDTO taskDTO = TaskDTO.create(); + taskDTO.setTitle(null); + ICreateTaskService.InputPort inputPort = new ICreateTaskService.InputPort(taskDTO); + // Act + ICreateTaskService.OutputPort outputPort = createTaskService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof ICreateTaskService.OutputPort.Error); + assertEquals("Title is required", ((ICreateTaskService.OutputPort.Error) outputPort).getMessage()); + } } \ No newline at end of file diff --git a/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java b/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java index 93497a5..14aaad3 100644 --- a/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java @@ -1,5 +1,7 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; @@ -7,25 +9,56 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) -public class DeleteTaskServiceTest { +import java.util.UUID; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; +@RunWith(KikahaRunner.class) +public class DeleteTaskServiceTest { private IDeleteTaskService deleteTaskService; + @Mock + private ITaskRepository taskRepository; + @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); - + MockitoAnnotations.initMocks(this); deleteTaskService = new DeleteTaskService(taskRepository); } @Test public void shouldBeAbleToDeleteTaskById() { - // TODO: Para que esse teste passe, sua aplicação deve permitir que tarefas sejam deletadas por id. + // Arrange + UUID taskId = UUID.randomUUID(); + Task task = new Task(UUID.randomUUID(), "Test Task", "Test Description", 0, TaskStatus.PROGRESS, null); + when(taskRepository.index(taskId)).thenReturn(task); + IDeleteTaskService.InputPort inputPort = new IDeleteTaskService.InputPort(taskId); + + // Act + IDeleteTaskService.OutputPort outputPort = deleteTaskService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof IDeleteTaskService.OutputPort.Ok); + assertNotNull(task); } + @Test + public void shouldBeAbleToReturnNotFoundWhenTaskIsNotFound() { + // Arrange + UUID taskId = UUID.randomUUID(); + when(taskRepository.index(taskId)).thenReturn(null); + IDeleteTaskService.InputPort inputPort = new IDeleteTaskService.InputPort(taskId); + // Act + IDeleteTaskService.OutputPort outputPort = deleteTaskService.execute(inputPort); + // Assert + assertTrue(outputPort instanceof IDeleteTaskService.OutputPort.NotFound); + } } \ No newline at end of file diff --git a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java index 68f0012..14a5a3d 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java @@ -1,5 +1,7 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; @@ -7,25 +9,49 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@RunWith(KikahaRunner.class) public class RetrieveAllTasksServiceTest { private IRetrieveAllTasksService retrieveAllTasksService; + @Mock + private ITaskRepository taskRepository; + @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); - + MockitoAnnotations.initMocks(this); retrieveAllTasksService = new RetrieveAllTasksService(taskRepository); } @Test public void shouldBeAbleToListTheTasks() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que seja - retornado um array com todas as tarefas que foram criadas até o momento. - */ + // Arrange + List tasks = createTasks(); + IRetrieveAllTasksService.OutputPort outputPort = retrieveAllTasksService.execute(); + + // Act + Mockito.when(taskRepository.show()).thenReturn(tasks); + + // Assert + assert outputPort instanceof IRetrieveAllTasksService.OutputPort.Ok; + assert tasks.size() == 3; + Mockito.verify(taskRepository, Mockito.times(1)).show(); + } + + private List createTasks() { + List tasks = new ArrayList<>(); + tasks.add(new Task(UUID.randomUUID(), "Test Task", "Test Description", 0, TaskStatus.PROGRESS, null)); + tasks.add(new Task(UUID.randomUUID(), "Test Task 2", "Test Description 2", 0, TaskStatus.PROGRESS, null)); + tasks.add(new Task(UUID.randomUUID(), "Test Task 3", "Test Description 3", 0, TaskStatus.PROGRESS, null)); + return tasks; } } \ No newline at end of file diff --git a/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java b/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java index eb96b15..61a1785 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java @@ -1,24 +1,88 @@ package backend.challenge.modules.task.services; - +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; -import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) -public class RetrieveTaskByIdServiceTest { +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; +@RunWith(KikahaRunner.class) +public class RetrieveTaskByIdServiceTest { private IRetrieveTaskByIdService retrieveTaskByIdService; + @Mock + private ITaskRepository taskRepository; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + retrieveTaskByIdService = new RetrieveTaskByIdService(taskRepository); + } + @Test public void shouldBeAbleToListTheTaskById() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que seja - retornado uma tarefa com o mesmo id informado. - */ + // Arrange + UUID taskId = UUID.randomUUID(); + Task task = new Task(UUID.randomUUID(), "Test Task", "Test Description", 0, TaskStatus.PROGRESS, null); + when(taskRepository.index(taskId)).thenReturn(task); + IRetrieveTaskByIdService.InputPort inputPort = new IRetrieveTaskByIdService.InputPort(taskId); + + // Act + IRetrieveTaskByIdService.OutputPort outputPort = retrieveTaskByIdService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof IRetrieveTaskByIdService.OutputPort.Ok); + assertNotNull(task); + assertEquals("Test Task", task.getTitle()); + assertEquals(0, task.getProgress()); + assertEquals(TaskStatus.PROGRESS, task.getStatus()); + assertNotNull(task.getId()); } -} + @Test + public void shouldBeAbleToReturnNotFoundWhenTaskIsNotFound() { + // Arrange + UUID taskId = UUID.randomUUID(); + when(taskRepository.index(taskId)).thenReturn(null); + IRetrieveTaskByIdService.InputPort inputPort = new IRetrieveTaskByIdService.InputPort(taskId); + + // Act + IRetrieveTaskByIdService.OutputPort outputPort = retrieveTaskByIdService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof IRetrieveTaskByIdService.OutputPort.NotFound); + } + + @Test + public void shouldBeAbleToReturnErrorWhenTaskIdIsNull() { + // Arrange + IRetrieveTaskByIdService.InputPort inputPort = new IRetrieveTaskByIdService.InputPort(null); + + // Act + IRetrieveTaskByIdService.OutputPort outputPort = retrieveTaskByIdService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof IRetrieveTaskByIdService.OutputPort.Error); + } + + @Test + public void shouldBeAbleToReturnErrorWhenInputPortIsNull() { + // Arrange + IRetrieveTaskByIdService.InputPort inputPort = null; + + // Act + IRetrieveTaskByIdService.OutputPort outputPort = retrieveTaskByIdService.execute(inputPort); + + // Assert + assertTrue(outputPort instanceof IRetrieveTaskByIdService.OutputPort.Error); + } +} \ No newline at end of file diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java index e6b0bf6..21ef87d 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java @@ -1,27 +1,115 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; import kikaha.core.test.KikahaRunner; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.UUID; + +import static org.mockito.Mockito.when; @RunWith( KikahaRunner.class ) public class UpdateTaskProgressServiceTest { + private IUpdateTaskProgressService updateTaskProgressService; + + @Mock + private ITaskRepository taskRepository; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + updateTaskProgressService = new UpdateTaskProgressService(taskRepository); + } @Test public void shouldBeAbleToUpdateTaskProgress() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas o campo `progress`. - */ + // Arrange + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create(); + taskProgressDTO.setProgress(50); + taskProgressDTO.setId(UUID.randomUUID()); + Task task = createTask(); + + when(taskRepository.index(taskProgressDTO.getId())).thenReturn(task); + + IUpdateTaskProgressService.InputPort inputPort = new IUpdateTaskProgressService.InputPort(taskProgressDTO); + + // Act + IUpdateTaskProgressService.OutputPort outputPort = updateTaskProgressService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskProgressService.OutputPort.Ok; + assert task.getProgress() == 50; } @Test public void shouldBeAbleToUpdateOnlyTaskStatusWhenProgressEqualsOneHundred() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterado apenas o campo `status`, quando o progresso for igual a 100. - */ + // Arrange + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create(); + taskProgressDTO.setProgress(100); + taskProgressDTO.setId(UUID.randomUUID()); + Task task = createTask(); + + when(taskRepository.index(taskProgressDTO.getId())).thenReturn(task); + + IUpdateTaskProgressService.InputPort inputPort = new IUpdateTaskProgressService.InputPort(taskProgressDTO); + + // Act + IUpdateTaskProgressService.OutputPort outputPort = updateTaskProgressService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskProgressService.OutputPort.Ok; + assert task.getProgress() == 100; + assert task.getStatus() == TaskStatus.COMPLETE; + } + + @Test + public void shouldNotBeAbleToUpdateTaskProgressWhenProgressLessThanOneHundred() { + // Arrange + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create(); + taskProgressDTO.setProgress(-1); + taskProgressDTO.setId(UUID.randomUUID()); + Task task = createTask(); + + when(taskRepository.index(taskProgressDTO.getId())).thenReturn(task); + + IUpdateTaskProgressService.InputPort inputPort = new IUpdateTaskProgressService.InputPort(taskProgressDTO); + + // Act + IUpdateTaskProgressService.OutputPort outputPort = updateTaskProgressService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskProgressService.OutputPort.Error; + } + + @Test + public void shouldNotBeAbleToUpdateTaskProgressWhenProgressGreaterThanOneHundred() { + // Arrange + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create(); + taskProgressDTO.setProgress(101); + taskProgressDTO.setId(UUID.randomUUID()); + Task task = createTask(); + + when(taskRepository.index(taskProgressDTO.getId())).thenReturn(task); + + IUpdateTaskProgressService.InputPort inputPort = new IUpdateTaskProgressService.InputPort(taskProgressDTO); + + // Act + IUpdateTaskProgressService.OutputPort outputPort = updateTaskProgressService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskProgressService.OutputPort.Error; + } + + private Task createTask() { + return new Task(UUID.randomUUID(), "Test Task", "Test Description", 0, TaskStatus.PROGRESS, null); } } diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java index 53a4082..6047e7b 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java @@ -1,5 +1,8 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.repositories.ITaskRepository; import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; @@ -7,38 +10,96 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.UUID; + +import static backend.challenge.modules.task.enums.TaskStatus.PROGRESS; +import static org.mockito.Mockito.when; @RunWith( KikahaRunner.class ) public class UpdateTaskServiceTest { private IUpdateTaskService updateTaskService; + @Mock + private ITaskRepository taskRepository; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + updateTaskService = new UpdateTaskService(taskRepository); + } + @Test public void shouldBeAbleToUpdateTask() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas os campos `title` e `observation`. - */ + // Arrange + UUID taskId = UUID.randomUUID(); + TaskDTO taskDTO = TaskDTO.create(); + taskDTO.setTitle("Test rename task"); + taskDTO.setDescription("Test rename description"); + + Task task = createTask(); + + when(taskRepository.index(taskId)).thenReturn(task); + + IUpdateTaskService.InputPort inputPort = new IUpdateTaskService.InputPort(taskId, taskDTO); + + // Act + IUpdateTaskService.OutputPort outputPort = updateTaskService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskService.OutputPort.Ok; + assert task.getTitle().equals("Test rename task"); + assert task.getDescription().equals("Test rename description"); } @Test public void shouldNotBeAbleToUpdateATaskThatDoesNotExist() { - /* - TODO: Para que esse teste passe, você deve validar na sua rota de update se - o id da tarefa enviada pela url existe ou não. Caso não exista, retornar um erro com status 400. - */ + // Arrange + UUID taskId = UUID.randomUUID(); + TaskDTO taskDTO = TaskDTO.create(); + taskDTO.setTitle("Test rename task"); + taskDTO.setDescription("Test rename description"); + + when(taskRepository.index(taskId)).thenReturn(null); + + IUpdateTaskService.InputPort inputPort = new IUpdateTaskService.InputPort(taskId, taskDTO); + + // Act + IUpdateTaskService.OutputPort outputPort = updateTaskService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskService.OutputPort.NotFound; + } @Test public void shouldNotBeAbleToUpdateTaskStatusManually() { - /* - TODO: Para que esse teste passe, você não deve permitir que sua rota de update - altere diretamente o `status` dessa tarefa, mantendo o mesmo status que a tarefa - já possuía antes da atualização. Isso porque o único lugar que deve atualizar essa informação - é a rota responsável por alterar o progresso da tarefa. + // Arrange + UUID taskId = UUID.randomUUID(); + TaskDTO taskDTO = TaskDTO.create(); + taskDTO.setTitle("Test rename task"); + taskDTO.setDescription("Test rename description"); - */ - } + Task task = createTask(); + when(taskRepository.index(taskId)).thenReturn(task); + IUpdateTaskService.InputPort inputPort = new IUpdateTaskService.InputPort(taskId, taskDTO); + + // Act + IUpdateTaskService.OutputPort outputPort = updateTaskService.execute(inputPort); + + // Assert + assert outputPort instanceof IUpdateTaskService.OutputPort.Ok; + assert task.getTitle().equals("Test rename task"); + assert task.getDescription().equals("Test rename description"); + assert task.getStatus() == PROGRESS; + } + + private Task createTask() { + return new Task(UUID.randomUUID(), "Test Task", "Test Description", 0, PROGRESS, null); + } } \ No newline at end of file