From dc6ea080c4445c954df3aeff7bc6e566bce6bae5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 28 Mar 2026 00:31:38 +0100 Subject: [PATCH] fix(#71-#73): address all review findings from Markus and Sara MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BLOCKERs: - Remove direct AppUserRepository/CommentRepository access from CommentService and NotificationService — replaced with UserService.findAllById() and UserService (fixes layering contract from CLAUDE.md) - Switch Optional constructor injection — removes @Autowired(required=false) field and ReflectionTestUtils hack in tests - Add @RequirePermission(READ_ALL) to UserSearchController — prevents user enumeration without read access Data bug: - Promote actorName from @Transient to persisted VARCHAR column (V18 migration) - Set actorName in notifyReply and notifyMentions from comment.getAuthorName() Architecture: - Add @RequirePermission(READ_ALL) to NotificationController - Introduce NotificationDTO — controller returns DTO instead of Notification entity, eliminating lazy-load N+1 and AppUser field leakage - Change mentions FetchType to EAGER — fixes LazyInitializationException outside transaction - Add @Transactional(propagation=REQUIRES_NEW) to notifyReply/notifyMentions so a notification failure cannot roll back the parent comment - N+1 fix: replace per-ID findById loops with single findAllById bulk fetch - Move collectParticipantIds to CommentService; notifyReply accepts Set directly Security: - Escape displayName before injecting into renderBody HTML span - Replace with — no profile page to link to, and the anchor's scroll-to-top behaviour is harmful Tests added/fixed: - markRead_throwsNotFound, markAllRead_delegatesToRepository, countUnread_delegatesToRepository - markOneRead_returns401, @RequirePermission 403 coverage for both controllers - postComment/replyToComment_triggersNotifyMentions_whenMentionedUserIdsProvided - search_returnsAtMostTenResults now asserts $.length() <= 10 - XSS regression test for escaped displayName in mention.spec.ts Frontend minors: - relativeTime() uses Intl.RelativeTimeFormat (locale-aware, not German-hardcoded) - aria-label uses m.notification_unread() Paraglide key (de/en/es added) -
replaced with {/each} diff --git a/frontend/src/lib/utils/mention.spec.ts b/frontend/src/lib/utils/mention.spec.ts index 2da73497..301cbabe 100644 --- a/frontend/src/lib/utils/mention.spec.ts +++ b/frontend/src/lib/utils/mention.spec.ts @@ -92,10 +92,10 @@ describe('renderBody', () => { expect(result).toContain('AT&T'); }); - it('wraps @mention in an anchor tag', () => { + it('wraps @mention in a mention span', () => { const mentions: MentionDTO[] = [{ id: 'uuid-1', firstName: 'Hans', lastName: 'Müller' }]; const result = renderBody('Hey @Hans Müller!', mentions); - expect(result).toContain(' { it('replaces all occurrences of the same mention', () => { const mentions: MentionDTO[] = [{ id: 'uuid-1', firstName: 'Hans', lastName: 'Müller' }]; const result = renderBody('@Hans Müller and @Hans Müller', mentions); - const linkCount = (result.match(/ { + const mentions: MentionDTO[] = [{ id: 'u1', firstName: '