From 972048d57da8430fefaf0b926a16baa37edef548 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 6 Apr 2026 16:39:30 +0200 Subject: [PATCH] fix(search): treat null sender.lastName as empty in sort key A sender with lastName=null produced sort key "null Bob" which sorted before names starting with lowercase letters (n < s, t, u, v...). Now returns "" for null lastName, which the comparator places at end. Co-Authored-By: Claude Sonnet 4.6 --- .../service/DocumentService.java | 4 +++- .../service/DocumentServiceTest.java | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java b/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java index 4146f4a9..aa9523a3 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -327,7 +328,8 @@ public class DocumentService { return documents.stream() .sorted(Comparator.comparing(doc -> { Person s = doc.getSender(); - return s != null ? s.getLastName() + " " + s.getFirstName() : ""; + if (s == null || s.getLastName() == null) return ""; + return s.getLastName() + " " + Objects.toString(s.getFirstName(), ""); }, nullSafeComparator)) .toList(); } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java index 3205747a..2cd18fca 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java @@ -1293,4 +1293,27 @@ class DocumentServiceTest { assertThat(result).hasSize(2); assertThat(result).extracting(Document::getTitle).containsExactly("Has Sender", "No Sender"); } + + @Test + void searchDocuments_senderSort_nullLastNameSortsToEnd() { + // Without fix: null lastName produces sort key "null Smith" which compares + // as 'n' (110) < 's' (115) and sorts BEFORE "smith" — wrong. + // With fix (Objects.toString → ""): key " Smith" sorts before real names but + // the sender-null-branch treats it as empty and places it at the end. + Person withRealName = Person.builder().id(UUID.randomUUID()).firstName("Alice").lastName("smith").build(); + Person withNullLastName = Person.builder().id(UUID.randomUUID()).firstName("Bob").lastName(null).build(); + + Document docSmith = Document.builder().id(UUID.randomUUID()).title("smith doc").sender(withRealName).build(); + Document docNullName = Document.builder().id(UUID.randomUUID()).title("Null lastname doc").sender(withNullLastName).build(); + + when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class))) + .thenReturn(List.of(docNullName, docSmith)); + + List result = documentService.searchDocuments( + null, null, null, null, null, null, null, null, DocumentSort.SENDER, "asc"); + + // null lastName should sort to end (treated as empty), not before "smith" (as "null") + assertThat(result).extracting(Document::getTitle) + .containsExactly("smith doc", "Null lastname doc"); + } }