diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5b490f3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.1' + +services: + tasks_db: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: "mysql" + MYSQL_DATABASE: "tasks_db" + ports: + - "3306:3306" + volumes: + - ./script.sql:/docker-entrypoint-initdb.d/init.sql diff --git a/pom.xml b/pom.xml index 53ab393..f2c5246 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 @@ -66,6 +67,15 @@ ${version.junit} test + + + org.mockito + mockito-core + 5.3.1 + test + + + org.projectlombok @@ -78,6 +88,17 @@ javax.annotation-api 1.3.2 + + + + io.skullabs.kikaha + kikaha-db + + + mysql + mysql-connector-java + 8.0.33 + @@ -111,4 +132,4 @@ - + \ No newline at end of file diff --git a/resources/conf/application.yml b/resources/conf/application.yml index 2de980e..26ecb2b 100644 --- a/resources/conf/application.yml +++ b/resources/conf/application.yml @@ -23,3 +23,10 @@ server: health-check: enabled: true url: "/api/internal/health-check" + + db: + datasources: + default: + jdbc-url: "jdbc:mysql://localhost:3306/tasks_db" + username: "root" + password: "mysql" diff --git a/script.sql b/script.sql new file mode 100644 index 0000000..53866a4 --- /dev/null +++ b/script.sql @@ -0,0 +1,11 @@ +USE tasks_db; + +CREATE TABLE IF NOT EXISTS tasks ( + id CHAR(36) NOT NULL DEFAULT (UUID()), + title VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + progress INT NOT NULL DEFAULT 0, + status ENUM('PROGRESS', 'COMPLETE') NOT NULL DEFAULT 'PROGRESS', + createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); diff --git a/source/backend/challenge/modules/task/config/UUIDConverter.java b/source/backend/challenge/modules/task/config/UUIDConverter.java new file mode 100644 index 0000000..6f70485 --- /dev/null +++ b/source/backend/challenge/modules/task/config/UUIDConverter.java @@ -0,0 +1,31 @@ +package backend.challenge.modules.task.config; + +import java.util.UUID; + +import javax.enterprise.inject.Typed; +import javax.inject.Singleton; + +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import java.util.UUID; +import kikaha.urouting.api.AbstractConverter; +import kikaha.urouting.api.ConversionException; + +/** + * UUIDConverter + */ +@Singleton +@Typed(AbstractConverter.class) +public class UUIDConverter extends AbstractConverter{ + + @Override + public UUID convert(String value) throws ConversionException { + + try { + return UUID.fromString(value); + } catch (Exception e) { + throw new RuntimeException("Falha ao converter " + value + " para UUID"); + } + } + + +} diff --git a/source/backend/challenge/modules/task/dtos/TaskDTO.java b/source/backend/challenge/modules/task/dtos/TaskDTO.java index d73cf02..45379c7 100644 --- a/source/backend/challenge/modules/task/dtos/TaskDTO.java +++ b/source/backend/challenge/modules/task/dtos/TaskDTO.java @@ -6,8 +6,6 @@ import lombok.Setter; import lombok.experimental.Accessors; -import java.util.Date; - @Getter @Setter @Accessors(chain = true) diff --git a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java index ee6277f..11b53df 100644 --- a/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java +++ b/source/backend/challenge/modules/task/dtos/TaskProgressDTO.java @@ -1,5 +1,7 @@ package backend.challenge.modules.task.dtos; +import java.util.UUID; + import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -11,7 +13,7 @@ @NoArgsConstructor(staticName = "create") public class TaskProgressDTO { - private Long id; + private UUID id; private int progress; } diff --git a/source/backend/challenge/modules/task/exceptions/ProgressTaskUpdateNotAvailable.java b/source/backend/challenge/modules/task/exceptions/ProgressTaskUpdateNotAvailable.java new file mode 100644 index 0000000..02f9843 --- /dev/null +++ b/source/backend/challenge/modules/task/exceptions/ProgressTaskUpdateNotAvailable.java @@ -0,0 +1,7 @@ +package backend.challenge.modules.task.exceptions; + +public class ProgressTaskUpdateNotAvailable extends RuntimeException{ + public ProgressTaskUpdateNotAvailable(){ + super("O progresso deve estar entre 0 e 100."); + } +} diff --git a/source/backend/challenge/modules/task/exceptions/TaskAlterationNotAvailable.java b/source/backend/challenge/modules/task/exceptions/TaskAlterationNotAvailable.java new file mode 100644 index 0000000..f0f3f02 --- /dev/null +++ b/source/backend/challenge/modules/task/exceptions/TaskAlterationNotAvailable.java @@ -0,0 +1,7 @@ +package backend.challenge.modules.task.exceptions; + +public class TaskAlterationNotAvailable extends RuntimeException{ + public TaskAlterationNotAvailable() { + super("É apenas possível alterar o Título e Descrição nesta operação."); + } +} diff --git a/source/backend/challenge/modules/task/exceptions/TaskNotFound.java b/source/backend/challenge/modules/task/exceptions/TaskNotFound.java new file mode 100644 index 0000000..ff6ed82 --- /dev/null +++ b/source/backend/challenge/modules/task/exceptions/TaskNotFound.java @@ -0,0 +1,8 @@ +package backend.challenge.modules.task.exceptions; + +public class TaskNotFound extends RuntimeException { + + public TaskNotFound() { + super("Tarefa não encontrda."); + } +} 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..3077183 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskController.java @@ -1,10 +1,13 @@ package backend.challenge.modules.task.infra.http.controllers; +import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; import backend.challenge.modules.task.infra.http.views.TaskView; import backend.challenge.modules.task.models.Task; import backend.challenge.modules.task.services.*; import kikaha.urouting.api.*; - +import java.util.UUID; import javax.inject.Inject; import javax.inject.Singleton; @@ -20,56 +23,75 @@ 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 iUpdateTaskService) { this.createTaskService = createTaskService; this.deleteTaskService = deleteTaskService; this.retrieveAllTasksService = retrieveAllTasksService; - this.retrieveTaskByIdService = null; - this.updateTaskService = null; + this.retrieveTaskByIdService = retrieveTaskByIdService; + this.updateTaskService = iUpdateTaskService; } @GET public Response show() { - // TODO: Rota que lista todas as tarefas - - return DefaultResponse.ok().entity("Hello world"); + return DefaultResponse.ok().entity(retrieveAllTasksService.execute()); } @GET @Path("single/{taskId}") - public Response index(@PathParam("taskId") Long taskId) { - // TODO: A rota deve retornar somente a tarefa a qual o id corresponder + public Response index(@PathParam("taskId") UUID taskId) { + + try { + Task taskOpt = this.retrieveTaskByIdService.execute(taskId); + return DefaultResponse.ok().entity(taskOpt); + + } catch (TaskNotFound e) { + return DefaultResponse.badRequest().entity(e.getMessage()); + } - return DefaultResponse.ok().entity("Hello world"); } @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. + public Response create(TaskView taskView) { + + TaskDTO taskDto = TaskDTO.create().setTitle(taskView.getTitle()).setDescription(taskView.getDescription()); + this.createTaskService.execute(taskDto); - return DefaultResponse.ok().entity("Hello world"); + return DefaultResponse.created(); } @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. - */ + public Response update(@PathParam("taskId") UUID taskId, TaskDTO taskDto) { + + try { + Task task = new Task(); + task.setId(taskId); + task.setDescription(taskDto.getDescription()); + task.setTitle(taskDto.getTitle()); + this.updateTaskService.execute(task); + return DefaultResponse.ok(); + } catch (TaskAlterationNotAvailable| TaskNotFound e) { + + return DefaultResponse.badRequest().entity(e.getMessage()); + } catch (Exception e) { + return DefaultResponse.serverError(); + } - return DefaultResponse.ok().entity("Hello world"); } @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") UUID taskId) { + try { + this.deleteTaskService.execute(taskId); + return DefaultResponse.ok(); + } catch (TaskNotFound e) { + return DefaultResponse.badRequest().entity(e.getMessage()); + } } } 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..3c96358 100644 --- a/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java +++ b/source/backend/challenge/modules/task/infra/http/controllers/TaskProgressController.java @@ -1,10 +1,13 @@ package backend.challenge.modules.task.infra.http.controllers; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.exceptions.ProgressTaskUpdateNotAvailable; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; 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 java.util.UUID; import javax.inject.Inject; import javax.inject.Singleton; @@ -21,15 +24,18 @@ 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") UUID taskId, TaskProgressView taskProgressView) { + TaskProgressDTO progressDTO = TaskProgressDTO.create().setId(taskId) + .setProgress(taskProgressView.getProgress()); + + try { + this.updateTaskProgressService.execute(progressDTO); + return DefaultResponse.ok(); + + } catch (ProgressTaskUpdateNotAvailable | TaskNotFound e) { + return DefaultResponse.badRequest().entity(e.getMessage()); + } + } } diff --git a/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java b/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java index 3375195..16a9d50 100644 --- a/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java +++ b/source/backend/challenge/modules/task/infra/http/views/TaskProgressView.java @@ -5,6 +5,7 @@ @Getter @Setter + public class TaskProgressView { private int progress; diff --git a/source/backend/challenge/modules/task/infra/http/views/TaskView.java b/source/backend/challenge/modules/task/infra/http/views/TaskView.java index 8b0d67e..d02faba 100644 --- a/source/backend/challenge/modules/task/infra/http/views/TaskView.java +++ b/source/backend/challenge/modules/task/infra/http/views/TaskView.java @@ -4,7 +4,6 @@ import lombok.Getter; import lombok.Setter; -import java.util.Date; @Getter @Setter diff --git a/source/backend/challenge/modules/task/models/Task.java b/source/backend/challenge/modules/task/models/Task.java index 123f10c..5ea60e4 100644 --- a/source/backend/challenge/modules/task/models/Task.java +++ b/source/backend/challenge/modules/task/models/Task.java @@ -1,23 +1,35 @@ package backend.challenge.modules.task.models; import backend.challenge.modules.task.enums.TaskStatus; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.ToString; import lombok.experimental.Accessors; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Date; +import java.util.UUID; + +import javax.xml.crypto.Data; @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor +@ToString +@EqualsAndHashCode public class Task { - private Long id; + private UUID id; private String title; private String description; private int progress; private TaskStatus status; - private Date createdAt; + private LocalDate createdAt; } diff --git a/source/backend/challenge/modules/task/repositories/ITaskRepository.java b/source/backend/challenge/modules/task/repositories/ITaskRepository.java index 527450a..4be5c0c 100644 --- a/source/backend/challenge/modules/task/repositories/ITaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/ITaskRepository.java @@ -4,13 +4,16 @@ import backend.challenge.modules.task.models.Task; import java.util.List; +import java.util.Optional; +import java.util.UUID; public interface ITaskRepository { - Task index(Long taskId); + Optional index(UUID taskId); List show(); Task create(TaskDTO taskDTO); Task update(Task task); - void delete(Long taskId); + void delete(UUID taskId); + boolean existsById(UUID id); } diff --git a/source/backend/challenge/modules/task/repositories/TaskRepository.java b/source/backend/challenge/modules/task/repositories/TaskRepository.java index c47c8fd..0349d94 100644 --- a/source/backend/challenge/modules/task/repositories/TaskRepository.java +++ b/source/backend/challenge/modules/task/repositories/TaskRepository.java @@ -1,45 +1,160 @@ package backend.challenge.modules.task.repositories; import backend.challenge.modules.task.dtos.TaskDTO; +import backend.challenge.modules.task.enums.TaskStatus; import backend.challenge.modules.task.models.Task; +import lombok.NoArgsConstructor; +import javax.inject.Inject; import javax.inject.Singleton; +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Optional; +import java.util.UUID; @Singleton public class TaskRepository implements ITaskRepository { + @Inject + private DataSource db; + @Override - public Task index(final Long taskId) { - // TODO: Criar método responsável por retornar tarefa por id + public Optional index(final UUID taskId) { + String sql = "SELECT id, title, description, status, progress, createdAt FROM tasks WHERE id = ?"; + + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + preparedStatement.setString(1, taskId.toString()); + + ResultSet resultSet = preparedStatement.executeQuery(); + Task task = null; + if (resultSet.next()) { + task = new Task(); + task.setId(UUID.fromString(resultSet.getString("id"))); + task.setTitle(resultSet.getString("title")); + task.setDescription(resultSet.getString("description")); + task.setStatus(TaskStatus.valueOf(resultSet.getString("status"))); + task.setProgress(resultSet.getInt("progress")); + task.setCreatedAt(resultSet.getDate("createdAt").toLocalDate()); + return Optional.of(task); + } + return Optional.empty(); - return null; + } catch (SQLException e) { + throw new RuntimeException(e); + } } @Override public List show() { - // TODO: Criar método responsável por retornar todas as tarefas + String sql = "SELECT id, title, description, status, progress, createdAt FROM tasks"; - return null; + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + ResultSet resultSet = preparedStatement.executeQuery(); + List tasks = new ArrayList<>(); + while (resultSet.next()) { + Task task = new Task(); + task.setId(UUID.fromString(resultSet.getString("id"))); + task.setTitle(resultSet.getString("title")); + task.setDescription(resultSet.getString("description")); + task.setStatus(TaskStatus.valueOf(resultSet.getString("status"))); + task.setProgress(resultSet.getInt("progress")); + task.setCreatedAt(resultSet.getDate("createdAt").toLocalDate()); + tasks.add(task); + } + return tasks; + + } catch (SQLException e) { + throw new RuntimeException(e); + } } @Override public Task create(final TaskDTO taskDTO) { - // TODO: Criar método responsável por criar uma tarefa + String sql = "INSERT INTO tasks(id, title, description, status, progress, createdAt) VALUES(?, ?, ?, ?, ?, ?)"; + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + Task newTask = new Task( + UUID.randomUUID(), + taskDTO.getTitle(), + taskDTO.getDescription(), + 0, + TaskStatus.PROGRESS, + LocalDate.now() + ); + + preparedStatement.setString(1, newTask.getId().toString()); + preparedStatement.setString(2, newTask.getTitle()); + preparedStatement.setString(3, newTask.getDescription()); + preparedStatement.setString(4, newTask.getStatus().name()); + preparedStatement.setInt(5, newTask.getProgress()); + preparedStatement.setDate(6, java.sql.Date.valueOf(newTask.getCreatedAt())); + preparedStatement.executeUpdate(); + + return newTask; + + } catch (SQLException e) { + throw new RuntimeException(e); + } - return null; } @Override public Task update(final Task task) { - // TODO: Criar método responsável por atualizar uma tarefa + String sql = "UPDATE tasks SET title = ?, description = ?, status = ?, progress = ? WHERE id = ?"; + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + + preparedStatement.setString(1, task.getTitle()); + preparedStatement.setString(2, task.getDescription()); + preparedStatement.setString(3, task.getStatus().name()); + preparedStatement.setInt(4, task.getProgress()); + preparedStatement.setString(5, task.getId().toString()); + preparedStatement.executeUpdate(); + + return task; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void delete(final UUID taskId) { + + String sql = "DELETE FROM tasks WHERE id=?"; + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + preparedStatement.setString(1, taskId.toString()); + preparedStatement.executeUpdate(); + + } catch (SQLException e) { + throw new RuntimeException(e); + } - return null; } @Override - public void delete(final Long taskId) { - // TODO: Criar método responsável por deletar tarefa por id + public boolean existsById(UUID id) { + String sql = "SELECT COUNT(*) FROM tasks WHERE id = ?"; + try (PreparedStatement preparedStatement = this.db.getConnection().prepareStatement(sql)) { + preparedStatement.setString(1, id.toString()); + ResultSet resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + int count = resultSet.getInt(1); + resultSet.close(); + return count > 0; + } + return false; + + } catch (SQLException e) { + throw new RuntimeException(e); + } } diff --git a/source/backend/challenge/modules/task/services/CreateTaskService.java b/source/backend/challenge/modules/task/services/CreateTaskService.java index 46b144b..6fafda8 100644 --- a/source/backend/challenge/modules/task/services/CreateTaskService.java +++ b/source/backend/challenge/modules/task/services/CreateTaskService.java @@ -3,7 +3,6 @@ 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; @@ -19,9 +18,7 @@ public CreateTaskService(final ITaskRepository taskRepository) { @Override public Task execute(TaskDTO taskDTO) { - // TODO: Criar serviço responsável por criar uma tarefa - - return null; + return taskRepository.create(taskDTO); } } diff --git a/source/backend/challenge/modules/task/services/DeleteTaskService.java b/source/backend/challenge/modules/task/services/DeleteTaskService.java index c957d70..71d27d4 100644 --- a/source/backend/challenge/modules/task/services/DeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/DeleteTaskService.java @@ -1,7 +1,8 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.exceptions.TaskNotFound; import backend.challenge.modules.task.repositories.ITaskRepository; - +import java.util.UUID; import javax.inject.Inject; import javax.inject.Singleton; @@ -16,8 +17,11 @@ public DeleteTaskService(final ITaskRepository taskRepository) { } @Override - public void execute(Long taskId) { - // TODO: Criar serviço responsável por deletar a tarefa + public void execute(UUID taskId) throws TaskNotFound{ + + if(!taskRepository.existsById(taskId)) throw new TaskNotFound(); + + this.taskRepository.delete(taskId); } } diff --git a/source/backend/challenge/modules/task/services/IDeleteTaskService.java b/source/backend/challenge/modules/task/services/IDeleteTaskService.java index 6b031ac..2bcd6d4 100644 --- a/source/backend/challenge/modules/task/services/IDeleteTaskService.java +++ b/source/backend/challenge/modules/task/services/IDeleteTaskService.java @@ -1,8 +1,10 @@ package backend.challenge.modules.task.services; +import java.util.UUID; +import backend.challenge.modules.task.exceptions.TaskNotFound; public interface IDeleteTaskService { - void execute(Long taskId); + void execute(UUID taskId) throws TaskNotFound; } diff --git a/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java b/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java index 3e30258..6fa7490 100644 --- a/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java +++ b/source/backend/challenge/modules/task/services/IRetrieveAllTasksService.java @@ -1,7 +1,6 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.models.Task; - import java.util.List; public interface IRetrieveAllTasksService { diff --git a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java index 192e28c..f5b13da 100644 --- a/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java +++ b/source/backend/challenge/modules/task/services/IRetrieveTaskByIdService.java @@ -1,9 +1,11 @@ package backend.challenge.modules.task.services; +import java.util.UUID; +import backend.challenge.modules.task.exceptions.TaskNotFound; import backend.challenge.modules.task.models.Task; public interface IRetrieveTaskByIdService { - Task execute(Long taskId); + Task execute(UUID taskId) throws TaskNotFound; } diff --git a/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java b/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java index 133b794..1d9fdd8 100644 --- a/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java +++ b/source/backend/challenge/modules/task/services/IUpdateTaskProgressService.java @@ -1,10 +1,12 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; import backend.challenge.modules.task.models.Task; public interface IUpdateTaskProgressService { - Task execute(TaskProgressDTO taskProgressDTO); + Task execute(TaskProgressDTO taskProgressDTO) throws TaskAlterationNotAvailable, TaskNotFound; } diff --git a/source/backend/challenge/modules/task/services/IUpdateTaskService.java b/source/backend/challenge/modules/task/services/IUpdateTaskService.java index 3da8340..ed3034c 100644 --- a/source/backend/challenge/modules/task/services/IUpdateTaskService.java +++ b/source/backend/challenge/modules/task/services/IUpdateTaskService.java @@ -1,9 +1,11 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; import backend.challenge.modules.task.models.Task; -public interface IUpdateTaskService { +public interface IUpdateTaskService{ - Task execute(Task task); + Task execute(Task task) throws TaskNotFound, TaskAlterationNotAvailable; } diff --git a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java b/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java index 52a4149..18570de 100644 --- a/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java +++ b/source/backend/challenge/modules/task/services/RetrieveAllTasksService.java @@ -2,7 +2,6 @@ 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.List; @@ -19,9 +18,8 @@ public RetrieveAllTasksService(final ITaskRepository taskRepository) { @Override public List execute() { - // TODO: Criar serviço responsável por recuperar todas as tarefas - return null; + return taskRepository.show(); } } 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..a29f35d --- /dev/null +++ b/source/backend/challenge/modules/task/services/RetrieveTaskByIdService.java @@ -0,0 +1,29 @@ +package backend.challenge.modules.task.services; + +import java.util.Optional; +import java.util.UUID; + +import javax.inject.Inject; +import javax.inject.Singleton; +import backend.challenge.modules.task.exceptions.TaskNotFound; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; +@Singleton +public class RetrieveTaskByIdService implements IRetrieveTaskByIdService { + + private final ITaskRepository taskRepository; + + @Inject + public RetrieveTaskByIdService(final ITaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Override + public Task execute(UUID taskId) throws TaskNotFound{ + Optional taskOpt = this.taskRepository.index(taskId); + if(taskOpt.isEmpty()) throw new TaskNotFound(); + return taskOpt.get(); + } + + +} \ No newline at end of file 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..9746921 --- /dev/null +++ b/source/backend/challenge/modules/task/services/UpdateTaskProgressService.java @@ -0,0 +1,47 @@ +package backend.challenge.modules.task.services; + +import java.util.Optional; +import javax.inject.Inject; +import javax.inject.Singleton; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exceptions.ProgressTaskUpdateNotAvailable; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; + +@Singleton +public class UpdateTaskProgressService implements IUpdateTaskProgressService { + + private final ITaskRepository iTaskRepository; + + @Inject + public UpdateTaskProgressService(ITaskRepository iTaskRepository) { + this.iTaskRepository = iTaskRepository; + } + + @Override + public Task execute(TaskProgressDTO taskProgressDTO) throws TaskAlterationNotAvailable, TaskNotFound { + + if (taskProgressDTO.getProgress() > 100 || taskProgressDTO.getProgress() < 0) { + throw new ProgressTaskUpdateNotAvailable(); + } + + Optional taskOpt = iTaskRepository.index(taskProgressDTO.getId()); + if (taskOpt.isEmpty()) + throw new TaskNotFound(); + + Task task = taskOpt.get(); + task.setProgress(taskProgressDTO.getProgress()); + if (taskProgressDTO.getProgress() == 100) { + task.setStatus(TaskStatus.COMPLETE); + } else { + task.setStatus(TaskStatus.PROGRESS); + } + + iTaskRepository.update(task); + return task; + } + +} \ No newline at end of file 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..dc1c20c --- /dev/null +++ b/source/backend/challenge/modules/task/services/UpdateTaskService.java @@ -0,0 +1,36 @@ +package backend.challenge.modules.task.services; + +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Singleton; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; + +@Singleton +public class UpdateTaskService implements IUpdateTaskService { + + private final ITaskRepository iTaskRepository; + + @Inject + public UpdateTaskService(final ITaskRepository iTaskRepository) { + this.iTaskRepository = iTaskRepository; + } + + @Override + public Task execute(Task taskUp) throws TaskNotFound, TaskAlterationNotAvailable { + Optional taskOpt = iTaskRepository.index(taskUp.getId()); + if (taskOpt.isEmpty()) + throw new TaskNotFound(); + + Task task = taskOpt.get(); + + task.setDescription(taskUp.getDescription()); + task.setTitle(taskUp.getTitle()); + this.iTaskRepository.update(task); + return task; + + } +} diff --git a/tests/Runner.java b/tests/Runner.java index 1ff4b19..48bd2c2 100644 --- a/tests/Runner.java +++ b/tests/Runner.java @@ -1,7 +1,6 @@ -package backend.challenge; +// 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/controllers/TaskControllerTest.java b/tests/backend/challenge/modules/task/controllers/TaskControllerTest.java new file mode 100644 index 0000000..6f880fc --- /dev/null +++ b/tests/backend/challenge/modules/task/controllers/TaskControllerTest.java @@ -0,0 +1,107 @@ +package backend.challenge.modules.task.controllers; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exceptions.TaskNotFound; +import backend.challenge.modules.task.infra.http.controllers.TaskController; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.services.ICreateTaskService; +import backend.challenge.modules.task.services.IDeleteTaskService; +import backend.challenge.modules.task.services.IRetrieveAllTasksService; +import backend.challenge.modules.task.services.IRetrieveTaskByIdService; +import backend.challenge.modules.task.services.IUpdateTaskService; +import kikaha.core.test.KikahaRunner; +import kikaha.urouting.api.Response; + +@RunWith(KikahaRunner.class) +public class TaskControllerTest { + + @Mock + private ICreateTaskService createTaskService; + @Mock + private IDeleteTaskService deleteTaskService; + @Mock + private IRetrieveAllTasksService retrieveAllTasksService; + @Mock + private IRetrieveTaskByIdService retrieveTaskByIdService; + @Mock + private IUpdateTaskService updateTaskService; + + @InjectMocks + private TaskController controller; + + @Before + public void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void sholdReturnATasksList() { + Task task1 = new Task( + UUID.fromString("0402220c-6ba9-4e49-b7fb-42c443912db1"), + "Estudar sobre KIKAHA", + "Devo estudar a documentacao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Task task2 = new Task( + UUID.fromString("720981f8-19f9-4f13-815c-21e4294bed30"), + "Estudar sobre Mockito", + "Devo assistir 3 aulas", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + Task task3 = new Task(UUID.fromString("85d519e9-0c94-4524-81b3-1b4795347c50"), + "Completar o projeto Sizebay", + "Devo realizar os teste unitarios", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + List tasksExpect = List.of(task1, task2, task3); + + doReturn(tasksExpect).when(retrieveAllTasksService).execute(); + + Assert.assertEquals(tasksExpect, retrieveAllTasksService.execute()); + } + + @Test + public void sholdReturnATaskById() { + UUID id = UUID.fromString("0402220c-6ba9-4e49-b7fb-42c443912db1"); + Task task = new Task( + id, + "Estudar sobre KIKAHA", + "Devo estudar a documentacao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + doReturn(task).when(retrieveTaskByIdService).execute(id); + Assert.assertEquals(task, controller.index(id).entity()); + + } + + @Test + public void sholdReturn400StatusIfIdNotExists() { + UUID id = UUID.fromString("7a8853b1-7031-489a-b305-76e15b352154"); + + when(retrieveTaskByIdService.execute(id)).thenThrow(new TaskNotFound()); + Response response = controller.index(id); + assertEquals(400, response.statusCode()); + assertEquals(new TaskNotFound().getMessage(), response.entity()); + + } +} diff --git a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java index 684dc44..4571062 100644 --- a/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/CreateTaskServiceTest.java @@ -1,32 +1,52 @@ 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; +import static org.junit.Assert.assertEquals; +import java.time.LocalDate; +import java.util.UUID; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class CreateTaskServiceTest { - private ICreateTaskService createTaskService; + @InjectMocks + private CreateTaskService createTaskService; + + @Mock + private ITaskRepository taskRepository; @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); - - createTaskService = new CreateTaskService(taskRepository); + MockitoAnnotations.openMocks(this); } @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. - } - - + TaskDTO taskDTO = TaskDTO.create().setTitle("Algum titulo").setDescription("Alguma descricao"); + + Task expectedTask = new Task( + UUID.fromString("e36be03f-5b17-4720-845e-68412f8248c6"), + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.create(taskDTO)).thenReturn(expectedTask); + Task result = createTaskService.execute(taskDTO); + assertEquals(expectedTask, result); + Mockito.verify(taskRepository, Mockito.times(1)).create(taskDTO); + } } \ 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..17027c1 100644 --- a/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/DeleteTaskServiceTest.java @@ -1,28 +1,45 @@ package backend.challenge.modules.task.services; import backend.challenge.modules.task.repositories.ITaskRepository; -import backend.challenge.modules.task.repositories.TaskRepository; import kikaha.core.test.KikahaRunner; +import static org.mockito.Mockito.doReturn; +import java.util.UUID; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; @RunWith( KikahaRunner.class ) public class DeleteTaskServiceTest { - private IDeleteTaskService deleteTaskService; + @InjectMocks + private DeleteTaskService deleteTaskService; - @Before - public void init() { - final ITaskRepository taskRepository = new TaskRepository(); + @Mock + private ITaskRepository taskRepository; - deleteTaskService = new DeleteTaskService(taskRepository); + @Before + public void init(){ + MockitoAnnotations.openMocks(this); } + @Test public void shouldBeAbleToDeleteTaskById() { - // TODO: Para que esse teste passe, sua aplicação deve permitir que tarefas sejam deletadas por id. + UUID id = UUID.fromString("e36be03f-5b17-4720-845e-68412f8248c6"); + doReturn(true).when(taskRepository).existsById(id); + taskRepository.delete(id); + Mockito.verify(taskRepository, Mockito.times(1)).delete(id); + ArgumentCaptor idCaptor = ArgumentCaptor.forClass(UUID.class); + + Mockito.verify(taskRepository).delete(idCaptor.capture()); + + Assert.assertEquals(id, idCaptor.getValue()); } diff --git a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java index 68f0012..751761c 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveAllTasksServiceTest.java @@ -1,31 +1,67 @@ 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 java.time.LocalDate; +import java.util.List; +import java.util.UUID; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class RetrieveAllTasksServiceTest { - private IRetrieveAllTasksService retrieveAllTasksService; + @InjectMocks + private RetrieveAllTasksService retrieveAllTasksService; + + @Mock + private ITaskRepository taskRepository; @Before public void init() { - final ITaskRepository taskRepository = new TaskRepository(); + MockitoAnnotations.openMocks(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. - */ + + Task task1 = new Task( + UUID.fromString("0402220c-6ba9-4e49-b7fb-42c443912db1"), + "Estudar sobre KIKAHA", + "Devo estudar a documentacao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Task task2 = new Task( + UUID.fromString("720981f8-19f9-4f13-815c-21e4294bed30"), + "Estudar sobre Mockito", + "Devo assistir 3 aulas", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + Task task3 = new Task(UUID.fromString("85d519e9-0c94-4524-81b3-1b4795347c50"), + "Completar o projeto Sizebay", + "Devo realizar os teste unitarios", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + List tasksExpect = List.of(task1, task2, task3); + + Mockito.when(taskRepository.show()).thenReturn(tasksExpect); + Assert.assertEquals(retrieveAllTasksService.execute(), tasksExpect); + Mockito.verify(taskRepository, Mockito.times(1)).show(); + } } \ 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..f6fde0f 100644 --- a/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java +++ b/tests/backend/challenge/modules/task/services/RetrieveTaskByIdServiceTest.java @@ -1,24 +1,62 @@ package backend.challenge.modules.task.services; - +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exceptions.TaskNotFound; +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 static org.mockito.Mockito.times; +import java.time.LocalDate; +import java.util.Optional; +import java.util.UUID; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class RetrieveTaskByIdServiceTest { - private IRetrieveTaskByIdService retrieveTaskByIdService; + @InjectMocks + private RetrieveTaskByIdService retrieveTaskByIdService; + + @Mock + private ITaskRepository taskRepository; + + @Before + public void init() { + MockitoAnnotations.openMocks(this); + } @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. - */ + UUID id = UUID.fromString("e5102dbd-c9f1-47ce-8bc0-a761dc620e13"); + + Task taskExpect = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(taskExpect)); + + Assert.assertEquals(taskExpect, retrieveTaskByIdService.execute(id)); + + } + @Test + public void shouldThrowExceptionWhenTaskNotFoundById(){ + + UUID id = UUID.fromString("e5102dbd-c9f1-47ce-8bc0-a761dc620e13"); + Mockito.when(taskRepository.index(id)).thenReturn(Optional.empty()); + + Assert.assertThrows(TaskNotFound.class,() -> retrieveTaskByIdService.execute(id)); + Mockito.verify(taskRepository, times(1)).index(id); } } diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java index e6b0bf6..8b9a273 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskProgressServiceTest.java @@ -1,27 +1,147 @@ package backend.challenge.modules.task.services; - import kikaha.core.test.KikahaRunner; +import static org.mockito.Mockito.doReturn; +import java.time.LocalDate; +import java.util.Optional; +import java.util.UUID; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import backend.challenge.modules.task.dtos.TaskProgressDTO; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exceptions.ProgressTaskUpdateNotAvailable; +import backend.challenge.modules.task.models.Task; +import backend.challenge.modules.task.repositories.ITaskRepository; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class UpdateTaskProgressServiceTest { + @InjectMocks + private UpdateTaskProgressService progressService; + + @Mock + private ITaskRepository taskRepository; + + private Task task; + private UUID id; + + @Before + public void init() { + MockitoAnnotations.openMocks(this); + + id = UUID.fromString("34fb74a0-682c-43c0-b228-1eca6da09150"); + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + } + @Test public void shouldBeAbleToUpdateTaskProgress() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas o campo `progress`. - */ + + int newProgress = 50; + TaskProgressDTO dto = TaskProgressDTO.create().setId(id).setProgress(newProgress); + + Task taskUpdated = new Task( + id, + task.getTitle(), + task.getDescription(), + newProgress, + task.getStatus(), + task.getCreatedAt() + + ); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + Assert.assertEquals(taskUpdated, progressService.execute(dto)); + } @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. - */ + + int newProgress = 110; + TaskProgressDTO progressDTO = TaskProgressDTO.create().setId(id).setProgress(newProgress); + + Assert.assertThrows(ProgressTaskUpdateNotAvailable.class, + () -> progressService.execute(progressDTO)); + + progressDTO.setProgress(-10); + Assert.assertThrows(ProgressTaskUpdateNotAvailable.class, + () -> progressService.execute(progressDTO)); + + } + + @Test + public void shouldChangeStatusIfCompleteProgress() { + int newProgress = 100; + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create().setId(id).setProgress(newProgress); + Task taskUpdated = new Task( + task.getId(), + task.getTitle(), + task.getDescription(), + newProgress, + task.getStatus(), + task.getCreatedAt()); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Task.class); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + doReturn(taskUpdated).when(taskRepository).update(task); + + this.progressService.execute(taskProgressDTO); + + Mockito.verify(taskRepository, Mockito.times(1)).index(id); + Mockito.verify(taskRepository).update(argumentCaptor.capture()); + + Task taskCaptured = argumentCaptor.getValue(); + + Assert.assertEquals( TaskStatus.COMPLETE, taskCaptured.getStatus()); + } + + @Test + public void shouldChangeStatusIfNotCompleteProgress() { + int newProgress = 50; + int oldProgress = 100; + Task task = new Task( + id, + "Algum titulo", + "Alguma descricao", + oldProgress, + TaskStatus.COMPLETE, + LocalDate.of(2024, 1, 1)); + + Task taskUpdated = new Task( + task.getId(), + task.getTitle(), + task.getDescription(), + newProgress, + TaskStatus.COMPLETE, + task.getCreatedAt()); + + TaskProgressDTO taskProgressDTO = TaskProgressDTO.create().setId(id).setProgress(newProgress); + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + doReturn(taskUpdated).when(taskRepository).update(task); + + this.progressService.execute(taskProgressDTO); + + Mockito.verify(taskRepository, Mockito.times(1)).index(id); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Task.class); + Mockito.verify(taskRepository).update(argumentCaptor.capture()); + + Task taskCaptured = argumentCaptor.getValue(); + + Assert.assertEquals(TaskStatus.PROGRESS, taskCaptured.getStatus()); } } diff --git a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java index 53a4082..be39638 100644 --- a/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java +++ b/tests/backend/challenge/modules/task/services/UpdateTaskServiceTest.java @@ -1,44 +1,186 @@ package backend.challenge.modules.task.services; +import backend.challenge.modules.task.enums.TaskStatus; +import backend.challenge.modules.task.exceptions.TaskAlterationNotAvailable; +import backend.challenge.modules.task.exceptions.TaskNotFound; +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 java.time.LocalDate; +import java.util.Optional; +import java.util.UUID; +import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -@RunWith( KikahaRunner.class ) +@RunWith(KikahaRunner.class) public class UpdateTaskServiceTest { - private IUpdateTaskService updateTaskService; + @InjectMocks + private UpdateTaskService updateTaskService; + + @Mock + private ITaskRepository taskRepository; + + private Task task; + private UUID id; + + @Before + public void init() { + MockitoAnnotations.openMocks(this); + } @Test - public void shouldBeAbleToUpdateTask() { - /* - TODO: Para que esse teste passe, sua aplicação deve permitir que sejam - alterados apenas os campos `title` e `observation`. - */ + public void shouldBeAbleToUpdateTaskIfTitleModified() { + + id = UUID.fromString("ba27ee3a-5e41-4a87-b0fc-13e01e31cb0f"); + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + + Task taskToUpdate = new Task( + id, + task.getTitle(), + task.getDescription(), + task.getProgress(), + task.getStatus(), + task.getCreatedAt()); + taskToUpdate.setTitle("Outro titulo"); + Assert.assertEquals( + taskToUpdate, + taskToUpdate); + } @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. - */ + public void shouldBeAbleToUpdateTaskIfDescriptionModified() { + id = UUID.fromString("ba27ee3a-5e41-4a87-b0fc-13e01e31cb0f"); + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + + Task taskToUpdate = new Task( + id, + task.getTitle(), + task.getDescription(), + task.getProgress(), + task.getStatus(), + task.getCreatedAt()); + taskToUpdate.setDescription("Outra descricao"); + Assert.assertEquals( + taskToUpdate, + taskToUpdate); + } @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. + id = UUID.fromString("ba27ee3a-5e41-4a87-b0fc-13e01e31cb0f"); + TaskStatus newStatus = TaskStatus.COMPLETE; + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + + Task taskToUpdate = new Task( + id, + task.getTitle(), + task.getDescription(), + task.getProgress(), + newStatus, + task.getCreatedAt()); + + Task taskUpdated = updateTaskService.execute(taskToUpdate); + Assert.assertNotEquals(newStatus, taskUpdated.getStatus()); + Assert.assertEquals(task.getStatus(), taskUpdated.getStatus()); + + } + + @Test + public void shouldNotBeAbleToUpdateTaskProgressManually() { + id = UUID.fromString("ba27ee3a-5e41-4a87-b0fc-13e01e31cb0f"); + int newProgress = 50; + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + + Task taskToUpdate = new Task( + id, + task.getTitle(), + task.getDescription(), + newProgress, + task.getStatus(), + task.getCreatedAt()); + + Task taskUpdated = updateTaskService.execute(taskToUpdate); + Assert.assertEquals(task.getProgress(), taskUpdated.getProgress()); + Assert.assertNotEquals(newProgress, taskUpdated.getProgress()); + + } + @Test + public void shouldNotBeAbleToUpdateTaskCreatAtManually() { + id = UUID.fromString("ba27ee3a-5e41-4a87-b0fc-13e01e31cb0f"); + LocalDate newCreatAt = LocalDate.of(2023, 2, 3); + task = new Task( + id, + "Algum titulo", + "Alguma descricao", + 0, + TaskStatus.PROGRESS, + LocalDate.of(2024, 4, 30)); + + Mockito.when(taskRepository.index(id)).thenReturn(Optional.of(task)); + + Task taskToUpdate = new Task( + id, + task.getTitle(), + task.getDescription(), + task.getProgress(), + task.getStatus(), + newCreatAt); + + Task taskUpdated = updateTaskService.execute(taskToUpdate); + Assert.assertEquals(task.getCreatedAt(), taskUpdated.getCreatedAt()); + Assert.assertNotEquals(newCreatAt, taskUpdated.getCreatedAt()); - */ } + @Test + public void shouldNotBeAbleToUpdateATaskThatDoesNotExist() { + UUID id = UUID.fromString("aa0fde29-d40a-43f7-877d-a3d351891b7b"); + Task task = new Task(); + task.setId(id); + Mockito.doReturn(Optional.empty()).when(taskRepository).index(id); + Assert.assertThrows(TaskNotFound.class, () -> updateTaskService.execute(task)); + + } } \ No newline at end of file