feat: add task assignment
This commit is contained in:
@ -15,6 +15,7 @@ import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
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.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@ -52,6 +53,7 @@ class TaskApiTest {
|
||||
.andExpect(jsonPath("$.description").value("Bottenvåningen"))
|
||||
.andExpect(jsonPath("$.status").value("WAITING"))
|
||||
.andExpect(jsonPath("$.points").value(7))
|
||||
.andExpect(jsonPath("$.assignee").value((Object) null))
|
||||
.andExpect(jsonPath("$.createdAt").isString());
|
||||
|
||||
mockMvc.perform(get("/api/tasks"))
|
||||
@ -59,6 +61,67 @@ class TaskApiTest {
|
||||
.andExpect(jsonPath("$[0].points").value(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsUnassignedTaskWhenAssigneeIsExplicitlyNull() throws Exception {
|
||||
mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"title": "Dammsuga", "points": 1, "assigneeId": null}
|
||||
"""))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.assignee").value((Object) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsAndListsTaskWithAssignee() throws Exception {
|
||||
UUID userId = createUser("Anna");
|
||||
|
||||
mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"title": "Dammsuga",
|
||||
"points": 7,
|
||||
"assigneeId": "%s"
|
||||
}
|
||||
""".formatted(userId)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.assignee.id").value(userId.toString()))
|
||||
.andExpect(jsonPath("$.assignee.name").value("Anna"));
|
||||
|
||||
mockMvc.perform(get("/api/tasks"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].assignee.id").value(userId.toString()))
|
||||
.andExpect(jsonPath("$[0].assignee.name").value("Anna"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnknownAssigneeWhenCreatingTask() throws Exception {
|
||||
mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"title": "Dammsuga",
|
||||
"points": 7,
|
||||
"assigneeId": "00000000-0000-0000-0000-000000000099"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("USER_NOT_FOUND"));
|
||||
|
||||
mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"title": "Dammsuga",
|
||||
"points": 7,
|
||||
"assigneeId": "inte-ett-uuid"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_TASK_ASSIGNMENT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void storesBlankDescriptionAsNull() throws Exception {
|
||||
mockMvc.perform(post("/api/tasks")
|
||||
@ -133,9 +196,12 @@ class TaskApiTest {
|
||||
UUID secondId = UUID.fromString("00000000-0000-0000-0000-000000000002");
|
||||
UUID newestId = UUID.fromString("00000000-0000-0000-0000-000000000003");
|
||||
|
||||
taskRepository.save(new Task(newestId, "Nyast", null, TaskStatus.WAITING, 3, newer));
|
||||
taskRepository.save(new Task(secondId, "Andra", null, TaskStatus.IN_PROGRESS, 2, older));
|
||||
taskRepository.save(new Task(firstId, "Första", null, TaskStatus.COMPLETED, 1, older));
|
||||
taskRepository.save(new Task(
|
||||
newestId, "Nyast", null, TaskStatus.WAITING, 3, null, newer));
|
||||
taskRepository.save(new Task(
|
||||
secondId, "Andra", null, TaskStatus.IN_PROGRESS, 2, null, older));
|
||||
taskRepository.save(new Task(
|
||||
firstId, "Första", null, TaskStatus.COMPLETED, 1, null, older));
|
||||
|
||||
mockMvc.perform(get("/api/tasks"))
|
||||
.andExpect(status().isOk())
|
||||
@ -145,6 +211,93 @@ class TaskApiTest {
|
||||
.andExpect(jsonPath("$[2].title").value("Nyast"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignsChangesAndRemovesAssigneeWithoutChangingOtherTaskFields() throws Exception {
|
||||
UUID firstUserId = createUser("Bo");
|
||||
UUID secondUserId = createUser("Cecilia");
|
||||
String taskId = createTask("Dammsuga", "Bottenvåningen", 7, null);
|
||||
|
||||
updateAssignee(taskId, """
|
||||
{"assigneeId": "%s"}
|
||||
""".formatted(firstUserId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.assignee.id").value(firstUserId.toString()))
|
||||
.andExpect(jsonPath("$.status").value("WAITING"))
|
||||
.andExpect(jsonPath("$.title").value("Dammsuga"))
|
||||
.andExpect(jsonPath("$.description").value("Bottenvåningen"))
|
||||
.andExpect(jsonPath("$.points").value(7));
|
||||
|
||||
updateAssignee(taskId, """
|
||||
{"assigneeId": "%s"}
|
||||
""".formatted(secondUserId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.assignee.id").value(secondUserId.toString()));
|
||||
|
||||
updateAssignee(taskId, """
|
||||
{"assigneeId": null}
|
||||
""")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.assignee").value((Object) null))
|
||||
.andExpect(jsonPath("$.status").value("WAITING"))
|
||||
.andExpect(jsonPath("$.title").value("Dammsuga"))
|
||||
.andExpect(jsonPath("$.description").value("Bottenvåningen"))
|
||||
.andExpect(jsonPath("$.points").value(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMissingAssigneeFieldAndInvalidUuid() throws Exception {
|
||||
String taskId = createTask("Dammsuga", null, 1, null);
|
||||
|
||||
updateAssignee(taskId, "{}")
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_TASK_ASSIGNMENT"));
|
||||
updateAssignee(taskId, """
|
||||
{"assigneeId": "inte-ett-uuid"}
|
||||
""")
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_TASK_ASSIGNMENT"));
|
||||
updateAssignee("inte-ett-uuid", """
|
||||
{"assigneeId": null}
|
||||
""")
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_TASK_ASSIGNMENT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNotFoundForUnknownTaskAndUnknownAssignee() throws Exception {
|
||||
String taskId = createTask("Dammsuga", null, 1, null);
|
||||
|
||||
updateAssignee("00000000-0000-0000-0000-000000000099", """
|
||||
{"assigneeId": null}
|
||||
""")
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("TASK_NOT_FOUND"));
|
||||
updateAssignee(taskId, """
|
||||
{"assigneeId": "00000000-0000-0000-0000-000000000099"}
|
||||
""")
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("USER_NOT_FOUND"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAssigneeChangeForTaskThatIsNotWaiting() throws Exception {
|
||||
UUID taskId = UUID.randomUUID();
|
||||
taskRepository.save(new Task(
|
||||
taskId,
|
||||
"Pågående",
|
||||
null,
|
||||
TaskStatus.IN_PROGRESS,
|
||||
3,
|
||||
null,
|
||||
Instant.parse("2026-07-26T12:00:00Z")));
|
||||
|
||||
updateAssignee(taskId.toString(), """
|
||||
{"assigneeId": null}
|
||||
""")
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(jsonPath("$.code").value("TASK_ASSIGNMENT_NOT_ALLOWED"));
|
||||
}
|
||||
|
||||
private ResultActions createTaskWithPoints(int points) throws Exception {
|
||||
return mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
@ -159,4 +312,50 @@ class TaskApiTest {
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("INVALID_TASK"));
|
||||
}
|
||||
|
||||
private UUID createUser(String name) throws Exception {
|
||||
String response = mockMvc.perform(post("/api/users")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"name": "%s"}
|
||||
""".formatted(name)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
|
||||
String id = com.jayway.jsonpath.JsonPath.read(response, "$.id");
|
||||
return UUID.fromString(id);
|
||||
}
|
||||
|
||||
private String createTask(
|
||||
String title,
|
||||
String description,
|
||||
int points,
|
||||
UUID assigneeId) throws Exception {
|
||||
String descriptionJson = description == null ? "null" : "\"%s\"".formatted(description);
|
||||
String assigneeJson = assigneeId == null ? "null" : "\"%s\"".formatted(assigneeId);
|
||||
String response = mockMvc.perform(post("/api/tasks")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"title": "%s",
|
||||
"description": %s,
|
||||
"points": %d,
|
||||
"assigneeId": %s
|
||||
}
|
||||
""".formatted(title, descriptionJson, points, assigneeJson)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
|
||||
return com.jayway.jsonpath.JsonPath.read(response, "$.id");
|
||||
}
|
||||
|
||||
private ResultActions updateAssignee(String taskId, String body) throws Exception {
|
||||
return mockMvc.perform(put("/api/tasks/{taskId}/assignee", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body));
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ class TaskTest {
|
||||
null,
|
||||
TaskStatus.WAITING,
|
||||
points,
|
||||
null,
|
||||
Instant.parse("2026-07-26T12:00:00Z"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user