feat(library): PATCH backlog saves only the sent fields

This commit is contained in:
lobo committed 2026-09-30 07:36:08 +02:00
1 parent 73cfcb58e2
commit 79d7bf17f2
8 files changed
+156 -2

No files matched your search

@@ -48,6 +48,13 @@ class ClientHeaderFilterTest extends AbstractIntegrationTest {
.andExpect(header().string("Access-Control-Allow-Origin", FRONTEND))
.andExpect(header().string("Access-Control-Allow-Credentials", "true"));
mockMvc.perform(options("/api/library/games/220/backlog")
.header("Origin", FRONTEND)
.header("Access-Control-Request-Method", "PATCH")
.header("Access-Control-Request-Headers", "x-tbp-client,content-type"))
.andExpect(status().isOk())
.andExpect(header().string("Access-Control-Allow-Origin", FRONTEND));
mockMvc.perform(options("/api/library/sync")
.header("Origin", "https://evil.andreas-dahm.eu")
.header("Access-Control-Request-Method", "POST"))
@@ -17,11 +17,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import java.time.Instant;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
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;
@@ -138,6 +142,87 @@ class LibraryControllerBacklogTest extends AbstractIntegrationTest {
.andExpect(status().isNotFound());
}
private ResultActions patchBacklog(long appid, String body) throws Exception {
return mockMvc.perform(patch("/api/library/games/" + appid + "/backlog").header(ClientHeaderFilter.CLIENT_HEADER, "test")
.contentType(MediaType.APPLICATION_JSON).content(body));
}
private void storeAllFields() {
UserGame game = stored();
game.setRating(4);
game.setNotes("old notes");
game.setPriority(GamePriority.HIGH);
userGameRepository.save(game);
}
@Test
void patchBacklog_OnlyNotes_KeepsTheOtherFieldsAndTheAutomaticStatus() throws Exception {
storeAllFields();
patchBacklog(APPID, "{\"notes\":\"new notes\"}")
.andExpect(status().isOk())
.andExpect(jsonPath("$.notes").value("new notes"))
.andExpect(jsonPath("$.rating").value(4));
UserGame game = stored();
assertEquals("new notes", game.getNotes());
assertEquals(4, game.getRating());
assertEquals(GamePriority.HIGH, game.getPriority());
assertEquals(GameStatus.BACKLOG, game.getStatus());
assertFalse(game.isStatusSetByUser());
}
@Test
void patchBacklog_Status_IsSetByTheUser() throws Exception {
patchBacklog(APPID, "{\"status\":\"PLAYING\"}").andExpect(status().isOk());
assertEquals(GameStatus.PLAYING, stored().getStatus());
assertTrue(stored().isStatusSetByUser());
}
@Test
void patchBacklog_NullClearsAnOptionalFieldAndBlankNotesAreNull() throws Exception {
storeAllFields();
patchBacklog(APPID, "{\"rating\":null,\"priority\":null,\"notes\":\" \"}").andExpect(status().isOk());
UserGame game = stored();
assertNull(game.getRating());
assertNull(game.getPriority());
assertNull(game.getNotes());
}
@ParameterizedTest
@ValueSource(strings = {
"{\"status\":null}",
"{\"status\":\"WISHLIST\"}",
"{\"rating\":0}",
"{\"rating\":6}",
"{\"rating\":4.7}",
"{\"priority\":\"URGENT\"}"})
void patchBacklog_InvalidBody_Is400AndStoresNothing(String body) throws Exception {
storeAllFields();
patchBacklog(APPID, body).andExpect(status().isBadRequest());
UserGame game = stored();
assertEquals(GameStatus.BACKLOG, game.getStatus());
assertEquals(4, game.getRating());
assertEquals(GamePriority.HIGH, game.getPriority());
}
@Test
void patchBacklog_NotesTooLong_Is400() throws Exception {
patchBacklog(APPID, "{\"notes\":\"" + "x".repeat(2001) + "\"}").andExpect(status().isBadRequest());
}
@Test
void patchBacklog_GameNotOwned_Is404() throws Exception {
patchBacklog(999L, "{\"notes\":\"x\"}")
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.code").value("GAME_NOT_FOUND"));
}
@Test
void updateStatus_KeepsRatingNotesAndPriority() throws Exception {
UserGame game = stored();