refactor(document): thread SearchFilters through the search chain (#683)
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m21s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m26s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m3s
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m21s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m26s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m3s
Replace the long positional filter lists on the document search chain with the SearchFilters record. searchDocuments now takes (SearchFilters, DocumentSort, String dir, Pageable) and findIdsForFilter takes a single SearchFilters; the four private helpers (buildSearchSpec, runSearch, countUndatedForFilter, isPureTextRelevance) no longer carry a positional 10-field filter list. The controller builds the record after its existing tagOp/undated coercions; the density path adapts its DensityFilters into a SearchFilters at the shared buildSearchSpec call. The forced-undated count path is preserved via filters.withUndated(true), so countUndatedForFilter still ignores the user's toggle (#668) while runSearch honours it. No behaviour change. Controller binding tests swap their positional any()/eq() matchers for ArgumentCaptor<SearchFilters>, asserting captured.undated()/.status()/ .sender() — strictly stronger than the previous any()-soup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -316,7 +316,8 @@ public class DocumentController {
|
|||||||
@RequestParam(required = false) Boolean undated,
|
@RequestParam(required = false) Boolean undated,
|
||||||
Authentication authentication) {
|
Authentication authentication) {
|
||||||
TagOperator operator = "OR".equalsIgnoreCase(tagOp) ? TagOperator.OR : TagOperator.AND;
|
TagOperator operator = "OR".equalsIgnoreCase(tagOp) ? TagOperator.OR : TagOperator.AND;
|
||||||
List<UUID> ids = documentService.findIdsForFilter(q, from, to, senderId, receiverId, tags, tagQ, status, operator, Boolean.TRUE.equals(undated));
|
SearchFilters filters = new SearchFilters(q, from, to, senderId, receiverId, tags, tagQ, status, operator, Boolean.TRUE.equals(undated));
|
||||||
|
List<UUID> ids = documentService.findIdsForFilter(filters);
|
||||||
if (ids.size() > BULK_EDIT_FILTER_MAX_IDS) {
|
if (ids.size() > BULK_EDIT_FILTER_MAX_IDS) {
|
||||||
throw DomainException.badRequest(ErrorCode.BULK_EDIT_TOO_MANY_IDS,
|
throw DomainException.badRequest(ErrorCode.BULK_EDIT_TOO_MANY_IDS,
|
||||||
"Filter matches " + ids.size() + " documents — refine filter (max " + BULK_EDIT_FILTER_MAX_IDS + ")");
|
"Filter matches " + ids.size() + " documents — refine filter (max " + BULK_EDIT_FILTER_MAX_IDS + ")");
|
||||||
@@ -388,8 +389,9 @@ public class DocumentController {
|
|||||||
// tagOp is a raw String at the HTTP boundary; any value other than "OR" (case-insensitive)
|
// tagOp is a raw String at the HTTP boundary; any value other than "OR" (case-insensitive)
|
||||||
// defaults to AND, which matches the frontend default and keeps old clients working.
|
// defaults to AND, which matches the frontend default and keeps old clients working.
|
||||||
TagOperator operator = "OR".equalsIgnoreCase(tagOp) ? TagOperator.OR : TagOperator.AND;
|
TagOperator operator = "OR".equalsIgnoreCase(tagOp) ? TagOperator.OR : TagOperator.AND;
|
||||||
|
SearchFilters filters = new SearchFilters(q, from, to, senderId, receiverId, tags, tagQ, status, operator, Boolean.TRUE.equals(undated));
|
||||||
Pageable pageable = PageRequest.of(page, size);
|
Pageable pageable = PageRequest.of(page, size);
|
||||||
return ResponseEntity.ok(documentService.searchDocuments(q, from, to, senderId, receiverId, tags, tagQ, status, sort, dir, operator, Boolean.TRUE.equals(undated), pageable));
|
return ResponseEntity.ok(documentService.searchDocuments(filters, sort, dir, pageable));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/density", produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(value = "/density", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
|||||||
@@ -167,11 +167,10 @@ public class DocumentService {
|
|||||||
/** Loads matching documents and projects to non-null {@link LocalDate}s. */
|
/** Loads matching documents and projects to non-null {@link LocalDate}s. */
|
||||||
private List<LocalDate> loadFilteredDates(DensityFilters filters, List<UUID> ftsIds) {
|
private List<LocalDate> loadFilteredDates(DensityFilters filters, List<UUID> ftsIds) {
|
||||||
boolean hasFts = ftsIds != null;
|
boolean hasFts = ftsIds != null;
|
||||||
Specification<Document> spec = buildSearchSpec(
|
SearchFilters searchFilters = new SearchFilters(
|
||||||
hasFts, ftsIds, null, null,
|
filters.text(), null, null, filters.sender(), filters.receiver(),
|
||||||
filters.sender(), filters.receiver(),
|
filters.tags(), filters.tagQ(), filters.status(), filters.tagOperator(), false);
|
||||||
filters.tags(), filters.tagQ(),
|
Specification<Document> spec = buildSearchSpec(hasFts, ftsIds, searchFilters);
|
||||||
filters.status(), filters.tagOperator(), false);
|
|
||||||
return documentRepository.findAll(spec).stream()
|
return documentRepository.findAll(spec).stream()
|
||||||
.map(Document::getDocumentDate)
|
.map(Document::getDocumentDate)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
@@ -500,18 +499,15 @@ public class DocumentService {
|
|||||||
* round-trip.
|
* round-trip.
|
||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<UUID> findIdsForFilter(String text, LocalDate from, LocalDate to, UUID sender, UUID receiver,
|
public List<UUID> findIdsForFilter(SearchFilters filters) {
|
||||||
List<String> tags, String tagQ, DocumentStatus status, TagOperator tagOperator,
|
boolean hasText = StringUtils.hasText(filters.text());
|
||||||
boolean undated) {
|
|
||||||
boolean hasText = StringUtils.hasText(text);
|
|
||||||
List<UUID> rankedIds = null;
|
List<UUID> rankedIds = null;
|
||||||
if (hasText) {
|
if (hasText) {
|
||||||
rankedIds = documentRepository.findAllMatchingIdsByFts(text);
|
rankedIds = documentRepository.findAllMatchingIdsByFts(filters.text());
|
||||||
if (rankedIds.isEmpty()) return List.of();
|
if (rankedIds.isEmpty()) return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
Specification<Document> spec = buildSearchSpec(
|
Specification<Document> spec = buildSearchSpec(hasText, rankedIds, filters);
|
||||||
hasText, rankedIds, from, to, sender, receiver, tags, tagQ, status, tagOperator, undated);
|
|
||||||
return documentRepository.findAll(spec).stream().map(Document::getId).toList();
|
return documentRepository.findAll(spec).stream().map(Document::getId).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,23 +517,18 @@ public class DocumentService {
|
|||||||
* (uncapped, ID-only). Caller does its own FTS short-circuit when the
|
* (uncapped, ID-only). Caller does its own FTS short-circuit when the
|
||||||
* full-text query returned no rows.
|
* full-text query returned no rows.
|
||||||
*/
|
*/
|
||||||
private Specification<Document> buildSearchSpec(boolean hasText, List<UUID> ftsIds,
|
private Specification<Document> buildSearchSpec(boolean hasText, List<UUID> ftsIds, SearchFilters filters) {
|
||||||
LocalDate from, LocalDate to,
|
boolean useOrLogic = filters.tagOperator() == TagOperator.OR;
|
||||||
UUID sender, UUID receiver,
|
List<Set<UUID>> expandedTagSets = tagService.expandTagNamesToDescendantIdSets(filters.tags());
|
||||||
List<String> tags, String tagQ,
|
|
||||||
DocumentStatus status, TagOperator tagOperator,
|
|
||||||
boolean undated) {
|
|
||||||
boolean useOrLogic = tagOperator == TagOperator.OR;
|
|
||||||
List<Set<UUID>> expandedTagSets = tagService.expandTagNamesToDescendantIdSets(tags);
|
|
||||||
Specification<Document> textSpec = hasText ? hasIds(ftsIds) : (root, query, cb) -> null;
|
Specification<Document> textSpec = hasText ? hasIds(ftsIds) : (root, query, cb) -> null;
|
||||||
return Specification.where(textSpec)
|
return Specification.where(textSpec)
|
||||||
.and(isBetween(from, to))
|
.and(isBetween(filters.from(), filters.to()))
|
||||||
.and(hasSender(sender))
|
.and(hasSender(filters.sender()))
|
||||||
.and(hasReceiver(receiver))
|
.and(hasReceiver(filters.receiver()))
|
||||||
.and(hasTags(expandedTagSets, useOrLogic))
|
.and(hasTags(expandedTagSets, useOrLogic))
|
||||||
.and(hasTagPartial(tagQ))
|
.and(hasTagPartial(filters.tagQ()))
|
||||||
.and(hasStatus(status))
|
.and(hasStatus(filters.status()))
|
||||||
.and(undatedOnly(undated));
|
.and(undatedOnly(filters.undated()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -666,8 +657,8 @@ public class DocumentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. Allgemeine Suche (für das Suchfeld im Frontend)
|
// 1. Allgemeine Suche (für das Suchfeld im Frontend)
|
||||||
public DocumentSearchResult searchDocuments(String text, LocalDate from, LocalDate to, UUID sender, UUID receiver, List<String> tags, String tagQ, DocumentStatus status, DocumentSort sort, String dir, TagOperator tagOperator, boolean undated, Pageable pageable) {
|
public DocumentSearchResult searchDocuments(SearchFilters filters, DocumentSort sort, String dir, Pageable pageable) {
|
||||||
boolean hasText = StringUtils.hasText(text);
|
boolean hasText = StringUtils.hasText(filters.text());
|
||||||
|
|
||||||
// Pure-text RELEVANCE: push pagination + ts_rank ordering into SQL — skip
|
// Pure-text RELEVANCE: push pagination + ts_rank ordering into SQL — skip
|
||||||
// findAllMatchingIdsByFts entirely (ADR-008). This must run BEFORE any
|
// findAllMatchingIdsByFts entirely (ADR-008). This must run BEFORE any
|
||||||
@@ -677,13 +668,13 @@ public class DocumentService {
|
|||||||
// no date/sender/receiver/tag/status filters, and undated documents are valid
|
// no date/sender/receiver/tag/status filters, and undated documents are valid
|
||||||
// FTS hits already folded into the ranked page, so there is no separate undated
|
// FTS hits already folded into the ranked page, so there is no separate undated
|
||||||
// count to report here.
|
// count to report here.
|
||||||
if (!undated && isPureTextRelevance(hasText, sort, from, to, sender, receiver, tags, tagQ, status)) {
|
if (!filters.undated() && isPureTextRelevance(hasText, sort, filters)) {
|
||||||
return relevanceSortedPageFromSql(text, pageable);
|
return relevanceSortedPageFromSql(filters.text(), pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> rankedIds = null;
|
List<UUID> rankedIds = null;
|
||||||
if (hasText) {
|
if (hasText) {
|
||||||
rankedIds = documentRepository.findAllMatchingIdsByFts(text);
|
rankedIds = documentRepository.findAllMatchingIdsByFts(filters.text());
|
||||||
// FTS matched nothing → no results and, by definition, no undated matches either.
|
// FTS matched nothing → no results and, by definition, no undated matches either.
|
||||||
if (rankedIds.isEmpty()) return DocumentSearchResult.of(List.of());
|
if (rankedIds.isEmpty()) return DocumentSearchResult.of(List.of());
|
||||||
}
|
}
|
||||||
@@ -691,37 +682,32 @@ public class DocumentService {
|
|||||||
// Global undated count for the current filter (q/tags/sender/receiver/status),
|
// Global undated count for the current filter (q/tags/sender/receiver/status),
|
||||||
// forcing undatedOnly(true) and IGNORING the user's "Nur undatierte" toggle so
|
// forcing undatedOnly(true) and IGNORING the user's "Nur undatierte" toggle so
|
||||||
// it never collapses to the page slice and never double-counts (issue #668).
|
// it never collapses to the page slice and never double-counts (issue #668).
|
||||||
long undatedCount = countUndatedForFilter(hasText, rankedIds, from, to, sender, receiver, tags, tagQ, status, tagOperator);
|
long undatedCount = countUndatedForFilter(hasText, rankedIds, filters.withUndated(true));
|
||||||
|
|
||||||
return runSearch(text, hasText, rankedIds, from, to, sender, receiver, tags, tagQ, status, sort, dir, tagOperator, undated, pageable)
|
return runSearch(hasText, rankedIds, filters, sort, dir, pageable)
|
||||||
.withUndatedCount(undatedCount);
|
.withUndatedCount(undatedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts every undated document (meta_date IS NULL) matching the active filter,
|
* Counts every undated document (meta_date IS NULL) matching the active filter,
|
||||||
* across all pages, independent of the undated toggle. Reuses {@link #buildSearchSpec}
|
* across all pages, independent of the undated toggle. The caller passes
|
||||||
* with {@code undated=true} forced so the count tracks q/tags/sender/receiver/status.
|
* {@code filters.withUndated(true)} so the count tracks q/tags/sender/receiver/status
|
||||||
* A {@code from}/{@code to} range excludes undated rows by the collision rule (#668),
|
* regardless of the user's "Nur undatierte" toggle. A {@code from}/{@code to} range
|
||||||
* so the count is legitimately 0 inside a date range.
|
* excludes undated rows by the collision rule (#668), so the count is legitimately 0
|
||||||
|
* inside a date range.
|
||||||
*/
|
*/
|
||||||
private long countUndatedForFilter(boolean hasText, List<UUID> ftsIds,
|
private long countUndatedForFilter(boolean hasText, List<UUID> ftsIds, SearchFilters filters) {
|
||||||
LocalDate from, LocalDate to, UUID sender, UUID receiver,
|
Specification<Document> undatedSpec = buildSearchSpec(hasText, ftsIds, filters);
|
||||||
List<String> tags, String tagQ, DocumentStatus status, TagOperator tagOperator) {
|
|
||||||
Specification<Document> undatedSpec = buildSearchSpec(
|
|
||||||
hasText, ftsIds, from, to, sender, receiver, tags, tagQ, status, tagOperator, true);
|
|
||||||
return documentRepository.count(undatedSpec);
|
return documentRepository.count(undatedSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The original search dispatch — produces the page slice + totals, sans undated count. */
|
/** The original search dispatch — produces the page slice + totals, sans undated count. */
|
||||||
private DocumentSearchResult runSearch(String text, boolean hasText, List<UUID> rankedIds,
|
private DocumentSearchResult runSearch(boolean hasText, List<UUID> rankedIds, SearchFilters filters,
|
||||||
LocalDate from, LocalDate to, UUID sender, UUID receiver,
|
DocumentSort sort, String dir, Pageable pageable) {
|
||||||
List<String> tags, String tagQ, DocumentStatus status,
|
|
||||||
DocumentSort sort, String dir, TagOperator tagOperator,
|
|
||||||
boolean undated, Pageable pageable) {
|
|
||||||
// The pure-text RELEVANCE fast path is handled by the caller (searchDocuments)
|
// The pure-text RELEVANCE fast path is handled by the caller (searchDocuments)
|
||||||
// before findAllMatchingIdsByFts runs, so it never reaches here (ADR-008).
|
// before findAllMatchingIdsByFts runs, so it never reaches here (ADR-008).
|
||||||
Specification<Document> spec = buildSearchSpec(
|
Specification<Document> spec = buildSearchSpec(hasText, rankedIds, filters);
|
||||||
hasText, rankedIds, from, to, sender, receiver, tags, tagQ, status, tagOperator, undated);
|
String text = filters.text();
|
||||||
|
|
||||||
// SENDER and RECEIVER sorts load the full match set and slice in-memory.
|
// SENDER and RECEIVER sorts load the full match set and slice in-memory.
|
||||||
// JPA's Sort.by("sender.lastName") generates an INNER JOIN that silently drops
|
// JPA's Sort.by("sender.lastName") generates an INNER JOIN that silently drops
|
||||||
@@ -755,12 +741,12 @@ public class DocumentService {
|
|||||||
return buildResultPaged(page.getContent(), text, pageable, page.getTotalElements());
|
return buildResultPaged(page.getContent(), text, pageable, page.getTotalElements());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPureTextRelevance(boolean hasText, DocumentSort sort,
|
private static boolean isPureTextRelevance(boolean hasText, DocumentSort sort, SearchFilters filters) {
|
||||||
LocalDate from, LocalDate to, UUID sender, UUID receiver,
|
|
||||||
List<String> tags, String tagQ, DocumentStatus status) {
|
|
||||||
return hasText && (sort == null || sort == DocumentSort.RELEVANCE)
|
return hasText && (sort == null || sort == DocumentSort.RELEVANCE)
|
||||||
&& from == null && to == null && sender == null && receiver == null
|
&& filters.from() == null && filters.to() == null
|
||||||
&& (tags == null || tags.isEmpty()) && (tagQ == null || tagQ.isBlank()) && status == null;
|
&& filters.sender() == null && filters.receiver() == null
|
||||||
|
&& (filters.tags() == null || filters.tags().isEmpty())
|
||||||
|
&& (filters.tagQ() == null || filters.tagQ().isBlank()) && filters.status() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -76,7 +75,7 @@ class DocumentControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_returns200_whenAuthenticated() throws Exception {
|
void search_returns200_whenAuthenticated() throws Exception {
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search"))
|
mockMvc.perform(get("/api/documents/search"))
|
||||||
@@ -88,7 +87,7 @@ class DocumentControllerTest {
|
|||||||
void search_undatedTrue_isReachableByAuthenticatedUser() throws Exception {
|
void search_undatedTrue_isReachableByAuthenticatedUser() throws Exception {
|
||||||
// The read GET must stay reachable for READ_ALL users — guards against a
|
// The read GET must stay reachable for READ_ALL users — guards against a
|
||||||
// future refactor accidentally write-guarding the undated triage path (#668).
|
// future refactor accidentally write-guarding the undated triage path (#668).
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search").param("undated", "true"))
|
mockMvc.perform(get("/api/documents/search").param("undated", "true"))
|
||||||
@@ -104,41 +103,43 @@ class DocumentControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_undatedTrue_isForwardedToServiceAsTrue() throws Exception {
|
void search_undatedTrue_isForwardedToServiceAsTrue() throws Exception {
|
||||||
ArgumentCaptor<Boolean> undatedCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<SearchFilters> filtersCaptor = ArgumentCaptor.forClass(SearchFilters.class);
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search").param("undated", "true"))
|
mockMvc.perform(get("/api/documents/search").param("undated", "true"))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
verify(documentService).searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), undatedCaptor.capture(), any());
|
verify(documentService).searchDocuments(filtersCaptor.capture(), any(), any(), any());
|
||||||
assertThat(undatedCaptor.getValue()).isTrue();
|
assertThat(filtersCaptor.getValue().undated()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_withoutUndatedParam_forwardsFalseToService() throws Exception {
|
void search_withoutUndatedParam_forwardsFalseToService() throws Exception {
|
||||||
ArgumentCaptor<Boolean> undatedCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<SearchFilters> filtersCaptor = ArgumentCaptor.forClass(SearchFilters.class);
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search"))
|
mockMvc.perform(get("/api/documents/search"))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
verify(documentService).searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), undatedCaptor.capture(), any());
|
verify(documentService).searchDocuments(filtersCaptor.capture(), any(), any(), any());
|
||||||
assertThat(undatedCaptor.getValue()).isFalse();
|
assertThat(filtersCaptor.getValue().undated()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_withStatusParam_passesItToService() throws Exception {
|
void search_withStatusParam_passesItToService() throws Exception {
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), eq(DocumentStatus.REVIEWED), any(), any(), any(), anyBoolean(), any()))
|
ArgumentCaptor<SearchFilters> filtersCaptor = ArgumentCaptor.forClass(SearchFilters.class);
|
||||||
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search").param("status", "REVIEWED"))
|
mockMvc.perform(get("/api/documents/search").param("status", "REVIEWED"))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
verify(documentService).searchDocuments(any(), any(), any(), any(), any(), any(), any(), eq(DocumentStatus.REVIEWED), any(), any(), any(), anyBoolean(), any());
|
verify(documentService).searchDocuments(filtersCaptor.capture(), any(), any(), any());
|
||||||
|
assertThat(filtersCaptor.getValue().status()).isEqualTo(DocumentStatus.REVIEWED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -165,7 +166,7 @@ class DocumentControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_responseContainsTotalCount() throws Exception {
|
void search_responseContainsTotalCount() throws Exception {
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search"))
|
mockMvc.perform(get("/api/documents/search"))
|
||||||
@@ -180,7 +181,7 @@ class DocumentControllerTest {
|
|||||||
UUID docId = UUID.randomUUID();
|
UUID docId = UUID.randomUUID();
|
||||||
var matchData = new SearchMatchData(
|
var matchData = new SearchMatchData(
|
||||||
"Er schrieb einen langen Brief", List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
"Er schrieb einen langen Brief", List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of(new DocumentListItem(
|
.thenReturn(DocumentSearchResult.of(List.of(new DocumentListItem(
|
||||||
docId, "Brief an Anna", "brief.pdf", null, null,
|
docId, "Brief an Anna", "brief.pdf", null, null,
|
||||||
DatePrecision.UNKNOWN, null, null,
|
DatePrecision.UNKNOWN, null, null,
|
||||||
@@ -200,7 +201,7 @@ class DocumentControllerTest {
|
|||||||
void search_returns_flat_item_with_id_and_without_sensitive_fields() throws Exception {
|
void search_returns_flat_item_with_id_and_without_sensitive_fields() throws Exception {
|
||||||
UUID docId = UUID.randomUUID();
|
UUID docId = UUID.randomUUID();
|
||||||
var matchData = new SearchMatchData(null, List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
var matchData = new SearchMatchData(null, List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of(new DocumentListItem(
|
.thenReturn(DocumentSearchResult.of(List.of(new DocumentListItem(
|
||||||
docId, "Brief an Anna", "brief.pdf", null, null,
|
docId, "Brief an Anna", "brief.pdf", null, null,
|
||||||
DatePrecision.UNKNOWN, null, null,
|
DatePrecision.UNKNOWN, null, null,
|
||||||
@@ -223,7 +224,7 @@ class DocumentControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_responseExposesPagingFields() throws Exception {
|
void search_responseExposesPagingFields() throws Exception {
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search"))
|
mockMvc.perform(get("/api/documents/search"))
|
||||||
@@ -268,7 +269,7 @@ class DocumentControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void search_passesPageRequestToService() throws Exception {
|
void search_passesPageRequestToService() throws Exception {
|
||||||
when(documentService.searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), any()))
|
when(documentService.searchDocuments(any(), any(), any(), any()))
|
||||||
.thenReturn(DocumentSearchResult.of(List.of()));
|
.thenReturn(DocumentSearchResult.of(List.of()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/search").param("page", "2").param("size", "25"))
|
mockMvc.perform(get("/api/documents/search").param("page", "2").param("size", "25"))
|
||||||
@@ -276,7 +277,7 @@ class DocumentControllerTest {
|
|||||||
|
|
||||||
org.mockito.ArgumentCaptor<org.springframework.data.domain.Pageable> captor =
|
org.mockito.ArgumentCaptor<org.springframework.data.domain.Pageable> captor =
|
||||||
org.mockito.ArgumentCaptor.forClass(org.springframework.data.domain.Pageable.class);
|
org.mockito.ArgumentCaptor.forClass(org.springframework.data.domain.Pageable.class);
|
||||||
verify(documentService).searchDocuments(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean(), captor.capture());
|
verify(documentService).searchDocuments(any(), any(), any(), captor.capture());
|
||||||
org.springframework.data.domain.Pageable pageable = captor.getValue();
|
org.springframework.data.domain.Pageable pageable = captor.getValue();
|
||||||
org.assertj.core.api.Assertions.assertThat(pageable.getPageNumber()).isEqualTo(2);
|
org.assertj.core.api.Assertions.assertThat(pageable.getPageNumber()).isEqualTo(2);
|
||||||
org.assertj.core.api.Assertions.assertThat(pageable.getPageSize()).isEqualTo(25);
|
org.assertj.core.api.Assertions.assertThat(pageable.getPageSize()).isEqualTo(25);
|
||||||
@@ -1208,7 +1209,7 @@ class DocumentControllerTest {
|
|||||||
void getDocumentIds_returns200_andDelegatesToService() throws Exception {
|
void getDocumentIds_returns200_andDelegatesToService() throws Exception {
|
||||||
when(userService.findByEmail(any())).thenReturn(AppUser.builder().id(UUID.randomUUID()).build());
|
when(userService.findByEmail(any())).thenReturn(AppUser.builder().id(UUID.randomUUID()).build());
|
||||||
UUID id = UUID.randomUUID();
|
UUID id = UUID.randomUUID();
|
||||||
when(documentService.findIdsForFilter(any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean()))
|
when(documentService.findIdsForFilter(any()))
|
||||||
.thenReturn(List.of(id));
|
.thenReturn(List.of(id));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/ids"))
|
mockMvc.perform(get("/api/documents/ids"))
|
||||||
@@ -1221,13 +1222,15 @@ class DocumentControllerTest {
|
|||||||
void getDocumentIds_passesSenderIdParamToService() throws Exception {
|
void getDocumentIds_passesSenderIdParamToService() throws Exception {
|
||||||
when(userService.findByEmail(any())).thenReturn(AppUser.builder().id(UUID.randomUUID()).build());
|
when(userService.findByEmail(any())).thenReturn(AppUser.builder().id(UUID.randomUUID()).build());
|
||||||
UUID senderId = UUID.randomUUID();
|
UUID senderId = UUID.randomUUID();
|
||||||
when(documentService.findIdsForFilter(any(), any(), any(), eq(senderId), any(), any(), any(), any(), any(), anyBoolean()))
|
ArgumentCaptor<SearchFilters> filtersCaptor = ArgumentCaptor.forClass(SearchFilters.class);
|
||||||
|
when(documentService.findIdsForFilter(any()))
|
||||||
.thenReturn(List.of());
|
.thenReturn(List.of());
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/ids").param("senderId", senderId.toString()))
|
mockMvc.perform(get("/api/documents/ids").param("senderId", senderId.toString()))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
verify(documentService).findIdsForFilter(any(), any(), any(), eq(senderId), any(), any(), any(), any(), any(), anyBoolean());
|
verify(documentService).findIdsForFilter(filtersCaptor.capture());
|
||||||
|
assertThat(filtersCaptor.getValue().sender()).isEqualTo(senderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1237,7 +1240,7 @@ class DocumentControllerTest {
|
|||||||
// Service returns 5001 IDs — one over BULK_EDIT_FILTER_MAX_IDS (5000).
|
// Service returns 5001 IDs — one over BULK_EDIT_FILTER_MAX_IDS (5000).
|
||||||
java.util.List<UUID> tooMany = new java.util.ArrayList<>(5001);
|
java.util.List<UUID> tooMany = new java.util.ArrayList<>(5001);
|
||||||
for (int i = 0; i < 5001; i++) tooMany.add(UUID.randomUUID());
|
for (int i = 0; i < 5001; i++) tooMany.add(UUID.randomUUID());
|
||||||
when(documentService.findIdsForFilter(any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean()))
|
when(documentService.findIdsForFilter(any()))
|
||||||
.thenReturn(tooMany);
|
.thenReturn(tooMany);
|
||||||
|
|
||||||
mockMvc.perform(get("/api/documents/ids"))
|
mockMvc.perform(get("/api/documents/ids"))
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ class DocumentLazyLoadingTest {
|
|||||||
savedDocument("SrDoc", "sr_doc.pdf", sender, Set.of(receiver), Set.of(tag));
|
savedDocument("SrDoc", "sr_doc.pdf", sender, Set.of(receiver), Set.of(tag));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.RECEIVER, "asc", null, false, PageRequest.of(0, 20));
|
DocumentSort.RECEIVER, "asc", PageRequest.of(0, 20));
|
||||||
assertThat(result.totalElements()).isGreaterThan(0);
|
assertThat(result.totalElements()).isGreaterThan(0);
|
||||||
assertThatCode(() ->
|
assertThatCode(() ->
|
||||||
result.items().forEach(i -> { if (i.sender() != null) i.sender().getLastName(); }))
|
result.items().forEach(i -> { if (i.sender() != null) i.sender().getLastName(); }))
|
||||||
@@ -137,8 +137,8 @@ class DocumentLazyLoadingTest {
|
|||||||
savedDocument("SsDoc", "ss_doc.pdf", sender, Set.of(), Set.of(tag));
|
savedDocument("SsDoc", "ss_doc.pdf", sender, Set.of(), Set.of(tag));
|
||||||
|
|
||||||
assertThatCode(() -> documentService.searchDocuments(
|
assertThatCode(() -> documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.SENDER, "asc", null, false, PageRequest.of(0, 20)))
|
DocumentSort.SENDER, "asc", PageRequest.of(0, 20)))
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ class DocumentListItemIntegrationTest {
|
|||||||
.build());
|
.build());
|
||||||
|
|
||||||
assertThatCode(() -> documentService.searchDocuments(
|
assertThatCode(() -> documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50)))
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50)))
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ class DocumentListItemIntegrationTest {
|
|||||||
.build());
|
.build());
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
assertThat(result.totalElements()).isGreaterThan(0);
|
assertThat(result.totalElements()).isGreaterThan(0);
|
||||||
DocumentListItem item = result.items().get(0);
|
DocumentListItem item = result.items().get(0);
|
||||||
@@ -91,8 +91,8 @@ class DocumentListItemIntegrationTest {
|
|||||||
.build());
|
.build());
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
DocumentListItem item = result.items().stream()
|
DocumentListItem item = result.items().stream()
|
||||||
.filter(i -> i.title().equals("Range Brief")).findFirst().orElseThrow();
|
.filter(i -> i.title().equals("Range Brief")).findFirst().orElseThrow();
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void search_firstPage_returnsExactlyPageSizeItems_andCorrectTotalElements() {
|
void search_firstPage_returnsExactlyPageSizeItems_andCorrectTotalElements() {
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(50);
|
assertThat(result.items()).hasSize(50);
|
||||||
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
||||||
@@ -74,8 +74,8 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void search_lastPartialPage_returnsRemainingItems() {
|
void search_lastPartialPage_returnsRemainingItems() {
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(2, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(2, 50));
|
||||||
|
|
||||||
// Page 2 (offset 100) of 120 docs → exactly 20 items on the tail.
|
// Page 2 (offset 100) of 120 docs → exactly 20 items on the tail.
|
||||||
assertThat(result.items()).hasSize(20);
|
assertThat(result.items()).hasSize(20);
|
||||||
@@ -86,8 +86,8 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void search_pageBeyondLast_returnsEmptyContent_totalElementsStillCorrect() {
|
void search_pageBeyondLast_returnsEmptyContent_totalElementsStillCorrect() {
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(99, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(99, 50));
|
||||||
|
|
||||||
assertThat(result.items()).isEmpty();
|
assertThat(result.items()).isEmpty();
|
||||||
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
||||||
@@ -99,8 +99,8 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
// comment in DocumentService). Proves that the in-memory slice path
|
// comment in DocumentService). Proves that the in-memory slice path
|
||||||
// returns the correct total from a real repository fetch.
|
// returns the correct total from a real repository fetch.
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.SENDER, "asc", null, false, PageRequest.of(1, 50));
|
DocumentSort.SENDER, "asc", PageRequest.of(1, 50));
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(50);
|
assertThat(result.items()).hasSize(50);
|
||||||
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
assertThat(result.totalElements()).isEqualTo(FIXTURE_SIZE);
|
||||||
@@ -125,8 +125,8 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
// Global undated count is the full undated total, independent of page size.
|
// Global undated count is the full undated total, independent of page size.
|
||||||
assertThat(result.undatedCount()).isEqualTo(undatedTotal);
|
assertThat(result.undatedCount()).isEqualTo(undatedTotal);
|
||||||
@@ -153,11 +153,11 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DocumentSearchResult unfiltered = documentService.searchDocuments(
|
DocumentSearchResult unfiltered = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
DocumentSearchResult undatedOnly = documentService.searchDocuments(
|
DocumentSearchResult undatedOnly = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, true),
|
||||||
DocumentSort.DATE, "DESC", null, true, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
assertThat(unfiltered.undatedCount()).isEqualTo(undatedTotal);
|
assertThat(unfiltered.undatedCount()).isEqualTo(undatedTotal);
|
||||||
assertThat(undatedOnly.undatedCount()).isEqualTo(undatedTotal);
|
assertThat(undatedOnly.undatedCount()).isEqualTo(undatedTotal);
|
||||||
@@ -178,9 +178,9 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, LocalDate.of(1900, 1, 1), LocalDate.of(2000, 12, 31),
|
new SearchFilters(null, LocalDate.of(1900, 1, 1), LocalDate.of(2000, 12, 31),
|
||||||
null, null, null, null, null,
|
null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
|
|
||||||
assertThat(result.undatedCount()).isZero();
|
assertThat(result.undatedCount()).isZero();
|
||||||
}
|
}
|
||||||
@@ -188,11 +188,11 @@ class DocumentSearchPagedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void search_differentPagesReturnDisjointSlices() {
|
void search_differentPagesReturnDisjointSlices() {
|
||||||
DocumentSearchResult page0 = documentService.searchDocuments(
|
DocumentSearchResult page0 = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(0, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(0, 50));
|
||||||
DocumentSearchResult page1 = documentService.searchDocuments(
|
DocumentSearchResult page1 = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null,
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.DATE, "DESC", null, false, PageRequest.of(1, 50));
|
DocumentSort.DATE, "DESC", PageRequest.of(1, 50));
|
||||||
|
|
||||||
// No document id should appear on both pages — slicing must be exclusive.
|
// No document id should appear on both pages — slicing must be exclusive.
|
||||||
var idsOnPage0 = page0.items().stream()
|
var idsOnPage0 = page0.items().stream()
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ class DocumentServiceSortTest {
|
|||||||
.thenReturn(new PageImpl<>(List.of(newer, older)));
|
.thenReturn(new PageImpl<>(List.of(newer, older)));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, DocumentSort.DATE, "DESC", null, false, PAGE);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.DATE, "DESC", PAGE);
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(2);
|
assertThat(result.items()).hasSize(2);
|
||||||
assertThat(result.items().get(0).id()).isEqualTo(id2); // newer first
|
assertThat(result.items().get(0).id()).isEqualTo(id2); // newer first
|
||||||
@@ -84,7 +85,8 @@ class DocumentServiceSortTest {
|
|||||||
.thenReturn(List.of(doc(id1)));
|
.thenReturn(List.of(doc(id1)));
|
||||||
|
|
||||||
documentService.searchDocuments(
|
documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, false, PAGE);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RELEVANCE, null, PAGE);
|
||||||
|
|
||||||
verify(documentRepository).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
verify(documentRepository).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
||||||
verify(documentRepository, never()).findAllMatchingIdsByFts(anyString());
|
verify(documentRepository, never()).findAllMatchingIdsByFts(anyString());
|
||||||
@@ -102,7 +104,8 @@ class DocumentServiceSortTest {
|
|||||||
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(id2), doc(id1))); // unordered from JPA
|
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(id2), doc(id1))); // unordered from JPA
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, false, PAGE);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RELEVANCE, null, PAGE);
|
||||||
|
|
||||||
assertThat(result.items().get(0).id()).isEqualTo(id1);
|
assertThat(result.items().get(0).id()).isEqualTo(id1);
|
||||||
}
|
}
|
||||||
@@ -119,7 +122,8 @@ class DocumentServiceSortTest {
|
|||||||
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(id2), doc(id1)));
|
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(id2), doc(id1)));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, null, null, null, false, PAGE);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
null, null, PAGE);
|
||||||
|
|
||||||
assertThat(result.items().get(0).id()).isEqualTo(id1);
|
assertThat(result.items().get(0).id()).isEqualTo(id1);
|
||||||
}
|
}
|
||||||
@@ -132,8 +136,8 @@ class DocumentServiceSortTest {
|
|||||||
Pageable hugePage = org.springframework.data.domain.PageRequest.of(Integer.MAX_VALUE / 10 + 1, 10);
|
Pageable hugePage = org.springframework.data.domain.PageRequest.of(Integer.MAX_VALUE / 10 + 1, 10);
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null,
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.RELEVANCE, null, null, false, hugePage);
|
DocumentSort.RELEVANCE, null, hugePage);
|
||||||
|
|
||||||
assertThat(result.items()).isEmpty();
|
assertThat(result.items()).isEmpty();
|
||||||
verify(documentRepository, never()).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
verify(documentRepository, never()).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
||||||
@@ -152,8 +156,8 @@ class DocumentServiceSortTest {
|
|||||||
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(uuidId)));
|
when(documentRepository.findAllById(any())).thenReturn(List.of(doc(uuidId)));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null,
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
DocumentSort.RELEVANCE, null, null, false, PAGE);
|
DocumentSort.RELEVANCE, null, PAGE);
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(1);
|
assertThat(result.items()).hasSize(1);
|
||||||
assertThat(result.items().get(0).id()).isEqualTo(uuidId);
|
assertThat(result.items().get(0).id()).isEqualTo(uuidId);
|
||||||
@@ -173,7 +177,8 @@ class DocumentServiceSortTest {
|
|||||||
// sender filter is active → triggers in-memory path, not findFtsPageRaw
|
// sender filter is active → triggers in-memory path, not findFtsPageRaw
|
||||||
LocalDate from = LocalDate.of(1900, 1, 1);
|
LocalDate from = LocalDate.of(1900, 1, 1);
|
||||||
documentService.searchDocuments(
|
documentService.searchDocuments(
|
||||||
"Brief", from, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, false, PAGE);
|
new SearchFilters("Brief", from, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RELEVANCE, null, PAGE);
|
||||||
|
|
||||||
verify(documentRepository, never()).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
verify(documentRepository, never()).findFtsPageRaw(anyString(), anyInt(), anyInt());
|
||||||
verify(documentRepository).findAllMatchingIdsByFts("Brief");
|
verify(documentRepository).findAllMatchingIdsByFts("Brief");
|
||||||
|
|||||||
@@ -1441,8 +1441,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", null, false, org.springframework.data.domain.PageRequest.of(1, 50));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
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));
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
||||||
verify(documentRepository, never()).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Sort.class));
|
verify(documentRepository, never()).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Sort.class));
|
||||||
@@ -1454,8 +1455,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", null, false, org.springframework.data.domain.PageRequest.of(3, 25));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
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());
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||||
assertThat(captor.getValue().getPageNumber()).isEqualTo(3);
|
assertThat(captor.getValue().getPageNumber()).isEqualTo(3);
|
||||||
@@ -1470,8 +1472,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of(d), org.springframework.data.domain.PageRequest.of(0, 50), 120L));
|
.thenReturn(new PageImpl<>(List.of(d), org.springframework.data.domain.PageRequest.of(0, 50), 120L));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", null, false, org.springframework.data.domain.PageRequest.of(0, 50));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
org.raddatz.familienarchiv.document.DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(0, 50));
|
||||||
|
|
||||||
assertThat(result.totalElements()).isEqualTo(120L);
|
assertThat(result.totalElements()).isEqualTo(120L);
|
||||||
assertThat(result.pageNumber()).isZero();
|
assertThat(result.pageNumber()).isZero();
|
||||||
@@ -1486,8 +1489,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
DocumentSort.DATE, "DESC", null, false, org.springframework.data.domain.PageRequest.of(0, 5));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.DATE, "DESC", org.springframework.data.domain.PageRequest.of(0, 5));
|
||||||
|
|
||||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||||
Sort.Order dateOrder = captor.getValue().getSort().getOrderFor("documentDate");
|
Sort.Order dateOrder = captor.getValue().getSort().getOrderFor("documentDate");
|
||||||
@@ -1509,8 +1513,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
DocumentSort.DATE, "ASC", null, false, org.springframework.data.domain.PageRequest.of(0, 5));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.DATE, "ASC", org.springframework.data.domain.PageRequest.of(0, 5));
|
||||||
|
|
||||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||||
Sort.Order dateOrder = captor.getValue().getSort().getOrderFor("documentDate");
|
Sort.Order dateOrder = captor.getValue().getSort().getOrderFor("documentDate");
|
||||||
@@ -1530,8 +1535,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
DocumentSort.UPDATED_AT, "DESC", null, false, org.springframework.data.domain.PageRequest.of(0, 5));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
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());
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), captor.capture());
|
||||||
assertThat(captor.getValue().getSort())
|
assertThat(captor.getValue().getSort())
|
||||||
@@ -1554,8 +1560,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
||||||
.thenReturn(all);
|
.thenReturn(all);
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", null, false, org.springframework.data.domain.PageRequest.of(1, 50));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", org.springframework.data.domain.PageRequest.of(1, 50));
|
||||||
|
|
||||||
assertThat(result.totalElements()).isEqualTo(120L);
|
assertThat(result.totalElements()).isEqualTo(120L);
|
||||||
assertThat(result.pageNumber()).isEqualTo(1);
|
assertThat(result.pageNumber()).isEqualTo(1);
|
||||||
@@ -1578,8 +1585,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
||||||
.thenReturn(all);
|
.thenReturn(all);
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(null, null, null, null, null, null, null, null,
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", null, false, org.springframework.data.domain.PageRequest.of(10, 50));
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
org.raddatz.familienarchiv.document.DocumentSort.SENDER, "asc", org.springframework.data.domain.PageRequest.of(10, 50));
|
||||||
|
|
||||||
assertThat(result.items()).isEmpty();
|
assertThat(result.items()).isEmpty();
|
||||||
assertThat(result.totalElements()).isEqualTo(30L);
|
assertThat(result.totalElements()).isEqualTo(30L);
|
||||||
@@ -1592,7 +1600,8 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, DocumentStatus.REVIEWED, null, null, null, false, UNPAGED);
|
documentService.searchDocuments(
|
||||||
|
new SearchFilters(null, null, null, null, null, null, null, DocumentStatus.REVIEWED, null, false), null, null, UNPAGED);
|
||||||
|
|
||||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
||||||
}
|
}
|
||||||
@@ -1602,7 +1611,8 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class)))
|
||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
documentService.searchDocuments(null, null, null, null, null, null, null, null, null, null, null, false, UNPAGED);
|
documentService.searchDocuments(
|
||||||
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false), null, null, UNPAGED);
|
||||||
|
|
||||||
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
verify(documentRepository).findAll(any(org.springframework.data.jpa.domain.Specification.class), any(Pageable.class));
|
||||||
}
|
}
|
||||||
@@ -1680,7 +1690,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(withSender, noSender));
|
.thenReturn(List.of(withSender, noSender));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.SENDER, "asc", null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.SENDER, "asc", UNPAGED);
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(2);
|
assertThat(result.items()).hasSize(2);
|
||||||
assertThat(result.items()).extracting(DocumentListItem::title).containsExactly("Has Sender", "No Sender");
|
assertThat(result.items()).extracting(DocumentListItem::title).containsExactly("Has Sender", "No Sender");
|
||||||
@@ -1700,7 +1711,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(noReceivers, withReceiver));
|
.thenReturn(List.of(noReceivers, withReceiver));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.RECEIVER, "asc", null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RECEIVER, "asc", UNPAGED);
|
||||||
|
|
||||||
assertThat(result.items()).extracting(DocumentListItem::title)
|
assertThat(result.items()).extracting(DocumentListItem::title)
|
||||||
.containsExactly("Has Receiver", "No Receivers");
|
.containsExactly("Has Receiver", "No Receivers");
|
||||||
@@ -1733,7 +1745,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.SENDER, "asc", null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.SENDER, "asc", UNPAGED);
|
||||||
|
|
||||||
// Bob's group precedes Anna's group (ASC by sender). The sort is stable, so
|
// Bob's group precedes Anna's group (ASC by sender). The sort is stable, so
|
||||||
// within each group the input order is preserved (undatedBob, datedBob for Bob;
|
// within each group the input order is preserved (undatedBob, datedBob for Bob;
|
||||||
@@ -1764,7 +1777,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
.thenReturn(List.of(undatedBob, datedAnna, datedBob, undatedAnna));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.SENDER, "desc", null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.SENDER, "desc", UNPAGED);
|
||||||
|
|
||||||
// Anna's group precedes Bob's (DESC by sender); undated stays inside its group.
|
// Anna's group precedes Bob's (DESC by sender); undated stays inside its group.
|
||||||
assertThat(result.items()).extracting(DocumentListItem::title)
|
assertThat(result.items()).extracting(DocumentListItem::title)
|
||||||
@@ -1787,7 +1801,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(undatedFromAlice));
|
.thenReturn(List.of(undatedFromAlice));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.SENDER, "asc", null, true, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, true),
|
||||||
|
DocumentSort.SENDER, "asc", UNPAGED);
|
||||||
|
|
||||||
// The in-memory path queried via a Specification (built by buildSearchSpec with
|
// The in-memory path queried via a Specification (built by buildSearchSpec with
|
||||||
// undatedOnly(true)) rather than skipping straight to a sorted findAll.
|
// undatedOnly(true)) rather than skipping straight to a sorted findAll.
|
||||||
@@ -1803,8 +1818,9 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
when(documentRepository.findAll(any(org.springframework.data.jpa.domain.Specification.class)))
|
||||||
.thenReturn(List.of());
|
.thenReturn(List.of());
|
||||||
|
|
||||||
documentService.searchDocuments("brief", null, null, null, null, null, null, null,
|
documentService.searchDocuments(
|
||||||
DocumentSort.RELEVANCE, null, null, true, UNPAGED);
|
new SearchFilters("brief", null, null, null, null, null, null, null, null, true),
|
||||||
|
DocumentSort.RELEVANCE, null, UNPAGED);
|
||||||
|
|
||||||
// The FTS-id path (buildSearchSpec) ran; the raw-page SQL shortcut did not.
|
// The FTS-id path (buildSearchSpec) ran; the raw-page SQL shortcut did not.
|
||||||
verify(documentRepository).findAllMatchingIdsByFts("brief");
|
verify(documentRepository).findAllMatchingIdsByFts("brief");
|
||||||
@@ -1827,7 +1843,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(docNullName, docSmith));
|
.thenReturn(List.of(docNullName, docSmith));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, DocumentSort.SENDER, "asc", null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.SENDER, "asc", UNPAGED);
|
||||||
|
|
||||||
// null lastName should sort to end (treated as empty), not before "smith" (as "null")
|
// null lastName should sort to end (treated as empty), not before "smith" (as "null")
|
||||||
assertThat(result.items()).extracting(DocumentListItem::title)
|
assertThat(result.items()).extracting(DocumentListItem::title)
|
||||||
@@ -1850,7 +1867,8 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findEnrichmentData(any(), eq("Brief"))).thenReturn(rows);
|
when(documentRepository.findEnrichmentData(any(), eq("Brief"))).thenReturn(rows);
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, false, UNPAGED);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RELEVANCE, null, UNPAGED);
|
||||||
|
|
||||||
assertThat(result.items()).hasSize(1);
|
assertThat(result.items()).hasSize(1);
|
||||||
SearchMatchData md = result.items().get(0).matchData();
|
SearchMatchData md = result.items().get(0).matchData();
|
||||||
@@ -1864,7 +1882,8 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(new PageImpl<>(List.of()));
|
.thenReturn(new PageImpl<>(List.of()));
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
null, null, null, null, null, null, null, null, null, null, null, false, UNPAGED);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false),
|
||||||
|
null, null, UNPAGED);
|
||||||
|
|
||||||
assertThat(result.items()).isEmpty();
|
assertThat(result.items()).isEmpty();
|
||||||
}
|
}
|
||||||
@@ -1884,7 +1903,8 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findEnrichmentData(any(), eq("Brief"))).thenReturn(rows);
|
when(documentRepository.findEnrichmentData(any(), eq("Brief"))).thenReturn(rows);
|
||||||
|
|
||||||
DocumentSearchResult result = documentService.searchDocuments(
|
DocumentSearchResult result = documentService.searchDocuments(
|
||||||
"Brief", null, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, false, UNPAGED);
|
new SearchFilters("Brief", null, null, null, null, null, null, null, null, false),
|
||||||
|
DocumentSort.RELEVANCE, null, UNPAGED);
|
||||||
|
|
||||||
SearchMatchData md = result.items().get(0).matchData();
|
SearchMatchData md = result.items().get(0).matchData();
|
||||||
assertThat(md.transcriptionSnippet()).isEqualTo("Hier ist der Brief aus Berlin");
|
assertThat(md.transcriptionSnippet()).isEqualTo("Hier ist der Brief aus Berlin");
|
||||||
@@ -2401,7 +2421,7 @@ class DocumentServiceTest {
|
|||||||
.thenReturn(List.of(d1, d2));
|
.thenReturn(List.of(d1, d2));
|
||||||
|
|
||||||
List<UUID> result = documentService.findIdsForFilter(
|
List<UUID> result = documentService.findIdsForFilter(
|
||||||
null, null, null, null, null, null, null, null, null, false);
|
new SearchFilters(null, null, null, null, null, null, null, null, null, false));
|
||||||
|
|
||||||
assertThat(result).containsExactly(d1.getId(), d2.getId());
|
assertThat(result).containsExactly(d1.getId(), d2.getId());
|
||||||
}
|
}
|
||||||
@@ -2416,7 +2436,7 @@ class DocumentServiceTest {
|
|||||||
when(tagService.expandTagNamesToDescendantIdSets(any())).thenReturn(List.of());
|
when(tagService.expandTagNamesToDescendantIdSets(any())).thenReturn(List.of());
|
||||||
|
|
||||||
documentService.findIdsForFilter(
|
documentService.findIdsForFilter(
|
||||||
null, null, null, null, null, List.of("Brief"), null, null, TagOperator.OR, false);
|
new SearchFilters(null, null, null, null, null, List.of("Brief"), null, null, TagOperator.OR, false));
|
||||||
|
|
||||||
// Spec built without throwing → OR branch was exercised. Coverage gain
|
// Spec built without throwing → OR branch was exercised. Coverage gain
|
||||||
// is in not-throwing on the OR-specific code path; the actual SQL is
|
// is in not-throwing on the OR-specific code path; the actual SQL is
|
||||||
@@ -2429,7 +2449,7 @@ class DocumentServiceTest {
|
|||||||
when(documentRepository.findAllMatchingIdsByFts("xyz")).thenReturn(List.of());
|
when(documentRepository.findAllMatchingIdsByFts("xyz")).thenReturn(List.of());
|
||||||
|
|
||||||
List<UUID> result = documentService.findIdsForFilter(
|
List<UUID> result = documentService.findIdsForFilter(
|
||||||
"xyz", null, null, null, null, null, null, null, null, false);
|
new SearchFilters("xyz", null, null, null, null, null, null, null, null, false));
|
||||||
|
|
||||||
assertThat(result).isEmpty();
|
assertThat(result).isEmpty();
|
||||||
verify(documentRepository, never()).findAll(any(org.springframework.data.jpa.domain.Specification.class));
|
verify(documentRepository, never()).findAll(any(org.springframework.data.jpa.domain.Specification.class));
|
||||||
|
|||||||
Reference in New Issue
Block a user