feat: add task deletion

This commit is contained in:
Urban Modig
2026-07-27 21:26:19 +02:00
parent 5dea4c4027
commit f296d15446
10 changed files with 1009 additions and 19 deletions

View File

@ -4,6 +4,7 @@ import java.util.List;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@ -65,4 +66,10 @@ public class TaskController {
request.parsedStatus(),
request.parsedActiveUserId().value());
}
@DeleteMapping("/{taskId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable UUID taskId) {
taskService.delete(taskId);
}
}

View File

@ -104,6 +104,13 @@ class TaskService {
return TaskResponse.from(task);
}
@Transactional
void delete(UUID taskId) {
Task task = taskRepository.findById(taskId)
.orElseThrow(TaskNotFoundException::new);
taskRepository.delete(task);
}
private User findAssignee(UUID requestedAssigneeId) {
if (requestedAssigneeId == null) {
return null;

View File

@ -0,0 +1,128 @@
package se.rubble.hemhub.task;
import java.time.Instant;
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.EnumSource;
import org.springframework.beans.factory.annotation.Autowired;
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.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import se.rubble.hemhub.user.User;
import se.rubble.hemhub.user.UserRepository;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
class TaskDeletionApiTest {
@Autowired
private WebApplicationContext context;
@Autowired
private TaskRepository taskRepository;
@Autowired
private UserRepository userRepository;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
taskRepository.deleteAll();
userRepository.deleteAll();
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@ParameterizedTest
@EnumSource(TaskStatus.class)
void deletesTaskInEveryStatus(TaskStatus statusValue) throws Exception {
User assignee = createUser();
Task task = saveTask(statusValue, assignee);
mockMvc.perform(delete("/api/tasks/{taskId}", task.getId()))
.andExpect(status().isNoContent())
.andExpect(content().string(""));
mockMvc.perform(get("/api/tasks"))
.andExpect(status().isOk())
.andExpect(jsonPath("$").isEmpty());
org.junit.jupiter.api.Assertions.assertTrue(userRepository.existsById(assignee.getId()));
}
@Test
void deletesOnlyRequestedTask() throws Exception {
User assignee = createUser();
Task deleted = saveTask(TaskStatus.WAITING, assignee);
Task remaining = saveTask(TaskStatus.COMPLETED, null);
mockMvc.perform(delete("/api/tasks/{taskId}", deleted.getId()))
.andExpect(status().isNoContent());
mockMvc.perform(get("/api/tasks"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(1))
.andExpect(jsonPath("$[0].id").value(remaining.getId().toString()))
.andExpect(jsonPath("$[0].status").value("COMPLETED"));
org.junit.jupiter.api.Assertions.assertTrue(userRepository.existsById(assignee.getId()));
}
@Test
void returnsNotFoundForUnknownAndAlreadyDeletedTask() throws Exception {
Task task = saveTask(TaskStatus.WAITING, null);
mockMvc.perform(delete("/api/tasks/{taskId}", task.getId()))
.andExpect(status().isNoContent());
mockMvc.perform(delete("/api/tasks/{taskId}", task.getId()))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.code").value("TASK_NOT_FOUND"));
mockMvc.perform(delete(
"/api/tasks/{taskId}",
"00000000-0000-0000-0000-000000000099"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.code").value("TASK_NOT_FOUND"));
}
@Test
void keepsExistingBadRequestForInvalidUuid() throws Exception {
mockMvc.perform(delete("/api/tasks/{taskId}", "inte-ett-uuid"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value("INVALID_TASK_ASSIGNMENT"));
}
private User createUser() throws Exception {
String response = mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"name": "Urban"}
"""))
.andExpect(status().isCreated())
.andReturn()
.getResponse()
.getContentAsString();
String id = com.jayway.jsonpath.JsonPath.read(response, "$.id");
return userRepository.findById(UUID.fromString(id)).orElseThrow();
}
private Task saveTask(TaskStatus statusValue, User assignee) {
return taskRepository.save(new Task(
UUID.randomUUID(),
"Dammsuga",
"Bottenvåningen",
statusValue,
7,
assignee,
Instant.parse("2026-07-28T09:00:00Z")));
}
}