diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..edb5fb2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: Java CI with Maven + +on: + push: + branches: [ "main", "test" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build with Maven + run: mvn clean install + + - name: Run tests + run: mvn test \ No newline at end of file diff --git a/src/test/java/me/eddypbr/todolist/IntegrationTest.java b/src/test/java/me/eddypbr/todolist/IntegrationTest.java new file mode 100644 index 0000000..f0f4b86 --- /dev/null +++ b/src/test/java/me/eddypbr/todolist/IntegrationTest.java @@ -0,0 +1,123 @@ +package me.eddypbr.todolist; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.UUID; + +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +import me.eddypbr.todolist.user.UserModel; + +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class IntegrationTest { + + @Autowired + private MockMvc mockMvc; + + private static String createdTaskId; + private static final UUID FIXED_USER_ID = UUID.fromString("11111111-1111-1111-1111-111111111111"); + + private static RequestPostProcessor withMockUser() { + return request -> { + UserModel user = new UserModel(); + user.setId(FIXED_USER_ID); + user.setUsername("testuser"); + request.setAttribute("user", user); + return request; + }; + } + + @Test + @Order(1) + public void testCreateTask() throws Exception { + String taskJson = """ + { + "title": "Integration Task", + "description": "Test", + "startAt": "2026-04-20T10:00:00", + "endAt": "2026-04-21T10:00:00" + } + """; + + String response = mockMvc.perform(post("/tasks") + .with(withMockUser()) + .contentType(MediaType.APPLICATION_JSON) + .content(taskJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andReturn() + .getResponse() + .getContentAsString(); + + createdTaskId = com.jayway.jsonpath.JsonPath.read(response, "$.id"); + System.out.println("Created Task ID: " + createdTaskId); + } + + @Test + @Order(2) + public void testUpdateTask() throws Exception { + System.out.println("Created Task ID: " + createdTaskId); + String updateJson = """ + { + "title": "Updated Integration Task" + } + """; + + mockMvc.perform(put("/tasks/" + createdTaskId) + .with(withMockUser()) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("Updated Integration Task")); + } + + @Test + @Order(3) + public void testDeleteTask() throws Exception { + System.out.println("Deleting Task ID: " + createdTaskId); + + mockMvc.perform(delete("/tasks/" + createdTaskId) + .with(withMockUser())) + .andDo(result -> { + System.out.println("Delete Status: " + result.getResponse().getStatus()); + }) + .andExpect(status().isOk()); + } + + @Test + @Order(4) + public void testCreateTaskWithInvalidDate() throws Exception { + String invalidJson = """ + { + "title": "Invalid Task", + "startAt": "2026-04-21T10:00:00", + "endAt": "2026-04-20T10:00:00" + } + """; + + mockMvc.perform(post("/tasks") + .with(withMockUser()) + .contentType(MediaType.APPLICATION_JSON) + .content(invalidJson)) + .andExpect(status().isBadRequest()); + } + + @Test + @Order(5) + public void testDeleteNonExistingTask() throws Exception { + String invalidId = "00000000-0000-0000-0000-000000000000"; + + mockMvc.perform(delete("/tasks/" + invalidId) + .with(withMockUser())) + .andExpect(status().isNotFound()); + } +} \ No newline at end of file diff --git a/src/test/java/me/eddypbr/todolist/UpdateTask.java b/src/test/java/me/eddypbr/todolist/UpdateTask.java new file mode 100644 index 0000000..7083a72 --- /dev/null +++ b/src/test/java/me/eddypbr/todolist/UpdateTask.java @@ -0,0 +1,114 @@ +package me.eddypbr.todolist; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.*; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import jakarta.servlet.http.HttpServletRequest; + +import me.eddypbr.todolist.task.*; +import me.eddypbr.todolist.user.UserModel; + +public class UpdateTask { + + @InjectMocks + private TaskController controller; + + @Mock + private ITaskRepository repo; + + @Mock + private HttpServletRequest request; + + private UUID userId; + private UUID taskId; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + userId = UUID.randomUUID(); + taskId = UUID.randomUUID(); + } + + private UserModel mockUser() { + UserModel user = new UserModel(); + user.setId(userId); + return user; + } + + private TaskModel mockTask() { + TaskModel task = new TaskModel(); + task.setId(taskId); + task.setIdUser(userId); + return task; + } + + @ParameterizedTest + @CsvSource({"Updated Title 1, Updated Desc 1"}) + public void testUpdateTaskSuccess(String title, String description) throws Exception { + when(request.getAttribute("user")).thenReturn(mockUser()); + when(repo.findById(taskId)).thenReturn(Optional.of(mockTask())); + when(repo.save(any())).thenReturn(mockTask()); + TaskModel update = new TaskModel(); + update.setTitle(title); + update.setDescription(description); + ResponseEntity response = controller.update(update, request, taskId); + assertEquals(HttpStatus.OK, response.getStatusCode()); + verify(repo, times(1)).save(any()); + } + + @ParameterizedTest + @CsvSource({"00000000-0000-0000-0000-000000000001"}) + public void testUpdateTaskNotFound(String idStr) throws Exception { + UUID id = UUID.fromString(idStr); + when(repo.findById(id)).thenReturn(Optional.empty()); + ResponseEntity response = controller.update(new TaskModel(), request, id); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + void testUpdateTaskEmptyTitle() throws Exception { + when(request.getAttribute("user")).thenReturn(mockUser()); + when(repo.findById(taskId)).thenReturn(Optional.of(mockTask())); + TaskModel update = new TaskModel(); + update.setTitle(""); + update.setDescription("desc"); + ResponseEntity response = controller.update(update, request, taskId); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + void testUpdateTaskNullDescription() throws Exception { + when(request.getAttribute("user")).thenReturn(mockUser()); + when(repo.findById(taskId)).thenReturn(Optional.of(mockTask())); + TaskModel update = new TaskModel(); + update.setTitle("Valid Title"); + update.setDescription(null); + ResponseEntity response = controller.update(update, request, taskId); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testUpdateTaskUnauthorizedUser() throws Exception { + UUID otherUserId = UUID.randomUUID(); + TaskModel existing = new TaskModel(); + existing.setId(taskId); + existing.setIdUser(otherUserId); + when(request.getAttribute("user")).thenReturn(mockUser()); + when(repo.findById(taskId)).thenReturn(Optional.of(existing)); + TaskModel update = new TaskModel(); + update.setTitle("Test"); + ResponseEntity response = controller.update(update, request, taskId); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } +} \ No newline at end of file