fix(#145): address PR review — full-table scan, a11y, grid, tests
- DocumentService.getRecentActivity: replace findAll(Sort)+stream().limit() with findAll(PageRequest) so LIMIT is pushed to the database - +page.svelte: collapse two-column grid to single column when mentions is empty - DashboardNeedsMetadata: raise "show all" link from text-xs (12px) to text-sm (14px) and add hover:underline for WCAG 1.4.1 - DashboardRecentDocuments: add comment explaining why T12:00:00 noon-anchor is absent (updatedAt is a full ISO datetime, not a date-only string) - DocumentServiceTest: update getRecentActivity tests to assert PageRequest usage instead of findAll(Sort) - DocumentRepositoryTest: add @DataJpaTest verifying findAll(PageRequest) returns only size rows, not the full table - DocumentControllerTest: add test for default size=5 when param is omitted - NotificationServiceTest: add test documenting that type+read=true falls through to the type-only query (intentional) - page.server.spec.ts: replace stale tests with full dashboard-mode coverage - DashboardMentions.svelte.spec.ts: add tests for REPLY type and absent documentId - DashboardResumeStrip.svelte.spec.ts: add corrupt localStorage test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -262,8 +262,9 @@ public class DocumentService {
|
||||
|
||||
// 0. Zuletzt aktive Dokumente (sortiert nach updatedAt DESC)
|
||||
public List<Document> getRecentActivity(int size) {
|
||||
return documentRepository.findAll(Sort.by(Sort.Direction.DESC, "updatedAt"))
|
||||
.stream().limit(size).toList();
|
||||
return documentRepository.findAll(
|
||||
PageRequest.of(0, size, Sort.by(Sort.Direction.DESC, "updatedAt"))
|
||||
).getContent();
|
||||
}
|
||||
|
||||
// 1. Allgemeine Suche (für das Suchfeld im Frontend)
|
||||
|
||||
@@ -427,6 +427,17 @@ class DocumentControllerTest {
|
||||
.andExpect(jsonPath("$[1].title").value("Beta"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void getRecentActivity_appliesDefaultSizeOfFive_whenSizeParamOmitted() throws Exception {
|
||||
when(documentService.getRecentActivity(5)).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get("/api/documents/recent-activity"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(documentService).getRecentActivity(5);
|
||||
}
|
||||
|
||||
// ─── GET /api/documents/{id}/versions ────────────────────────────────────
|
||||
|
||||
@Test
|
||||
|
||||
@@ -152,6 +152,23 @@ class DocumentRepositoryTest {
|
||||
assertThat(documentRepository.countByMetadataCompleteFalse()).isEqualTo(1);
|
||||
}
|
||||
|
||||
// ─── findAll (PageRequest) — recent activity ──────────────────────────────
|
||||
|
||||
@Test
|
||||
void findAll_withPageRequest_returnsOnlySizeRows_notFullTable() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
documentRepository.save(Document.builder()
|
||||
.title("Doc " + i).originalFilename("doc" + i + ".pdf")
|
||||
.status(DocumentStatus.PLACEHOLDER).build());
|
||||
}
|
||||
|
||||
Page<Document> result = documentRepository.findAll(
|
||||
PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "updatedAt")));
|
||||
|
||||
assertThat(result.getContent()).hasSize(3);
|
||||
assertThat(result.getTotalElements()).isEqualTo(10);
|
||||
}
|
||||
|
||||
// ─── findByMetadataCompleteFalse (Pageable) ───────────────────────────────
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1218,21 +1218,30 @@ class DocumentServiceTest {
|
||||
|
||||
@Test
|
||||
void getRecentActivity_returnsMostRecentlyUpdatedDocuments() {
|
||||
java.time.LocalDateTime oldest = java.time.LocalDateTime.of(2024, 1, 1, 0, 0);
|
||||
java.time.LocalDateTime middle = java.time.LocalDateTime.of(2024, 6, 1, 0, 0);
|
||||
java.time.LocalDateTime newest = java.time.LocalDateTime.of(2024, 12, 1, 0, 0);
|
||||
|
||||
Document doc1 = Document.builder().id(UUID.randomUUID()).title("Oldest").build();
|
||||
Document doc2 = Document.builder().id(UUID.randomUUID()).title("Middle").build();
|
||||
Document doc3 = Document.builder().id(UUID.randomUUID()).title("Newest").build();
|
||||
|
||||
// findAll(Sort) returns documents already sorted DESC by updatedAt
|
||||
when(documentRepository.findAll(Sort.by(Sort.Direction.DESC, "updatedAt")))
|
||||
.thenReturn(List.of(doc3, doc2, doc1));
|
||||
Page<Document> page = new PageImpl<>(List.of(doc3, doc2));
|
||||
when(documentRepository.findAll(any(Pageable.class))).thenReturn(page);
|
||||
|
||||
List<Document> result = documentService.getRecentActivity(2);
|
||||
|
||||
assertThat(result).hasSize(2);
|
||||
assertThat(result).containsExactly(doc3, doc2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRecentActivity_usesPageRequestWithSizeLimit_notFindAll() {
|
||||
Page<Document> page = new PageImpl<>(List.of());
|
||||
when(documentRepository.findAll(any(Pageable.class))).thenReturn(page);
|
||||
|
||||
documentService.getRecentActivity(3);
|
||||
|
||||
ArgumentCaptor<Pageable> captor = ArgumentCaptor.forClass(Pageable.class);
|
||||
verify(documentRepository).findAll(captor.capture());
|
||||
assertThat(captor.getValue().getPageSize()).isEqualTo(3);
|
||||
assertThat(captor.getValue().getSort())
|
||||
.isEqualTo(Sort.by(Sort.Direction.DESC, "updatedAt"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +401,24 @@ class NotificationServiceTest {
|
||||
.findByRecipientIdAndTypeAndReadFalseOrderByCreatedAtDesc(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNotifications_withTypeAndReadTrue_fallsBackToTypeOnlyQuery() {
|
||||
// read=true with a type filter falls through to the type-only branch —
|
||||
// it returns all notifications of that type (both read and unread).
|
||||
// The read=true filter is intentionally not supported on the backend;
|
||||
// callers that need only-read results must filter client-side.
|
||||
when(notificationRepository.findByRecipientIdAndTypeOrderByCreatedAtDesc(
|
||||
eq(userA.getId()), eq(NotificationType.MENTION), any()))
|
||||
.thenReturn(Page.empty());
|
||||
|
||||
notificationService.getNotifications(userA.getId(), NotificationType.MENTION, true, Pageable.ofSize(5));
|
||||
|
||||
verify(notificationRepository).findByRecipientIdAndTypeOrderByCreatedAtDesc(
|
||||
eq(userA.getId()), eq(NotificationType.MENTION), any());
|
||||
verify(notificationRepository, never())
|
||||
.findByRecipientIdAndTypeAndReadFalseOrderByCreatedAtDesc(any(), any(), any());
|
||||
}
|
||||
|
||||
// ─── private helpers ──────────────────────────────────────────────────────
|
||||
|
||||
private DocumentComment commentWithAuthor(UUID id, UUID parentId, UUID authorId, String authorName) {
|
||||
|
||||
Reference in New Issue
Block a user