feat: add task points

This commit is contained in:
Urban Modig
2026-07-26 17:43:07 +02:00
parent 1654b54a22
commit 059d4da921
16 changed files with 263 additions and 49 deletions

View File

@ -9,6 +9,7 @@ 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.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@ -41,7 +42,8 @@ class TaskApiTest {
.content("""
{
"title": " Dammsuga ",
"description": " Bottenvåningen "
"description": " Bottenvåningen ",
"points": 7
}
"""))
.andExpect(status().isCreated())
@ -49,7 +51,12 @@ class TaskApiTest {
.andExpect(jsonPath("$.title").value("Dammsuga"))
.andExpect(jsonPath("$.description").value("Bottenvåningen"))
.andExpect(jsonPath("$.status").value("WAITING"))
.andExpect(jsonPath("$.points").value(7))
.andExpect(jsonPath("$.createdAt").isString());
mockMvc.perform(get("/api/tasks"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].points").value(7));
}
@Test
@ -57,7 +64,7 @@ class TaskApiTest {
mockMvc.perform(post("/api/tasks")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"title": "Dammsuga", "description": " "}
{"title": "Dammsuga", "description": " ", "points": 1}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.description").value((Object) null));
@ -66,18 +73,58 @@ class TaskApiTest {
@Test
void rejectsBlankAndTooLongTitles() throws Exception {
assertInvalidTask("""
{"title": " "}
{"title": " ", "points": 1}
""");
assertInvalidTask("{\"title\": \"%s\"}".formatted("a".repeat(101)));
assertInvalidTask(
"{\"title\": \"%s\", \"points\": 1}".formatted("a".repeat(101)));
}
@Test
void rejectsTooLongDescription() throws Exception {
assertInvalidTask("""
{"title": "Dammsuga", "description": "%s"}
{"title": "Dammsuga", "description": "%s", "points": 1}
""".formatted("a".repeat(501)));
}
@Test
void acceptsPointBoundaries() throws Exception {
createTaskWithPoints(1)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.points").value(1));
createTaskWithPoints(99)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.points").value(99));
}
@Test
void rejectsMissingNullAndOutOfRangePoints() throws Exception {
assertInvalidTask("""
{"title": "Saknas"}
""");
assertInvalidTask("""
{"title": "Null", "points": null}
""");
assertInvalidTask("""
{"title": "Noll", "points": 0}
""");
assertInvalidTask("""
{"title": "Negativ", "points": -1}
""");
assertInvalidTask("""
{"title": "För stor", "points": 100}
""");
}
@Test
void rejectsNonIntegerPoints() throws Exception {
assertInvalidTask("""
{"title": "Decimal", "points": 1.5}
""");
assertInvalidTask("""
{"title": "Text", "points": "sju"}
""");
}
@Test
void listsTasksOldestFirstWithIdAsTieBreaker() throws Exception {
Instant older = Instant.parse("2026-07-24T10:00:00Z");
@ -86,17 +133,25 @@ 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, newer));
taskRepository.save(new Task(secondId, "Andra", null, TaskStatus.IN_PROGRESS, older));
taskRepository.save(new Task(firstId, "Första", null, TaskStatus.COMPLETED, older));
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));
mockMvc.perform(get("/api/tasks"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title").value("Första"))
.andExpect(jsonPath("$[0].points").value(1))
.andExpect(jsonPath("$[1].title").value("Andra"))
.andExpect(jsonPath("$[2].title").value("Nyast"));
}
private ResultActions createTaskWithPoints(int points) throws Exception {
return mockMvc.perform(post("/api/tasks")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"title\": \"Uppgift %d\", \"points\": %d}"
.formatted(points, points)));
}
private void assertInvalidTask(String body) throws Exception {
mockMvc.perform(post("/api/tasks")
.contentType(MediaType.APPLICATION_JSON)

View File

@ -0,0 +1,27 @@
package se.rubble.hemhub.task;
import java.time.Instant;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
class TaskTest {
@Test
void rejectsPointsOutsideAllowedRange() {
assertThrows(InvalidTaskException.class, () -> taskWithPoints(0));
assertThrows(InvalidTaskException.class, () -> taskWithPoints(100));
}
private Task taskWithPoints(int points) {
return new Task(
UUID.randomUUID(),
"Dammsuga",
null,
TaskStatus.WAITING,
points,
Instant.parse("2026-07-26T12:00:00Z"));
}
}