Compare commits
3 Commits
dcb57ffacd
...
3b594c0b0b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b594c0b0b | ||
|
|
2e44cab614 | ||
|
|
4c2f036de0 |
@@ -167,6 +167,9 @@ public class DocumentService {
|
||||
/** Loads matching documents and projects to non-null {@link LocalDate}s. */
|
||||
private List<LocalDate> loadFilteredDates(DensityFilters filters, List<UUID> ftsIds) {
|
||||
boolean hasFts = ftsIds != null;
|
||||
// Density and search keep separate filter records (DensityFilters has no
|
||||
// date/undated fields); adapt to SearchFilters here to reuse buildSearchSpec.
|
||||
// Date bounds stay null and undated=false — the density path never filters by date.
|
||||
SearchFilters searchFilters = new SearchFilters(
|
||||
filters.text(), null, null, filters.sender(), filters.receiver(),
|
||||
filters.tags(), filters.tagQ(), filters.status(), filters.tagOperator(), false);
|
||||
|
||||
@@ -1233,6 +1233,24 @@ class DocumentControllerTest {
|
||||
assertThat(filtersCaptor.getValue().sender()).isEqualTo(senderId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void getDocumentIds_withoutUndatedParam_coercesNullToFalse() throws Exception {
|
||||
// The controller coerces a null boxed Boolean to primitive false
|
||||
// (Boolean.TRUE.equals(undated)) so the absent param never NPEs and the
|
||||
// record always holds a concrete boolean.
|
||||
when(userService.findByEmail(any())).thenReturn(AppUser.builder().id(UUID.randomUUID()).build());
|
||||
ArgumentCaptor<SearchFilters> filtersCaptor = ArgumentCaptor.forClass(SearchFilters.class);
|
||||
when(documentService.findIdsForFilter(any()))
|
||||
.thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/documents/ids"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(documentService).findIdsForFilter(filtersCaptor.capture());
|
||||
assertThat(filtersCaptor.getValue().undated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void getDocumentIds_returns400_whenResultExceedsFilterCap() throws Exception {
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.raddatz.familienarchiv.document.SearchFiltersFixtures.noFilters;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -122,7 +123,7 @@ class DocumentLazyLoadingTest {
|
||||
savedDocument("SrDoc", "sr_doc.pdf", sender, Set.of(receiver), Set.of(tag));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.RECEIVER, "asc", PageRequest.of(0, 20));
|
||||
assertThat(result.totalElements()).isGreaterThan(0);
|
||||
assertThatCode(() ->
|
||||
@@ -137,7 +138,7 @@ class DocumentLazyLoadingTest {
|
||||
savedDocument("SsDoc", "ss_doc.pdf", sender, Set.of(), Set.of(tag));
|
||||
|
||||
assertThatCode(() -> documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "asc", PageRequest.of(0, 20)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.raddatz.familienarchiv.document.SearchFiltersFixtures.noFilters;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
/**
|
||||
@@ -55,7 +56,7 @@ class DocumentListItemIntegrationTest {
|
||||
.build());
|
||||
|
||||
assertThatCode(() -> documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
@@ -70,7 +71,7 @@ class DocumentListItemIntegrationTest {
|
||||
.build());
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
|
||||
assertThat(result.totalElements()).isGreaterThan(0);
|
||||
@@ -91,7 +92,7 @@ class DocumentListItemIntegrationTest {
|
||||
.build());
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
|
||||
DocumentListItem item = result.items().stream()
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.raddatz.familienarchiv.document.SearchFiltersFixtures.noFilters;
|
||||
|
||||
/**
|
||||
* End-to-end paged search test with real PostgreSQL (Testcontainers). Covers the
|
||||
@@ -61,7 +62,7 @@ class DocumentSearchPagedIntegrationTest {
|
||||
@Test
|
||||
void search_firstPage_returnsExactlyPageSizeItems_andCorrectTotalElements() {
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
|
||||
assertThat(result.items()).hasSize(50);
|
||||
@@ -74,7 +75,7 @@ class DocumentSearchPagedIntegrationTest {
|
||||
@Test
|
||||
void search_lastPartialPage_returnsRemainingItems() {
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(2, 50));
|
||||
|
||||
// Page 2 (offset 100) of 120 docs → exactly 20 items on the tail.
|
||||
@@ -86,7 +87,7 @@ class DocumentSearchPagedIntegrationTest {
|
||||
@Test
|
||||
void search_pageBeyondLast_returnsEmptyContent_totalElementsStillCorrect() {
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(99, 50));
|
||||
|
||||
assertThat(result.items()).isEmpty();
|
||||
@@ -99,7 +100,7 @@ class DocumentSearchPagedIntegrationTest {
|
||||
// comment in DocumentService). Proves that the in-memory slice path
|
||||
// returns the correct total from a real repository fetch.
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "asc", PageRequest.of(1, 50));
|
||||
|
||||
assertThat(result.items()).hasSize(50);
|
||||
@@ -125,7 +126,7 @@ class DocumentSearchPagedIntegrationTest {
|
||||
}
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
|
||||
// Global undated count is the full undated total, independent of page size.
|
||||
@@ -153,10 +154,10 @@ class DocumentSearchPagedIntegrationTest {
|
||||
}
|
||||
|
||||
DocumentSearchResult unfiltered = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
DocumentSearchResult undatedOnly = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, true),
|
||||
noFilters().withUndated(true),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
|
||||
assertThat(unfiltered.undatedCount()).isEqualTo(undatedTotal);
|
||||
@@ -188,10 +189,10 @@ class DocumentSearchPagedIntegrationTest {
|
||||
@Test
|
||||
void search_differentPagesReturnDisjointSlices() {
|
||||
DocumentSearchResult page0 = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||
DocumentSearchResult page1 = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", PageRequest.of(1, 50));
|
||||
|
||||
// No document id should appear on both pages — slicing must be exclusive.
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.raddatz.familienarchiv.document.SearchFiltersFixtures.noFilters;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -1442,7 +1443,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(1, 50));
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
||||
@@ -1456,7 +1457,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(3, 25));
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||
@@ -1473,7 +1474,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of(d), org.springframework.data.domain.PageRequest.of(0, 50), 120L));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(0, 50));
|
||||
|
||||
assertThat(result.totalElements()).isEqualTo(120L);
|
||||
@@ -1490,7 +1491,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(0, 5));
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||
@@ -1514,7 +1515,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.DATE, "ASC", org.springframework.data.domain.PageRequest.of(0, 5));
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||
@@ -1536,7 +1537,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.UPDATED_AT, "DESC", org.springframework.data.domain.PageRequest.of(0, 5));
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||
@@ -1561,7 +1562,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(all);
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", org.springframework.data.domain.PageRequest.of(1, 50));
|
||||
|
||||
assertThat(result.totalElements()).isEqualTo(120L);
|
||||
@@ -1586,7 +1587,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(all);
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", org.springframework.data.domain.PageRequest.of(10, 50));
|
||||
|
||||
assertThat(result.items()).isEmpty();
|
||||
@@ -1612,7 +1613,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false), null, null, UNPAGED);
|
||||
noFilters(), null, null, UNPAGED);
|
||||
|
||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
||||
}
|
||||
@@ -1690,7 +1691,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(withSender, noSender));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "asc", UNPAGED);
|
||||
|
||||
assertThat(result.items()).hasSize(2);
|
||||
@@ -1711,7 +1712,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(noReceivers, withReceiver));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.RECEIVER, "asc", UNPAGED);
|
||||
|
||||
assertThat(result.items()).extracting(DocumentListItem::title)
|
||||
@@ -1745,7 +1746,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "asc", UNPAGED);
|
||||
|
||||
// Bob's group precedes Anna's group (ASC by sender). The sort is stable, so
|
||||
@@ -1777,7 +1778,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "desc", UNPAGED);
|
||||
|
||||
// Anna's group precedes Bob's (DESC by sender); undated stays inside its group.
|
||||
@@ -1801,7 +1802,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(undatedFromAlice));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, true),
|
||||
noFilters().withUndated(true),
|
||||
DocumentSort.SENDER, "asc", UNPAGED);
|
||||
|
||||
// The in-memory path queried via a Specification (built by buildSearchSpec with
|
||||
@@ -1843,7 +1844,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(docNullName, docSmith));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
DocumentSort.SENDER, "asc", UNPAGED);
|
||||
|
||||
// null lastName should sort to end (treated as empty), not before "smith" (as "null")
|
||||
@@ -1882,7 +1883,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(new PageImpl<>(List.of()));
|
||||
|
||||
DocumentSearchResult result = documentService.searchDocuments(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||
noFilters(),
|
||||
null, null, UNPAGED);
|
||||
|
||||
assertThat(result.items()).isEmpty();
|
||||
@@ -2421,7 +2422,7 @@ class DocumentServiceTest {
|
||||
.thenReturn(List.of(d1, d2));
|
||||
|
||||
List<UUID> result = documentService.findIdsForFilter(
|
||||
new SearchFilters(null, null, null, null, null, null, null, null, null, false));
|
||||
noFilters());
|
||||
|
||||
assertThat(result).containsExactly(d1.getId(), d2.getId());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.raddatz.familienarchiv.document;
|
||||
|
||||
/** Test fixtures for {@link SearchFilters}. */
|
||||
final class SearchFiltersFixtures {
|
||||
|
||||
private SearchFiltersFixtures() {}
|
||||
|
||||
/**
|
||||
* A {@link SearchFilters} with no predicate active — the common search-test
|
||||
* baseline. Combine with {@code .withUndated(true)} for the undated-only case;
|
||||
* construct {@code new SearchFilters(...)} directly when a test pins a specific
|
||||
* field, so the intent stays visible at the call site.
|
||||
*/
|
||||
static SearchFilters noFilters() {
|
||||
return new SearchFilters(null, null, null, null, null, null, null, null, null, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user