From fe981336c4578d61c73bf16e4b79da344f48afe1 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 26 Apr 2026 00:28:57 +0200 Subject: [PATCH] fix(persons): trim title server-side and add SKIP controller test - PersonController trims title (both create + update) matching the existing firstName/lastName trim pattern - PersonControllerTest: verifies title is trimmed before service call (ArgumentCaptor) - PersonControllerTest: verifies createPerson returns 400 when personType is SKIP Co-Authored-By: Claude Sonnet 4.6 --- .../controller/PersonController.java | 2 ++ .../controller/PersonControllerTest.java | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java b/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java index de2b7498..329a1969 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java @@ -66,6 +66,7 @@ public class PersonController { validatePersonNames(dto); if (dto.getFirstName() != null) dto.setFirstName(dto.getFirstName().trim()); dto.setLastName(dto.getLastName().trim()); + if (dto.getTitle() != null) dto.setTitle(dto.getTitle().trim()); return ResponseEntity.ok(personService.createPerson(dto)); } @@ -75,6 +76,7 @@ public class PersonController { validatePersonNames(dto); if (dto.getFirstName() != null) dto.setFirstName(dto.getFirstName().trim()); dto.setLastName(dto.getLastName().trim()); + if (dto.getTitle() != null) dto.setTitle(dto.getTitle().trim()); return ResponseEntity.ok(personService.updatePerson(id, dto)); } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java index 34a92dbf..3e73a6ae 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java @@ -1,6 +1,9 @@ package org.raddatz.familienarchiv.controller; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.raddatz.familienarchiv.exception.DomainException; +import org.raddatz.familienarchiv.exception.ErrorCode; import org.raddatz.familienarchiv.model.Document; import org.raddatz.familienarchiv.model.Person; import org.raddatz.familienarchiv.model.PersonNameAlias; @@ -25,6 +28,7 @@ import java.util.UUID; import org.raddatz.familienarchiv.dto.PersonSummaryDTO; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; @@ -243,6 +247,34 @@ class PersonControllerTest { .andExpect(jsonPath("$.lastName").value("Verlag GmbH")); } + @Test + @WithMockUser(authorities = "WRITE_ALL") + void createPerson_trimsTitle_beforePersisting() throws Exception { + ArgumentCaptor captor = + ArgumentCaptor.forClass(org.raddatz.familienarchiv.dto.PersonUpdateDTO.class); + Person saved = Person.builder().id(UUID.randomUUID()).firstName("Hans").lastName("Müller").build(); + when(personService.createPerson(captor.capture())).thenReturn(saved); + + mockMvc.perform(post("/api/persons") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"title\":\" Prof. \",\"personType\":\"PERSON\"}")) + .andExpect(status().isOk()); + + assertThat(captor.getValue().getTitle()).isEqualTo("Prof."); + } + + @Test + @WithMockUser(authorities = "WRITE_ALL") + void createPerson_returns400_whenPersonTypeIsSkip() throws Exception { + when(personService.createPerson(any())).thenThrow( + DomainException.badRequest(ErrorCode.INVALID_PERSON_TYPE, "SKIP is not a valid person type")); + + mockMvc.perform(post("/api/persons") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"lastName\":\"Müller\",\"personType\":\"SKIP\"}")) + .andExpect(status().isBadRequest()); + } + // ─── PUT /api/persons/{id} ──────────────────────────────────────────────── @Test